diff --git a/.github/workflows/publish_npm_scoped_x402_fastify.yml b/.github/workflows/publish_npm_scoped_x402_fastify.yml
new file mode 100644
index 0000000000..c794464be0
--- /dev/null
+++ b/.github/workflows/publish_npm_scoped_x402_fastify.yml
@@ -0,0 +1,56 @@
+name: Publish @x402/fastify package to NPM
+
+on:
+ workflow_dispatch:
+
+jobs:
+ publish-npm-x402-fastify:
+ runs-on: ubuntu-latest
+ environment: ${{ github.ref == 'refs/heads/main' && 'npm' || '' }}
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
+ with:
+ version: 10.7.0
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "24"
+ registry-url: "https://registry.npmjs.org"
+ cache: "pnpm"
+ cache-dependency-path: ./typescript
+
+ - name: Update npm for OIDC trusted publishing
+ run: npm install -g npm@latest
+
+ - name: Configure npm for trusted publishing
+ run: npm config delete always-auth 2>/dev/null || true
+
+ - name: Install and build
+ working-directory: ./typescript
+ run: |
+ pnpm install --frozen-lockfile
+ pnpm -r --filter=@x402/core --filter=@x402/extensions --filter=@x402/fastify run build
+
+ - name: Publish @x402/fastify package
+ working-directory: ./typescript/packages/http/fastify
+ run: |
+ # Get package information directly
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
+ PACKAGE_VERSION=$(node -p "require('./package.json').version")
+
+ echo "Package: $PACKAGE_NAME@$PACKAGE_VERSION"
+
+ # Check if running on main branch
+ if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
+ echo "Publishing to NPM (main branch)"
+ pnpm publish --provenance --access public
+ else
+ echo "Dry run only (non-main branch: ${{ github.ref }})"
+ pnpm publish --dry-run --no-git-checks
+ fi
diff --git a/.github/workflows/publish_npm_scoped_x402_stellar.yml b/.github/workflows/publish_npm_scoped_x402_stellar.yml
new file mode 100644
index 0000000000..92d09e057b
--- /dev/null
+++ b/.github/workflows/publish_npm_scoped_x402_stellar.yml
@@ -0,0 +1,56 @@
+name: Publish @x402/stellar package to NPM
+
+on:
+ workflow_dispatch:
+
+jobs:
+ publish-npm-x402-stellar:
+ runs-on: ubuntu-latest
+ environment: ${{ github.ref == 'refs/heads/main' && 'npm' || '' }}
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
+ with:
+ version: 10.7.0
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "24"
+ registry-url: "https://registry.npmjs.org"
+ cache: "pnpm"
+ cache-dependency-path: ./typescript
+
+ - name: Update npm for OIDC trusted publishing
+ run: npm install -g npm@latest
+
+ - name: Configure npm for trusted publishing
+ run: npm config delete always-auth 2>/dev/null || true
+
+ - name: Install and build
+ working-directory: ./typescript
+ run: |
+ pnpm install --frozen-lockfile
+ pnpm -r --filter=@x402/core --filter=@x402/stellar run build
+
+ - name: Publish @x402/stellar package
+ working-directory: ./typescript/packages/mechanisms/stellar
+ run: |
+ # Get package information directly
+ PACKAGE_NAME=$(node -p "require('./package.json').name")
+ PACKAGE_VERSION=$(node -p "require('./package.json').version")
+
+ echo "Package: $PACKAGE_NAME@$PACKAGE_VERSION"
+
+ # Check if running on main branch
+ if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
+ echo "Publishing to NPM (main branch)"
+ pnpm publish --provenance --access public
+ else
+ echo "Dry run only (non-main branch: ${{ github.ref }})"
+ pnpm publish --dry-run --no-git-checks
+ fi
diff --git a/.gitignore b/.gitignore
index 52308c896f..91f46e3f18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.env
+.env.local
node_modules/
dist/
.turbo/
@@ -20,12 +21,17 @@ e2e/clients/mcp-go/mcp-client
e2e/clients/mcp-go/mcp-go
e2e/facilitators/go/facilitator
e2e/facilitators/go/go
+e2e/servers/echo/echo
e2e/servers/gin/gin
e2e/servers/gin/server
+e2e/servers/nethttp/nethttp
e2e/servers/mcp-go/mcp-go
e2e/servers/mcp-go/mcp-server
e2e/legacy/servers/gin/gin
+# Example build artifacts
+examples/go/servers/upto/upto
+
# Agent artifacts
TASK.md
.claude/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6c17d67b53..683297dae1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -135,6 +135,18 @@ x402 aims to be chain-agnostic. New chain implementations are welcome.
Because different chains have different best practices, a scheme may have a different mechanism on a new chain than it does on EVM. If the scheme mechanism varies from the reference implementation, the x402 Foundation will re-audit the scheme for that chain before accepting.
+### Adding a Default Asset for an EVM Chain
+
+If your chain is EVM-compatible and you want to add a default stablecoin for
+dollar-string pricing (`"$0.10"`), you don't need the full 3-PR workflow below. See:
+
+- [Go: DEFAULT_ASSET.md](go/mechanisms/evm/DEFAULT_ASSET.md)
+- [TypeScript: DEFAULT_ASSET.md](typescript/packages/mechanisms/evm/src/exact/server/DEFAULT_ASSET.md)
+
+These guides include a cross-SDK checklist of every file to update.
+
+### Adding a New Chain Family
+
### PR 1: Specification Only
Open a PR with specs for one payment scheme implementation.
diff --git a/README.md b/README.md
index 51ef38a7bc..efcdc0790a 100644
--- a/README.md
+++ b/README.md
@@ -51,9 +51,9 @@ go get github.com/coinbase/x402/go
- **Open standard:** x402 is an open standard, freely accessible and usable by anyone. It will never force reliance on a single party.
- **HTTP / Transport Native:** x402 is meant to seamlessly complement existing data transportation. It should whenever possible not mandate additional requests outside the scope of a typical client / server flow.
- **Network, token, and currency agnostic:** we welcome contributions that add support for new networks (both crypto and fiat), signing standards, or schemes, so long as they meet our acceptance criteria laid out in [CONTRIBUTING.md](https://github.com/coinbase/x402/blob/main/CONTRIBUTING.md). x402 may extend support to fiat based networks, but will never deprioritize onchain payments in favor of fiat payments.
-- **Backwards Compatible:** x402 will not deprecate support for any existing networks unless such removal is deemed necessary for the security of the standard
+- **Backwards Compatible:** x402 will not deprecate support for any existing networks unless such removal is deemed necessary for the security of the standard. Whenever possible, x402 will aim for backwards compatibility for non-major version changes.
- **Trust minimizing:** all payment schemes must not allow for the facilitator or resource server to move funds, other than in accordance with client intentions
-- **Easy to use:** x402 needs to be 10x better than existing ways to pay on the internet. This means abstracting as many details of crypto as possible away from the client and resource server, and into the facilitator. This means the client/server should not need to think about gas, rpc, etc.
+- **Easy to use:** It is the goal of the x402 community to improve ease of use relative to other forms of payment on the Internet. This means abstracting as many details of crypto as possible away from the client and resource server, and into the facilitator. This means the client/server should not need to think about gas, rpc, etc.
## Ecosystem
diff --git a/contracts/evm/README.md b/contracts/evm/README.md
index e744fb71b1..ddbad6a791 100644
--- a/contracts/evm/README.md
+++ b/contracts/evm/README.md
@@ -18,14 +18,23 @@ Both contracts:
- Support both standard Permit2 and EIP-2612 flows
- Deploy to the **same address on all EVM chains** via CREATE2
-## Deployed Addresses
+## Canonical Addresses
-### Base Sepolia (Testnet)
+| Contract | Address |
+|----------|---------|
+| x402ExactPermit2Proxy | `0x402085c248EeA27D92E8b30b2C58ed07f9E20001` |
+| x402UptoPermit2Proxy | `0x4020a4f3b7b90CCA423b9FabCC0CE57c6c240002` |
-| Contract | Address | Verified |
-|----------|---------|----------|
-| x402ExactPermit2Proxy | [`0x4020cd856c882d5fb903d99ce35316a085bb0001`](https://sepolia.basescan.org/address/0x4020cd856c882d5fb903d99ce35316a085bb0001) | ✓ |
-| x402UptoPermit2Proxy | [`0x40204513ec14919adfd30d77c0a991371b420002`](https://sepolia.basescan.org/address/0x40204513ec14919adfd30d77c0a991371b420002) | ✓ |
+### Current Deployments
+
+| Chain | Exact | Upto |
+|-------|-------|------|
+| Base Mainnet | [Deployed](https://basescan.org/address/0x402085c248EeA27D92E8b30b2C58ed07f9E20001) | — |
+| Base Sepolia | [Deployed](https://sepolia.basescan.org/address/0x402085c248EeA27D92E8b30b2C58ed07f9E20001) | [Legacy\*](https://sepolia.basescan.org/address/0x402039b3d6E6BEC5A02c2C9fd937ac17A6940002) |
+
+> \*The Base Sepolia Upto deployment at `0x4020...0002` predates the deterministic build fix
+> and uses a different bytecode (with CBOR metadata). The canonical Upto address for all
+> new deployments is `0x4020a4f3...0002`.
## Prerequisites
@@ -34,13 +43,87 @@ Both contracts:
## Installation
```bash
-# Install dependencies
forge install
-
-# Build contracts
forge build
```
+## Deploying to a New EVM Chain
+
+Anyone can deploy both contracts to their canonical addresses on any EVM chain.
+No special build environment, private key, or permission is required—only gas on the target chain.
+
+### How it works
+
+Both contracts are deployed via [Arachnid's deterministic CREATE2 deployer](https://github.com/Arachnid/deterministic-deployment-proxy)
+(`0x4e59b44847b379578588920cA78FbF26c0B4956C`), which exists at the same address on
+virtually every EVM chain. The CREATE2 address depends only on the deployer, a salt,
+and `keccak256(initCode)`—not on who sends the transaction.
+
+| Contract | Bytecode source | Why |
+|----------|----------------|-----|
+| **Exact** | Pre-built initCode in `script/data/exact-proxy-initcode.hex` | The original build included Solidity CBOR metadata (an IPFS hash that varies per build environment). The committed hex file is the exact initCode from the original deployment, ensuring the same address everywhere. |
+| **Upto** | Compiled from source (`forge build`) | Built with `cbor_metadata = false` so the bytecode is identical on every machine at the same git commit. |
+
+### Step-by-step
+
+1. **Clone and build**
+ ```bash
+ cd contracts/evm
+ forge install
+ forge build
+ ```
+
+2. **Verify expected addresses** (optional, no RPC needed)
+ ```bash
+ forge script script/ComputeAddress.s.sol
+ ```
+ You should see:
+ - Exact → `0x402085c248EeA27D92E8b30b2C58ed07f9E20001`
+ - Upto → `0x4020a4f3b7b90CCA423b9FabCC0CE57c6c240002`
+
+3. **Check prerequisites on the target chain**
+ - [Permit2](https://github.com/Uniswap/permit2) must be deployed at `0x000000000022D473030F116dDEE9F6B43aC78BA3`
+ - The CREATE2 deployer must exist at `0x4e59b44847b379578588920cA78FbF26c0B4956C`
+ - Your wallet needs enough native gas to pay for deployment (~300k gas per contract)
+
+4. **Deploy**
+ ```bash
+ export PRIVATE_KEY="your_private_key"
+
+ forge script script/Deploy.s.sol \
+ --rpc-url \
+ --broadcast \
+ --verify
+ ```
+
+ The script 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
+
+5. **Verify on Etherscan** (if `--verify` didn't work automatically)
+ ```bash
+ forge verify-contract x402UptoPermit2Proxy \
+ --rpc-url \
+ --constructor-args $(cast abi-encode "constructor(address)" 0x000000000022D473030F116dDEE9F6B43aC78BA3)
+ ```
+
+ For the Exact proxy, verification may require matching the original compiler metadata.
+ The verified source on Base Sepolia / Base Mainnet can be used as a reference.
+
+### Overriding Permit2 address
+
+If the target chain has Permit2 at a non-canonical address:
+
+```bash
+export PERMIT2_ADDRESS="0x..."
+forge script script/Deploy.s.sol --rpc-url --broadcast
+```
+
+> **Warning:** Overriding the Permit2 address changes the initCode for the Upto contract
+> and will produce a different deployment address. The Exact contract's pre-built initCode
+> already encodes the canonical Permit2 address and cannot be overridden.
+
## Testing
```bash
@@ -71,90 +154,66 @@ forge test --match-contract Invariants
Fork tests run against real Permit2 on Base Sepolia:
```bash
-# Set up environment
export BASE_SEPOLIA_RPC_URL="https://sepolia.base.org"
-# Run fork tests for Exact variant
forge test --match-contract X402ExactPermit2ProxyForkTest --fork-url $BASE_SEPOLIA_RPC_URL
-
-# Run fork tests for Upto variant
forge test --match-contract X402UptoPermit2ProxyForkTest --fork-url $BASE_SEPOLIA_RPC_URL
```
-## Deployment
-
-### Compute Expected Addresses
+## Vanity Address Mining
-```bash
-forge script script/ComputeAddress.s.sol
-```
+Both contracts use vanity addresses with prefix `0x4020` and suffix `0001` (Exact) or `0002` (Upto).
-### Deploy to Testnet
+The vanity miner is only needed if the contract source code changes (which changes the
+initCodeHash and invalidates existing salts). To re-mine:
```bash
-# Set environment variables
-export PRIVATE_KEY="your_private_key"
-export BASE_SEPOLIA_RPC_URL="https://sepolia.base.org"
-export BASESCAN_API_KEY="your_api_key"
-
-# Deploy both contracts with verification
-forge script script/Deploy.s.sol \
- --rpc-url $BASE_SEPOLIA_RPC_URL \
- --broadcast \
- --verify
-```
-
-### Deploy to Mainnet
+cd vanity-miner
-```bash
-export BASE_RPC_URL="https://mainnet.base.org"
+# Mine both contracts
+cargo run --release
-forge script script/Deploy.s.sol \
- --rpc-url $BASE_RPC_URL \
- --broadcast \
- --verify
+# Mine only one
+cargo run --release -- exact
+cargo run --release -- upto
```
-## Vanity Address Mining
+After mining, update the salt constants in `script/Deploy.s.sol` and `script/ComputeAddress.s.sol`,
+and the init code hashes in `vanity-miner/src/main.rs`.
-The deployment uses vanity addresses starting with `0x4020`. To mine new salts:
+## Deterministic Build Configuration
-```bash
-cd vanity-miner
-cargo run --release
+The `foundry.toml` includes two settings that ensure bytecode reproducibility:
+
+```toml
+cbor_metadata = false
+bytecode_hash = "none"
```
-The Rust miner uses parallel processing for efficient address generation. Update the init code hashes in `vanity-miner/src/main.rs` if the contract bytecode changes.
+Without these, the Solidity compiler appends a CBOR-encoded IPFS hash of the contract
+metadata to the bytecode. This hash varies across build environments (even with identical
+source code and compiler version), breaking CREATE2 address determinism.
+
+The `x402ExactPermit2Proxy` was deployed before this fix was in place, which is why it
+uses a committed initCode hex file instead of compiler-derived bytecode.
## Contract Architecture
```
src/
+├── x402BasePermit2Proxy.sol # Shared settlement logic and Permit2 interaction
├── x402ExactPermit2Proxy.sol # Exact amount transfers (EIP-3009-like)
├── x402UptoPermit2Proxy.sol # Flexible amount transfers (up to permitted)
└── interfaces/
└── ISignatureTransfer.sol # Permit2 SignatureTransfer interface
-test/
-├── x402ExactPermit2Proxy.t.sol # Exact variant unit tests
-├── x402ExactPermit2Proxy.fork.t.sol # Exact variant fork tests
-├── x402UptoPermit2Proxy.t.sol # Upto variant unit tests
-├── x402UptoPermit2Proxy.fork.t.sol # Upto variant fork tests
-├── invariants/
-│ ├── X402ExactInvariants.t.sol # Exact variant invariant tests
-│ └── X402UptoInvariants.t.sol # Upto variant invariant tests
-└── mocks/
- ├── MockERC20.sol
- ├── MockERC20Permit.sol
- ├── MockPermit2.sol
- ├── MaliciousReentrantExact.sol
- └── MaliciousReentrantUpto.sol
-
script/
-├── Deploy.s.sol # CREATE2 deployment for both contracts
-└── ComputeAddress.s.sol # Address computation for both contracts
+├── Deploy.s.sol # CREATE2 deployment for both contracts
+├── ComputeAddress.s.sol # Address computation (no RPC needed)
+└── data/
+ └── exact-proxy-initcode.hex # Pre-built initCode for Exact proxy
-vanity-miner/ # Rust-based vanity address miner
+vanity-miner/ # Rust-based vanity address miner
└── src/main.rs
```
@@ -194,11 +253,11 @@ The function signatures follow the same pattern as `settle()` for each variant.
## Security
-- **Immutable:** No upgrade mechanism, one-time initialization
+- **Immutable:** No upgrade mechanism, no owner, no admin functions
- **No custody:** Contracts never hold tokens
- **Destination locked:** Witness pattern enforces payTo address
- **Reentrancy protected:** Uses OpenZeppelin's ReentrancyGuard
-- **Deterministic:** Same address on all chains via CREATE2 (Permit2 set via initialize)
+- **Deterministic:** Same address on all chains via CREATE2
## Coverage
@@ -213,10 +272,7 @@ forge coverage --no-match-coverage "(test|script)/.*" --offline
## Gas Snapshots
```bash
-# Create snapshot
forge snapshot
-
-# Compare against baseline
forge snapshot --diff
```
diff --git a/contracts/evm/foundry.toml b/contracts/evm/foundry.toml
index 21e56bb1b0..69e3e75e9b 100644
--- a/contracts/evm/foundry.toml
+++ b/contracts/evm/foundry.toml
@@ -8,6 +8,8 @@ solc = "0.8.28"
optimizer = true
optimizer_runs = 200
via_ir = false
+cbor_metadata = false
+bytecode_hash = "none"
ffi = false
fs_permissions = [{ access = "read", path = "./" }]
gas_reports = ["x402ExactPermit2Proxy", "x402UptoPermit2Proxy"]
diff --git a/contracts/evm/script/ComputeAddress.s.sol b/contracts/evm/script/ComputeAddress.s.sol
index be92590f2e..602ef3d13b 100644
--- a/contracts/evm/script/ComputeAddress.s.sol
+++ b/contracts/evm/script/ComputeAddress.s.sol
@@ -10,9 +10,11 @@ import {x402UptoPermit2Proxy} from "../src/x402UptoPermit2Proxy.sol";
* @title ComputeAddress
* @notice Compute the deterministic CREATE2 addresses for x402 Permit2 Proxies
*
- * @dev The Permit2 address is a constructor argument. Since the canonical Permit2
- * address is the same on all EVM chains, the initCode is identical everywhere,
- * preserving uniform CREATE2 addresses.
+ * @dev x402ExactPermit2Proxy uses a pre-built initCode (script/data/exact-proxy-initcode.hex)
+ * because the original build included non-deterministic CBOR metadata.
+ *
+ * x402UptoPermit2Proxy uses compiler-derived creationCode, which is deterministic
+ * thanks to cbor_metadata = false in foundry.toml.
*
* @dev Run with default salts:
* forge script script/ComputeAddress.s.sol
@@ -28,12 +30,15 @@ contract ComputeAddress is Script {
address constant CANONICAL_PERMIT2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;
/// @notice Default salt for x402ExactPermit2Proxy
- /// @dev Vanity mined for address 0x4020cd856c882d5fb903d99ce35316a085bb0001
- bytes32 constant DEFAULT_EXACT_SALT = 0x0000000000000000000000000000000000000000000000002c00000003c30a30;
+ /// @dev Vanity mined for address 0x402085c248eea27d92e8b30b2c58ed07f9e20001
+ bytes32 constant DEFAULT_EXACT_SALT = 0x0000000000000000000000000000000000000000000000003000000007263b0e;
/// @notice Default salt for x402UptoPermit2Proxy
- /// @dev Vanity mined for address 0x40204513ec14919adfd30d77c0a991371b420002
- bytes32 constant DEFAULT_UPTO_SALT = 0x00000000000000000000000000000000000000000000000084000000275d7dbb;
+ /// @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;
/**
* @notice Computes the CREATE2 addresses using the default salts
@@ -59,15 +64,17 @@ contract ComputeAddress is Script {
console2.log(" Permit2 (ctor arg): ", CANONICAL_PERMIT2);
console2.log("");
- // Compute x402ExactPermit2Proxy address
+ // x402ExactPermit2Proxy — uses pre-built initCode from hex file
{
- bytes memory initCode =
- abi.encodePacked(type(x402ExactPermit2Proxy).creationCode, abi.encode(CANONICAL_PERMIT2));
+ bytes memory initCode = vm.parseBytes(vm.readFile("script/data/exact-proxy-initcode.hex"));
bytes32 initCodeHash = keccak256(initCode);
+
+ require(initCodeHash == EXACT_INIT_CODE_HASH, "Exact initCode hash mismatch - hex file may be corrupted");
+
address expectedAddress = _computeCreate2Addr(exactSalt, initCodeHash, CREATE2_DEPLOYER);
console2.log("------------------------------------------------------------");
- console2.log(" x402ExactPermit2Proxy");
+ console2.log(" x402ExactPermit2Proxy (pre-built initCode)");
console2.log("------------------------------------------------------------");
console2.log(" Salt: ", vm.toString(exactSalt));
console2.log(" Init Code Hash: ", vm.toString(initCodeHash));
@@ -81,7 +88,7 @@ contract ComputeAddress is Script {
console2.log("");
}
- // Compute x402UptoPermit2Proxy address
+ // x402UptoPermit2Proxy — uses compiler-derived creationCode (deterministic)
{
bytes memory initCode =
abi.encodePacked(type(x402UptoPermit2Proxy).creationCode, abi.encode(CANONICAL_PERMIT2));
@@ -89,7 +96,7 @@ contract ComputeAddress is Script {
address expectedAddress = _computeCreate2Addr(uptoSalt, initCodeHash, CREATE2_DEPLOYER);
console2.log("------------------------------------------------------------");
- console2.log(" x402UptoPermit2Proxy");
+ console2.log(" x402UptoPermit2Proxy (deterministic build)");
console2.log("------------------------------------------------------------");
console2.log(" Salt: ", vm.toString(uptoSalt));
console2.log(" Init Code Hash: ", vm.toString(initCodeHash));
diff --git a/contracts/evm/script/Deploy.s.sol b/contracts/evm/script/Deploy.s.sol
index ee758bea09..9f3713bc00 100644
--- a/contracts/evm/script/Deploy.s.sol
+++ b/contracts/evm/script/Deploy.s.sol
@@ -11,14 +11,26 @@ import {ISignatureTransfer} from "../src/interfaces/ISignatureTransfer.sol";
* @notice Deployment script for x402 Permit2 Proxy contracts using CREATE2
* @dev Run with: forge script script/Deploy.s.sol --rpc-url $RPC_URL --broadcast --verify
*
- * The Permit2 address is passed as a constructor argument. Since Permit2 is
- * deployed via a deterministic CREATE2 deployer, its canonical address
- * (0x000000000022D473030F116dDEE9F6B43aC78BA3) is the same on all EVM chains.
- * Using the same constructor argument on every chain keeps the initCode identical,
- * preserving a uniform CREATE2 address for these proxies across all chains.
+ * ## Deployment Strategy
*
- * If Permit2 has not yet been deployed on the target chain, deploy it first
- * using the canonical Permit2 deployment method before running this script.
+ * **x402ExactPermit2Proxy** — Uses a pre-built initCode blob stored at
+ * `script/data/exact-proxy-initcode.hex`. The original build included
+ * Solidity CBOR metadata (an IPFS hash that varies per build
+ * environment), so recompiling from source produces a *different*
+ * initCodeHash and therefore a different CREATE2 address. By shipping
+ * the exact initCode that was used for the first deployment, anyone can
+ * deploy to the canonical address on any chain without needing the
+ * original build environment.
+ *
+ * **x402UptoPermit2Proxy** — Built from source with deterministic
+ * bytecode (`cbor_metadata = false` in foundry.toml). Any machine
+ * compiling at the same git commit will produce the same initCode and
+ * therefore the same CREATE2 address.
+ *
+ * Both contracts use the canonical Permit2 address
+ * (0x000000000022D473030F116dDEE9F6B43aC78BA3) as a constructor
+ * argument. If Permit2 has not yet been deployed on the target chain,
+ * deploy it first.
*/
contract DeployX402Proxies is Script {
/// @notice Canonical Permit2 address (Uniswap's official deployment)
@@ -29,15 +41,17 @@ contract DeployX402Proxies is Script {
address constant CREATE2_DEPLOYER = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
/// @notice Salt for x402ExactPermit2Proxy deterministic deployment
- /// @dev Vanity mined for address 0x4020cd856c882d5fb903d99ce35316a085bb0001
- bytes32 constant EXACT_SALT = 0x0000000000000000000000000000000000000000000000002c00000003c30a30;
+ /// @dev Vanity mined for address 0x402085c248eea27d92e8b30b2c58ed07f9e20001
+ bytes32 constant EXACT_SALT = 0x0000000000000000000000000000000000000000000000003000000007263b0e;
/// @notice Salt for x402UptoPermit2Proxy deterministic deployment
- /// @dev Vanity mined for address 0x40204513ec14919adfd30d77c0a991371b420002
- bytes32 constant UPTO_SALT = 0x00000000000000000000000000000000000000000000000084000000275d7dbb;
+ /// @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;
function run() public {
- // Allow override of Permit2 address for chains with non-canonical deployments
address permit2 = vm.envOr("PERMIT2_ADDRESS", CANONICAL_PERMIT2);
console2.log("");
@@ -46,13 +60,11 @@ contract DeployX402Proxies is Script {
console2.log("============================================================");
console2.log("");
- // Log configuration
console2.log("Network: chainId", block.chainid);
console2.log("Permit2:", permit2);
console2.log("CREATE2 Deployer:", CREATE2_DEPLOYER);
console2.log("");
- // Verify Permit2 exists (skip for local networks)
if (block.chainid != 31_337 && block.chainid != 1337) {
require(permit2.code.length > 0, "Permit2 not found on this network");
console2.log("Permit2 verified");
@@ -61,7 +73,6 @@ contract DeployX402Proxies is Script {
console2.log("CREATE2 deployer verified");
}
- // Deploy both contracts (Permit2 address is a constructor arg, no initialization needed)
_deployExact(permit2);
_deployUpto(permit2);
@@ -78,8 +89,16 @@ contract DeployX402Proxies is Script {
console2.log(" Deploying x402ExactPermit2Proxy");
console2.log("------------------------------------------------------------");
- // Constructor arg is the canonical Permit2 address (same on all chains)
- bytes memory initCode = abi.encodePacked(type(x402ExactPermit2Proxy).creationCode, abi.encode(permit2));
+ bytes memory initCode;
+
+ if (block.chainid == 31_337 || block.chainid == 1337) {
+ initCode = abi.encodePacked(type(x402ExactPermit2Proxy).creationCode, abi.encode(permit2));
+ } else {
+ initCode = vm.parseBytes(vm.readFile("script/data/exact-proxy-initcode.hex"));
+ bytes32 actualHash = keccak256(initCode);
+ require(actualHash == EXACT_INIT_CODE_HASH, "Exact initCode hash mismatch - hex file may be corrupted");
+ }
+
bytes32 initCodeHash = keccak256(initCode);
address expectedAddress = _computeCreate2Addr(EXACT_SALT, initCodeHash, CREATE2_DEPLOYER);
@@ -127,7 +146,6 @@ contract DeployX402Proxies is Script {
console2.log(" Deploying x402UptoPermit2Proxy");
console2.log("------------------------------------------------------------");
- // Constructor arg is the canonical Permit2 address (same on all chains)
bytes memory initCode = abi.encodePacked(type(x402UptoPermit2Proxy).creationCode, abi.encode(permit2));
bytes32 initCodeHash = keccak256(initCode);
address expectedAddress = _computeCreate2Addr(UPTO_SALT, initCodeHash, CREATE2_DEPLOYER);
diff --git a/contracts/evm/script/data/exact-proxy-initcode.hex b/contracts/evm/script/data/exact-proxy-initcode.hex
new file mode 100644
index 0000000000..cfeedd607c
--- /dev/null
+++ b/contracts/evm/script/data/exact-proxy-initcode.hex
@@ -0,0 +1 @@
+0x60a060405234801561000f575f5ffd5b50604051610c3f380380610c3f83398101604081905261002e9161008c565b8060017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00556001600160a01b03811661007a576040516359106c8960e01b815260040160405180910390fd5b6001600160a01b0316608052506100b9565b5f6020828403121561009c575f5ffd5b81516001600160a01b03811681146100b2575f5ffd5b9392505050565b608051610b616100de5f395f818160c6015281816103fc01526104a90152610b615ff3fe608060405234801561000f575f5ffd5b5060043610610055575f3560e01c806313cd3b5314610059578063156e21521461006e5780632c03ae6a1461008c5780636afdd850146100c1578063fa34037814610100575b5f5ffd5b61006c61006736600461076c565b610113565b005b610076610211565b604051610083919061080e565b60405180910390f35b6100b37fd97b3239a7f32295517bd14cb074edfdd188dfe5eb42f802bb26d4fd1eb12c3781565b604051908152602001610083565b6100e87f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610083565b61006c61010e366004610827565b61022d565b61011b610322565b5f7fd97b3239a7f32295517bd14cb074edfdd188dfe5eb42f802bb26d4fd1eb12c3761014a60208601866108b5565b604080516020818101949094526001600160a01b039092169082015290850135606082015260800160408051601f19818403018152919052805160209182012091506101cb908790818101359088906101a5908901896108b5565b8860200135866040518060a0016040528060648152602001610aa8606491398a8a610340565b6040517f97088ec3606cfe8cc112180570d03fcde05f9b8e1bfef8e27784eaf5dd5691b6905f90a15061020a60015f516020610b0c5f395f51905f5255565b5050505050565b6040518060a0016040528060648152602001610aa86064913981565b610235610322565b61025161024560208701876108b5565b85886020890135610476565b5f7fd97b3239a7f32295517bd14cb074edfdd188dfe5eb42f802bb26d4fd1eb12c3761028060208601866108b5565b604080516020818101949094526001600160a01b039092169082015290850135606082015260800160408051601f19818403018152919052805160209182012091506102db908790818101359088906101a5908901896108b5565b6040517fde5b89d10fc800c459329c382fabfcad0be0ed7e5328e01fae04e507b09ef5d8905f90a15061031a60015f516020610b0c5f395f51905f5255565b505050505050565b61032a6106b5565b60025f516020610b0c5f395f51905f5255565b90565b875f036103605760405163162908e360e11b815260040160405180910390fd5b6001600160a01b038716610387576040516349e27cff60e01b815260040160405180910390fd5b6001600160a01b0386166103ae5760405163ac6b05f560e01b815260040160405180910390fd5b844210156103cf5760405163532a9cfd60e11b815260040160405180910390fd5b6040805180820182526001600160a01b038089168252602082018b905291516309be14ff60e11b815290917f0000000000000000000000000000000000000000000000000000000000000000169063137c29fe9061043d908d9085908d908b908b908b908b906004016108f6565b5f604051808303815f87803b158015610454575f5ffd5b505af1158015610466573d5f5f3e3d5ffd5b5050505050505050505050505050565b813581146104975760405163050cda4960e01b815260040160405180910390fd5b6001600160a01b03841663d505accf847f0000000000000000000000000000000000000000000000000000000000000000853560208701356104df60a0890160808a01610995565b604080516001600160e01b031960e089901b1681526001600160a01b0396871660048201529590941660248601526044850192909252606484015260ff16608483015285013560a4820152606085013560c482015260e4015f604051808303815f87803b15801561054e575f5ffd5b505af192505050801561055f575060015b6106af5761056b6109b5565b806308c379a0036105db575061057f610a06565b8061058a575061063d565b836001600160a01b0316856001600160a01b03167f0fea7e5a0e14e9f700b9b0666ffd524bacb9ceb9e6234ea2bfb8e1d0ee2365e7836040516105cd919061080e565b60405180910390a3506106af565b634e487b710361063d576105ed610a8a565b906105f8575061063d565b836001600160a01b0316856001600160a01b03167f278207c426558a8c6ef49e9a3c6f87d257ca5c1852f0c988ad5caf894d197623836040516105cd91815260200190565b3d808015610666576040519150601f19603f3d011682016040523d82523d5f602084013e61066b565b606091505b50836001600160a01b0316856001600160a01b03167f41d715076dfd9c1fd3135fbb7675020aab7fb8e82349a0cd5aaa970dcfb816fe836040516105cd919061080e565b50505050565b5f516020610b0c5f395f51905f52546002036106e457604051633ee5aeb560e01b815260040160405180910390fd5b565b5f608082840312156106f6575f5ffd5b50919050565b80356001600160a01b0381168114610712575f5ffd5b919050565b5f604082840312156106f6575f5ffd5b5f5f83601f840112610737575f5ffd5b50813567ffffffffffffffff81111561074e575f5ffd5b602083019150836020828501011115610765575f5ffd5b9250929050565b5f5f5f5f5f6101008688031215610781575f5ffd5b61078b87876106e6565b9450610799608087016106fc565b93506107a88760a08801610717565b925060e086013567ffffffffffffffff8111156107c3575f5ffd5b6107cf88828901610727565b969995985093965092949392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61082060208301846107e0565b9392505050565b5f5f5f5f5f5f8688036101a081121561083e575f5ffd5b60a081121561084b575f5ffd5b5086955061085c8860a089016106e6565b945061086b61012088016106fc565b935061087b886101408901610717565b925061018087013567ffffffffffffffff811115610897575f5ffd5b6108a389828a01610727565b979a9699509497509295939492505050565b5f602082840312156108c5575f5ffd5b610820826106fc565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b6001600160a01b03610907896106fc565b168152602088810135818301526040808a0135908301526060808a01359083015287516001600160a01b0316608083015287015160a08201525f61095660c08301886001600160a01b03169052565b8560e08301526101406101008301526109736101408301866107e0565b8281036101208401526109878185876108ce565b9a9950505050505050505050565b5f602082840312156109a5575f5ffd5b813560ff81168114610820575f5ffd5b5f60033d111561033d5760045f5f3e505f5160e01c90565b601f8201601f1916810167ffffffffffffffff811182821017156109ff57634e487b7160e01b5f52604160045260245ffd5b6040525050565b5f60443d1015610a135790565b6040513d600319016004823e80513d602482011167ffffffffffffffff82111715610a3d57505090565b808201805167ffffffffffffffff811115610a59575050505090565b3d8401600319018282016020011115610a73575050505090565b610a82602082850101856109cd565b509392505050565b5f5f60233d1115610aa357602060045f3e50505f516001905b909156fe5769746e657373207769746e65737329546f6b656e5065726d697373696f6e73286164647265737320746f6b656e2c75696e7432353620616d6f756e74295769746e657373286164647265737320746f2c75696e743235362076616c69644166746572299b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a264697066735822122027467754a9b59749d429ab315c3464fc316abae10e53d8b86e8fa8ae8a4a0e1f64736f6c634300081c0033000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba3
\ No newline at end of file
diff --git a/contracts/evm/src/x402BasePermit2Proxy.sol b/contracts/evm/src/x402BasePermit2Proxy.sol
index c180bd7a9f..46c6c8863a 100644
--- a/contracts/evm/src/x402BasePermit2Proxy.sol
+++ b/contracts/evm/src/x402BasePermit2Proxy.sol
@@ -28,15 +28,6 @@ abstract contract x402BasePermit2Proxy is ReentrancyGuard {
/// @notice The Permit2 contract address (set once at construction, immutable)
ISignatureTransfer public immutable PERMIT2;
- /// @notice EIP-712 type string for witness data
- /// @dev Must match the exact format expected by Permit2
- /// Types must be in ALPHABETICAL order after the primary type (TokenPermissions < Witness)
- string public constant WITNESS_TYPE_STRING =
- "Witness witness)TokenPermissions(address token,uint256 amount)Witness(address to,address facilitator,uint256 validAfter)";
-
- /// @notice EIP-712 typehash for witness struct
- bytes32 public constant WITNESS_TYPEHASH = keccak256("Witness(address to,address facilitator,uint256 validAfter)");
-
/// @notice Emitted when settle() completes successfully
event Settled();
@@ -79,24 +70,6 @@ abstract contract x402BasePermit2Proxy is ReentrancyGuard {
/// @notice Thrown when EIP-2612 permit value doesn't match Permit2 permitted amount
error Permit2612AmountMismatch();
- /// @notice Thrown when msg.sender does not match the facilitator in the witness
- error UnauthorizedFacilitator();
-
- /**
- * @notice Witness data structure for payment authorization
- * @param to Destination address (immutable once signed)
- * @param facilitator Address authorized to settle this payment (must be msg.sender)
- * @param validAfter Earliest timestamp when payment can be settled
- * @dev The upper time bound is enforced by Permit2's deadline field.
- * The facilitator field prevents frontrunning/griefing by binding the
- * settlement caller to the payer's signature.
- */
- struct Witness {
- address to;
- address facilitator;
- uint256 validAfter;
- }
-
/**
* @notice EIP-2612 permit parameters grouped to reduce stack depth
* @param value Approval amount for Permit2
@@ -129,43 +102,37 @@ abstract contract x402BasePermit2Proxy is ReentrancyGuard {
/**
* @notice Internal settlement logic shared by all settlement functions
- * @dev Validates all parameters and executes the Permit2 transfer
+ * @dev Validates common parameters and executes the Permit2 witness transfer.
+ * Each child contract computes its own witnessHash and witnessTypeString
+ * based on its Witness struct definition.
* @param permit The Permit2 transfer authorization
* @param settlementAmount The actual amount to transfer (may be <= permit.permitted.amount)
* @param owner The token owner (payer)
- * @param witness The witness data containing destination and validity window
+ * @param to The destination address for the transfer
+ * @param validAfter Earliest timestamp when payment can be settled
+ * @param witnessHash The EIP-712 hash of the child's witness struct
+ * @param witnessTypeString The EIP-712 type string for the child's witness
* @param signature The payer's signature
*/
function _settle(
ISignatureTransfer.PermitTransferFrom calldata permit,
uint256 settlementAmount,
address owner,
- Witness calldata witness,
+ address to,
+ uint256 validAfter,
+ bytes32 witnessHash,
+ string memory witnessTypeString,
bytes calldata signature
) internal {
- // Validate amount is non-zero to prevent no-op settlements that consume nonces
if (settlementAmount == 0) revert InvalidAmount();
-
- // Validate addresses
if (owner == address(0)) revert InvalidOwner();
- if (witness.to == address(0)) revert InvalidDestination();
-
- // Validate caller is the authorized facilitator signed over by the payer
- if (msg.sender != witness.facilitator) revert UnauthorizedFacilitator();
+ if (to == address(0)) revert InvalidDestination();
+ if (block.timestamp < validAfter) revert PaymentTooEarly();
- // Validate time window (upper bound enforced by Permit2's deadline)
- if (block.timestamp < witness.validAfter) revert PaymentTooEarly();
-
- // Prepare transfer details with destination from witness
ISignatureTransfer.SignatureTransferDetails memory transferDetails =
- ISignatureTransfer.SignatureTransferDetails({to: witness.to, requestedAmount: settlementAmount});
-
- // Reconstruct witness hash to enforce integrity
- bytes32 witnessHash =
- keccak256(abi.encode(WITNESS_TYPEHASH, witness.to, witness.facilitator, witness.validAfter));
+ ISignatureTransfer.SignatureTransferDetails({to: to, requestedAmount: settlementAmount});
- // Execute transfer via Permit2
- PERMIT2.permitWitnessTransferFrom(permit, transferDetails, owner, witnessHash, WITNESS_TYPE_STRING, signature);
+ PERMIT2.permitWitnessTransferFrom(permit, transferDetails, owner, witnessHash, witnessTypeString, signature);
}
/**
@@ -184,21 +151,19 @@ abstract contract x402BasePermit2Proxy is ReentrancyGuard {
EIP2612Permit calldata permit2612,
uint256 permittedAmount
) internal {
- if (permit2612.value != permittedAmount) revert Permit2612AmountMismatch();
+ if (permit2612.value != permittedAmount) {
+ revert Permit2612AmountMismatch();
+ }
try IERC20Permit(token).permit(
owner, address(PERMIT2), permit2612.value, permit2612.deadline, permit2612.v, permit2612.r, permit2612.s
) {
// EIP-2612 permit succeeded
} catch Error(string memory reason) {
- // Legacy revert(string) or require(condition, string) — e.g. older token implementations
emit EIP2612PermitFailedWithReason(token, owner, reason);
} catch Panic(uint256 errorCode) {
- // Solidity panic — e.g. arithmetic overflow in non-standard implementations
emit EIP2612PermitFailedWithPanic(token, owner, errorCode);
} catch (bytes memory data) {
- // Custom errors (e.g. OZ v5 ERC2612ExpiredSignature, ERC2612InvalidSigner),
- // empty reverts, or out-of-gas from non-EIP-2612 tokens
emit EIP2612PermitFailedWithData(token, owner, data);
}
}
diff --git a/contracts/evm/src/x402ExactPermit2Proxy.sol b/contracts/evm/src/x402ExactPermit2Proxy.sol
index 44083c1992..56e0e5a4a1 100644
--- a/contracts/evm/src/x402ExactPermit2Proxy.sol
+++ b/contracts/evm/src/x402ExactPermit2Proxy.sol
@@ -12,12 +12,29 @@ import {ISignatureTransfer} from "./interfaces/ISignatureTransfer.sol";
* It uses the "witness" pattern to cryptographically bind the payment destination,
* preventing facilitators from redirecting funds.
*
- * Unlike x402UptoPermit2Proxy, this contract always transfers the EXACT permitted
- * amount, similar to EIP-3009's transferWithAuthorization behavior.
+ * This contract always transfers the EXACT permitted amount, similar to
+ * EIP-3009's transferWithAuthorization behavior.
*
* @author x402 Protocol
*/
contract x402ExactPermit2Proxy is x402BasePermit2Proxy {
+ /// @notice EIP-712 type string for witness data
+ string public constant WITNESS_TYPE_STRING =
+ "Witness witness)TokenPermissions(address token,uint256 amount)Witness(address to,uint256 validAfter)";
+
+ /// @notice EIP-712 typehash for witness struct
+ bytes32 public constant WITNESS_TYPEHASH = keccak256("Witness(address to,uint256 validAfter)");
+
+ /**
+ * @notice Witness data structure for payment authorization
+ * @param to Destination address (immutable once signed)
+ * @param validAfter Earliest timestamp when payment can be settled
+ */
+ struct Witness {
+ address to;
+ uint256 validAfter;
+ }
+
constructor(
address _permit2
) x402BasePermit2Proxy(_permit2) {}
@@ -37,7 +54,17 @@ contract x402ExactPermit2Proxy is x402BasePermit2Proxy {
Witness calldata witness,
bytes calldata signature
) external nonReentrant {
- _settle(permit, permit.permitted.amount, owner, witness, signature);
+ bytes32 witnessHash = keccak256(abi.encode(WITNESS_TYPEHASH, witness.to, witness.validAfter));
+ _settle(
+ permit,
+ permit.permitted.amount,
+ owner,
+ witness.to,
+ witness.validAfter,
+ witnessHash,
+ WITNESS_TYPE_STRING,
+ signature
+ );
emit Settled();
}
@@ -63,7 +90,17 @@ contract x402ExactPermit2Proxy is x402BasePermit2Proxy {
bytes calldata signature
) external nonReentrant {
_executePermit(permit.permitted.token, owner, permit2612, permit.permitted.amount);
- _settle(permit, permit.permitted.amount, owner, witness, signature);
+ bytes32 witnessHash = keccak256(abi.encode(WITNESS_TYPEHASH, witness.to, witness.validAfter));
+ _settle(
+ permit,
+ permit.permitted.amount,
+ owner,
+ witness.to,
+ witness.validAfter,
+ witnessHash,
+ WITNESS_TYPE_STRING,
+ signature
+ );
emit SettledWithPermit();
}
}
diff --git a/contracts/evm/src/x402UptoPermit2Proxy.sol b/contracts/evm/src/x402UptoPermit2Proxy.sol
index fc03b23a06..236e9e858d 100644
--- a/contracts/evm/src/x402UptoPermit2Proxy.sol
+++ b/contracts/evm/src/x402UptoPermit2Proxy.sol
@@ -12,20 +12,42 @@ import {ISignatureTransfer} from "./interfaces/ISignatureTransfer.sol";
* It uses the "witness" pattern to cryptographically bind the payment destination,
* preventing facilitators from redirecting funds.
*
- * Unlike x402ExactPermit2Proxy, this contract allows the facilitator to specify
- * how much to transfer (up to the permitted amount), useful for scenarios where
- * the actual amount is determined at settlement time.
+ * This contract allows the facilitator to transfer up to the permitted amount.
+ * The witness includes a facilitator field so only the authorized caller can
+ * choose the final settlement amount.
*
* @author x402 Protocol
*/
contract x402UptoPermit2Proxy is x402BasePermit2Proxy {
- constructor(
- address _permit2
- ) x402BasePermit2Proxy(_permit2) {}
+ /// @notice EIP-712 type string for witness data
+ string public constant WITNESS_TYPE_STRING =
+ "Witness witness)TokenPermissions(address token,uint256 amount)Witness(address to,address facilitator,uint256 validAfter)";
+
+ /// @notice EIP-712 typehash for witness struct
+ bytes32 public constant WITNESS_TYPEHASH = keccak256("Witness(address to,address facilitator,uint256 validAfter)");
+
+ /// @notice Thrown when msg.sender does not match the facilitator in the witness
+ error UnauthorizedFacilitator();
/// @notice Thrown when requested amount exceeds permitted amount
error AmountExceedsPermitted();
+ /**
+ * @notice Witness data structure for payment authorization
+ * @param to Destination address (immutable once signed)
+ * @param facilitator Address authorized to settle this payment (must be msg.sender)
+ * @param validAfter Earliest timestamp when payment can be settled
+ */
+ struct Witness {
+ address to;
+ address facilitator;
+ uint256 validAfter;
+ }
+
+ constructor(
+ address _permit2
+ ) x402BasePermit2Proxy(_permit2) {}
+
/**
* @notice Settles a payment using a Permit2 signature
* @dev This is the standard settlement path when user has already approved Permit2
@@ -43,7 +65,10 @@ contract x402UptoPermit2Proxy is x402BasePermit2Proxy {
bytes calldata signature
) external nonReentrant {
if (amount > permit.permitted.amount) revert AmountExceedsPermitted();
- _settle(permit, amount, owner, witness, signature);
+ if (msg.sender != witness.facilitator) revert UnauthorizedFacilitator();
+ bytes32 witnessHash =
+ keccak256(abi.encode(WITNESS_TYPEHASH, witness.to, witness.facilitator, witness.validAfter));
+ _settle(permit, amount, owner, witness.to, witness.validAfter, witnessHash, WITNESS_TYPE_STRING, signature);
emit Settled();
}
@@ -70,8 +95,11 @@ contract x402UptoPermit2Proxy is x402BasePermit2Proxy {
bytes calldata signature
) external nonReentrant {
if (amount > permit.permitted.amount) revert AmountExceedsPermitted();
+ if (msg.sender != witness.facilitator) revert UnauthorizedFacilitator();
_executePermit(permit.permitted.token, owner, permit2612, permit.permitted.amount);
- _settle(permit, amount, owner, witness, signature);
+ bytes32 witnessHash =
+ keccak256(abi.encode(WITNESS_TYPEHASH, witness.to, witness.facilitator, witness.validAfter));
+ _settle(permit, amount, owner, witness.to, witness.validAfter, witnessHash, WITNESS_TYPE_STRING, signature);
emit SettledWithPermit();
}
}
diff --git a/contracts/evm/test/invariants/X402ExactInvariants.t.sol b/contracts/evm/test/invariants/X402ExactInvariants.t.sol
index 74c6407bd9..9c20f7133f 100644
--- a/contracts/evm/test/invariants/X402ExactInvariants.t.sol
+++ b/contracts/evm/test/invariants/X402ExactInvariants.t.sol
@@ -45,12 +45,11 @@ contract X402ExactHandler is Test {
deadline: t + 3600
});
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t > 60 ? t - 60 : 0});
+ x402ExactPermit2Proxy.Witness memory witness =
+ x402ExactPermit2Proxy.Witness({to: recipient, validAfter: t > 60 ? t - 60 : 0});
bytes memory sig = abi.encodePacked(bytes32(uint256(1)), bytes32(uint256(2)), uint8(27));
- // Exact variant has no amount parameter - always transfers the full permitted amount
try proxy.settle(permit, payer, witness, sig) {
totalSettled += amount;
settleCallCount++;
diff --git a/contracts/evm/test/invariants/X402UptoInvariants.t.sol b/contracts/evm/test/invariants/X402UptoInvariants.t.sol
index 534fa8c96c..038d820126 100644
--- a/contracts/evm/test/invariants/X402UptoInvariants.t.sol
+++ b/contracts/evm/test/invariants/X402UptoInvariants.t.sol
@@ -45,8 +45,8 @@ contract X402UptoHandler is Test {
deadline: t + 3600
});
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t > 60 ? t - 60 : 0});
+ x402UptoPermit2Proxy.Witness memory witness =
+ x402UptoPermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t > 60 ? t - 60 : 0});
bytes memory sig = abi.encodePacked(bytes32(uint256(1)), bytes32(uint256(2)), uint8(27));
diff --git a/contracts/evm/test/mocks/MaliciousReentrantExact.sol b/contracts/evm/test/mocks/MaliciousReentrantExact.sol
index 1d44f8ad61..8d9d522d32 100644
--- a/contracts/evm/test/mocks/MaliciousReentrantExact.sol
+++ b/contracts/evm/test/mocks/MaliciousReentrantExact.sol
@@ -3,7 +3,6 @@ pragma solidity ^0.8.20;
import {ISignatureTransfer} from "../../src/interfaces/ISignatureTransfer.sol";
import {x402ExactPermit2Proxy} from "../../src/x402ExactPermit2Proxy.sol";
-import {x402BasePermit2Proxy} from "../../src/x402BasePermit2Proxy.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract MaliciousReentrantExact is ISignatureTransfer {
@@ -12,7 +11,7 @@ contract MaliciousReentrantExact is ISignatureTransfer {
ISignatureTransfer.PermitTransferFrom public storedPermit;
address public storedOwner;
- x402BasePermit2Proxy.Witness public storedWitness;
+ x402ExactPermit2Proxy.Witness public storedWitness;
bytes public storedSignature;
mapping(address => mapping(uint256 => uint256)) public nonceBitmapStorage;
@@ -32,7 +31,7 @@ contract MaliciousReentrantExact is ISignatureTransfer {
function setAttackParams(
ISignatureTransfer.PermitTransferFrom calldata permit,
address owner,
- x402BasePermit2Proxy.Witness calldata witness,
+ x402ExactPermit2Proxy.Witness calldata witness,
bytes calldata signature
) external {
storedPermit = permit;
@@ -67,7 +66,6 @@ contract MaliciousReentrantExact is ISignatureTransfer {
nonceBitmapStorage[owner][wordPos] |= (1 << bitPos);
if (attemptReentry && address(target) != address(0)) {
- // Exact variant has no amount parameter
target.settle(storedPermit, storedOwner, storedWitness, storedSignature);
}
diff --git a/contracts/evm/test/mocks/MaliciousReentrantUpto.sol b/contracts/evm/test/mocks/MaliciousReentrantUpto.sol
index 38a4d2f019..73e6310687 100644
--- a/contracts/evm/test/mocks/MaliciousReentrantUpto.sol
+++ b/contracts/evm/test/mocks/MaliciousReentrantUpto.sol
@@ -13,7 +13,7 @@ contract MaliciousReentrantUpto is ISignatureTransfer {
ISignatureTransfer.PermitTransferFrom public storedPermit;
uint256 public storedAmount;
address public storedOwner;
- x402BasePermit2Proxy.Witness public storedWitness;
+ x402UptoPermit2Proxy.Witness public storedWitness;
bytes public storedSignature;
mapping(address => mapping(uint256 => uint256)) public nonceBitmapStorage;
@@ -34,7 +34,7 @@ contract MaliciousReentrantUpto is ISignatureTransfer {
ISignatureTransfer.PermitTransferFrom calldata permit,
uint256 amount,
address owner,
- x402BasePermit2Proxy.Witness calldata witness,
+ x402UptoPermit2Proxy.Witness calldata witness,
bytes calldata signature
) external {
storedPermit = permit;
diff --git a/contracts/evm/test/x402ExactPermit2Proxy.fork.t.sol b/contracts/evm/test/x402ExactPermit2Proxy.fork.t.sol
index 66a3e367ff..9a599e12eb 100644
--- a/contracts/evm/test/x402ExactPermit2Proxy.fork.t.sol
+++ b/contracts/evm/test/x402ExactPermit2Proxy.fork.t.sol
@@ -15,7 +15,7 @@ contract X402ExactPermit2ProxyForkTest is Test {
bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
bytes32 constant PERMIT_TYPEHASH = keccak256(
- "PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,Witness witness)TokenPermissions(address token,uint256 amount)Witness(address to,address facilitator,uint256 validAfter)"
+ "PermitWitnessTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline,Witness witness)TokenPermissions(address token,uint256 amount)Witness(address to,uint256 validAfter)"
);
bytes32 constant TOKEN_PERMISSIONS_TYPEHASH = keccak256("TokenPermissions(address token,uint256 amount)");
@@ -35,7 +35,6 @@ contract X402ExactPermit2ProxyForkTest is Test {
if (block.chainid == 31_337) return;
require(PERMIT2.code.length > 0, "Permit2 not deployed");
- // Use a key that produces an EOA (not a deployed contract) on the fork
payerKey = uint256(keccak256("x402-test-payer"));
payer = vm.addr(payerKey);
recipient = makeAddr("recipient");
@@ -68,11 +67,9 @@ contract X402ExactPermit2ProxyForkTest is Test {
uint256 amount,
uint256 nonce,
uint256 deadline,
- x402BasePermit2Proxy.Witness memory witness
+ x402ExactPermit2Proxy.Witness memory witness
) internal view returns (bytes memory) {
- // Must match contract's witness hash computation order
- bytes32 witnessHash =
- keccak256(abi.encode(proxy.WITNESS_TYPEHASH(), witness.to, witness.facilitator, witness.validAfter));
+ bytes32 witnessHash = keccak256(abi.encode(proxy.WITNESS_TYPEHASH(), witness.to, witness.validAfter));
bytes32 tokenHash = keccak256(abi.encode(TOKEN_PERMISSIONS_TYPEHASH, tokenAddr, amount));
@@ -90,8 +87,8 @@ contract X402ExactPermit2ProxyForkTest is Test {
uint256 nonce = _nonce(1);
uint256 deadline = t + 3600;
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402ExactPermit2Proxy.Witness memory witness =
+ x402ExactPermit2Proxy.Witness({to: recipient, validAfter: t - 60});
bytes memory sig = _sign(address(token), TRANSFER_AMOUNT, nonce, deadline, witness);
@@ -115,8 +112,8 @@ contract X402ExactPermit2ProxyForkTest is Test {
uint256 t = block.timestamp;
uint256 nonce = _nonce(2);
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402ExactPermit2Proxy.Witness memory witness =
+ x402ExactPermit2Proxy.Witness({to: recipient, validAfter: t - 60});
ISignatureTransfer.PermitTransferFrom memory permit = ISignatureTransfer.PermitTransferFrom({
permitted: ISignatureTransfer.TokenPermissions({token: address(token), amount: TRANSFER_AMOUNT}),
@@ -135,12 +132,11 @@ contract X402ExactPermit2ProxyForkTest is Test {
uint256 nonce = _nonce(3);
uint256 deadline = t + 3600;
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402ExactPermit2Proxy.Witness memory witness =
+ x402ExactPermit2Proxy.Witness({to: recipient, validAfter: t - 60});
uint256 wrongKey = 0xdeadbeef;
- bytes32 witnessHash =
- keccak256(abi.encode(proxy.WITNESS_TYPEHASH(), witness.to, witness.facilitator, witness.validAfter));
+ bytes32 witnessHash = keccak256(abi.encode(proxy.WITNESS_TYPEHASH(), witness.to, witness.validAfter));
bytes32 tokenHash = keccak256(abi.encode(TOKEN_PERMISSIONS_TYPEHASH, address(token), TRANSFER_AMOUNT));
bytes32 structHash =
keccak256(abi.encode(PERMIT_TYPEHASH, tokenHash, address(proxy), nonce, deadline, witnessHash));
@@ -163,8 +159,8 @@ contract X402ExactPermit2ProxyForkTest is Test {
uint256 nonce = _nonce(4);
uint256 deadline = t + 3600;
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402ExactPermit2Proxy.Witness memory witness =
+ x402ExactPermit2Proxy.Witness({to: recipient, validAfter: t - 60});
bytes memory sig = _sign(address(token), TRANSFER_AMOUNT, nonce, deadline, witness);
@@ -185,8 +181,8 @@ contract X402ExactPermit2ProxyForkTest is Test {
uint256 nonce = _nonce(5);
uint256 deadline = t - 60; // expired (Permit2's deadline enforces the upper bound)
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 120});
+ x402ExactPermit2Proxy.Witness memory witness =
+ x402ExactPermit2Proxy.Witness({to: recipient, validAfter: t - 120});
bytes memory sig = _sign(address(token), TRANSFER_AMOUNT, nonce, deadline, witness);
@@ -207,8 +203,8 @@ contract X402ExactPermit2ProxyForkTest is Test {
address attacker = makeAddr("attacker");
- x402BasePermit2Proxy.Witness memory signedWitness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402ExactPermit2Proxy.Witness memory signedWitness =
+ x402ExactPermit2Proxy.Witness({to: recipient, validAfter: t - 60});
bytes memory sig = _sign(address(token), TRANSFER_AMOUNT, nonce, deadline, signedWitness);
@@ -218,11 +214,8 @@ contract X402ExactPermit2ProxyForkTest is Test {
deadline: deadline
});
- x402BasePermit2Proxy.Witness memory tamperedWitness = x402BasePermit2Proxy.Witness({
- to: attacker,
- facilitator: signedWitness.facilitator,
- validAfter: signedWitness.validAfter
- });
+ x402ExactPermit2Proxy.Witness memory tamperedWitness =
+ x402ExactPermit2Proxy.Witness({to: attacker, validAfter: signedWitness.validAfter});
vm.expectRevert();
proxy.settle(permit, payer, tamperedWitness, sig);
diff --git a/contracts/evm/test/x402ExactPermit2Proxy.t.sol b/contracts/evm/test/x402ExactPermit2Proxy.t.sol
index 3e2e58e3db..5a6039663f 100644
--- a/contracts/evm/test/x402ExactPermit2Proxy.t.sol
+++ b/contracts/evm/test/x402ExactPermit2Proxy.t.sol
@@ -56,12 +56,8 @@ contract X402ExactPermit2ProxyTest is Test {
});
}
- function _witness(
- address to,
- address facilitator,
- uint256 validAfter
- ) internal pure returns (x402BasePermit2Proxy.Witness memory) {
- return x402BasePermit2Proxy.Witness({to: to, facilitator: facilitator, validAfter: validAfter});
+ function _witness(address to, uint256 validAfter) internal pure returns (x402ExactPermit2Proxy.Witness memory) {
+ return x402ExactPermit2Proxy.Witness({to: to, validAfter: validAfter});
}
function _sig() internal pure returns (bytes memory) {
@@ -84,35 +80,25 @@ contract X402ExactPermit2ProxyTest is Test {
function test_settle_revertsOnZeroOwner() public {
uint256 t = block.timestamp;
vm.expectRevert(x402BasePermit2Proxy.InvalidOwner.selector);
- proxy.settle(
- _permit(TRANSFER_AMOUNT, 0, t + 3600), address(0), _witness(recipient, address(this), t - 60), _sig()
- );
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), address(0), _witness(recipient, t - 60), _sig());
}
function test_settle_revertsOnZeroDestination() public {
uint256 t = block.timestamp;
vm.expectRevert(x402BasePermit2Proxy.InvalidDestination.selector);
- proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(address(0), address(this), t - 60), _sig());
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(address(0), t - 60), _sig());
}
function test_settle_revertsBeforeValidAfter() public {
uint256 t = block.timestamp;
vm.expectRevert(x402BasePermit2Proxy.PaymentTooEarly.selector);
- proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, address(this), t + 60), _sig());
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, t + 60), _sig());
}
function test_settle_revertsOnZeroAmount() public {
uint256 t = block.timestamp;
vm.expectRevert(x402BasePermit2Proxy.InvalidAmount.selector);
- proxy.settle(_permit(0, 0, t + 3600), payer, _witness(recipient, address(this), t - 60), _sig());
- }
-
- function test_settle_revertsOnUnauthorizedFacilitator() public {
- uint256 t = block.timestamp;
- address attacker = makeAddr("attacker");
- vm.prank(attacker);
- vm.expectRevert(x402BasePermit2Proxy.UnauthorizedFacilitator.selector);
- proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settle(_permit(0, 0, t + 3600), payer, _witness(recipient, t - 60), _sig());
}
// Note: validBefore was removed - upper time bound is enforced by Permit2's deadline
@@ -123,7 +109,7 @@ contract X402ExactPermit2ProxyTest is Test {
uint256 t = block.timestamp;
uint256 balanceBefore = token.balanceOf(recipient);
- proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, t - 60), _sig());
assertEq(token.balanceOf(recipient) - balanceBefore, TRANSFER_AMOUNT);
}
@@ -134,12 +120,24 @@ contract X402ExactPermit2ProxyTest is Test {
vm.expectEmit(false, false, false, false);
emit Settled();
- proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, t - 60), _sig());
}
function test_settle_atExactValidAfter() public {
uint256 t = block.timestamp;
- proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, address(this), t), _sig());
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, t), _sig());
+ assertEq(token.balanceOf(recipient), TRANSFER_AMOUNT);
+ }
+
+ // --- Security: Permissionless settlement ---
+
+ function test_settle_anyoneCanSettle() public {
+ uint256 t = block.timestamp;
+ address anyone = makeAddr("anyone");
+
+ vm.prank(anyone);
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, t - 60), _sig());
+
assertEq(token.balanceOf(recipient), TRANSFER_AMOUNT);
}
@@ -161,7 +159,7 @@ contract X402ExactPermit2ProxyTest is Test {
nonce: 0,
deadline: t + 3600
});
- x402ExactPermit2Proxy.Witness memory witness = _witness(recipient, address(this), t - 60);
+ x402ExactPermit2Proxy.Witness memory witness = _witness(recipient, t - 60);
maliciousPermit2.setAttemptReentry(true);
maliciousPermit2.setAttackParams(permit, payer, witness, _sig());
@@ -174,7 +172,7 @@ contract X402ExactPermit2ProxyTest is Test {
function test_settle_proxyNeverHoldsTokens() public {
uint256 t = block.timestamp;
- proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, t + 3600), payer, _witness(recipient, t - 60), _sig());
assertEq(token.balanceOf(address(proxy)), 0);
}
@@ -204,7 +202,7 @@ contract X402ExactPermit2ProxyTest is Test {
vm.expectEmit(false, false, false, false);
emit SettledWithPermit();
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
assertEq(permitToken.balanceOf(recipient), TRANSFER_AMOUNT);
}
@@ -232,7 +230,7 @@ contract X402ExactPermit2ProxyTest is Test {
s: bytes32(uint256(2))
});
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
assertEq(permitToken.balanceOf(recipient), TRANSFER_AMOUNT);
}
@@ -260,7 +258,7 @@ contract X402ExactPermit2ProxyTest is Test {
vm.expectEmit(true, true, false, true);
emit EIP2612PermitFailedWithReason(address(permitToken), payer, "Permit failed");
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
}
function test_settleWithPermit_emitsPermitFailedWithPanic() public {
@@ -286,7 +284,7 @@ contract X402ExactPermit2ProxyTest is Test {
vm.expectEmit(true, true, false, true);
emit EIP2612PermitFailedWithPanic(address(permitToken), payer, 0x12);
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
}
function test_settleWithPermit_emitsPermitFailedWithData() public {
@@ -312,7 +310,7 @@ contract X402ExactPermit2ProxyTest is Test {
vm.expectEmit(true, true, false, false);
emit EIP2612PermitFailedWithData(address(permitToken), payer, "");
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
}
function test_settleWithPermit_doesNotEmitPermitFailedOnSuccess() public {
@@ -336,7 +334,7 @@ contract X402ExactPermit2ProxyTest is Test {
});
vm.recordLogs();
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
VmSafe.Log[] memory entries = vm.getRecordedLogs();
bytes32 reasonSig = keccak256("EIP2612PermitFailedWithReason(address,address,string)");
@@ -373,7 +371,7 @@ contract X402ExactPermit2ProxyTest is Test {
});
vm.expectRevert(x402BasePermit2Proxy.InvalidAmount.selector);
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
}
function test_settleWithPermit_revertsWhenPermit2612ValueTooSmall() public {
@@ -396,7 +394,7 @@ contract X402ExactPermit2ProxyTest is Test {
});
vm.expectRevert(x402BasePermit2Proxy.Permit2612AmountMismatch.selector);
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
}
function test_settleWithPermit_revertsWhenPermit2612ValueTooLarge() public {
@@ -419,7 +417,7 @@ contract X402ExactPermit2ProxyTest is Test {
});
vm.expectRevert(x402BasePermit2Proxy.Permit2612AmountMismatch.selector);
- proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settleWithPermit(permit2612, permit, payer, _witness(recipient, t - 60), _sig());
}
// --- Fuzz: Time window ---
@@ -430,12 +428,7 @@ contract X402ExactPermit2ProxyTest is Test {
vm.warp(currentTime);
- proxy.settle(
- _permit(TRANSFER_AMOUNT, 0, currentTime + 3600),
- payer,
- _witness(recipient, address(this), validAfter),
- _sig()
- );
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, currentTime + 3600), payer, _witness(recipient, validAfter), _sig());
assertEq(token.balanceOf(recipient), TRANSFER_AMOUNT);
}
@@ -447,12 +440,7 @@ contract X402ExactPermit2ProxyTest is Test {
vm.warp(currentTime);
vm.expectRevert();
- proxy.settle(
- _permit(TRANSFER_AMOUNT, 0, currentTime + 3600),
- payer,
- _witness(recipient, address(this), validAfter),
- _sig()
- );
+ proxy.settle(_permit(TRANSFER_AMOUNT, 0, currentTime + 3600), payer, _witness(recipient, validAfter), _sig());
}
// --- Fuzz: Amount (exact always transfers full permitted amount) ---
@@ -464,7 +452,7 @@ contract X402ExactPermit2ProxyTest is Test {
uint256 t = block.timestamp;
- proxy.settle(_permit(permitted, 0, t + 3600), payer, _witness(recipient, address(this), t - 60), _sig());
+ proxy.settle(_permit(permitted, 0, t + 3600), payer, _witness(recipient, t - 60), _sig());
assertEq(token.balanceOf(recipient), permitted);
}
diff --git a/contracts/evm/test/x402UptoPermit2Proxy.fork.t.sol b/contracts/evm/test/x402UptoPermit2Proxy.fork.t.sol
index 44364e2fda..a065e19d2a 100644
--- a/contracts/evm/test/x402UptoPermit2Proxy.fork.t.sol
+++ b/contracts/evm/test/x402UptoPermit2Proxy.fork.t.sol
@@ -68,7 +68,7 @@ contract X402UptoPermit2ProxyForkTest is Test {
uint256 amount,
uint256 nonce,
uint256 deadline,
- x402BasePermit2Proxy.Witness memory witness
+ x402UptoPermit2Proxy.Witness memory witness
) internal view returns (bytes memory) {
// Must match contract's witness hash computation order
bytes32 witnessHash =
@@ -90,8 +90,8 @@ contract X402UptoPermit2ProxyForkTest is Test {
uint256 nonce = _nonce(1);
uint256 deadline = t + 3600;
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402UptoPermit2Proxy.Witness memory witness =
+ x402UptoPermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
bytes memory sig = _sign(address(token), TRANSFER_AMOUNT, nonce, deadline, witness);
@@ -115,8 +115,8 @@ contract X402UptoPermit2ProxyForkTest is Test {
uint256 t = block.timestamp;
uint256 nonce = _nonce(2);
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402UptoPermit2Proxy.Witness memory witness =
+ x402UptoPermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
ISignatureTransfer.PermitTransferFrom memory permit = ISignatureTransfer.PermitTransferFrom({
permitted: ISignatureTransfer.TokenPermissions({token: address(token), amount: TRANSFER_AMOUNT}),
@@ -135,8 +135,8 @@ contract X402UptoPermit2ProxyForkTest is Test {
uint256 nonce = _nonce(3);
uint256 deadline = t + 3600;
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402UptoPermit2Proxy.Witness memory witness =
+ x402UptoPermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
uint256 wrongKey = 0xdeadbeef;
bytes32 witnessHash =
@@ -163,8 +163,8 @@ contract X402UptoPermit2ProxyForkTest is Test {
uint256 nonce = _nonce(4);
uint256 deadline = t + 3600;
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402UptoPermit2Proxy.Witness memory witness =
+ x402UptoPermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
bytes memory sig = _sign(address(token), TRANSFER_AMOUNT, nonce, deadline, witness);
@@ -185,8 +185,8 @@ contract X402UptoPermit2ProxyForkTest is Test {
uint256 nonce = _nonce(5);
uint256 deadline = t - 60; // expired (Permit2's deadline enforces the upper bound)
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 120});
+ x402UptoPermit2Proxy.Witness memory witness =
+ x402UptoPermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 120});
bytes memory sig = _sign(address(token), TRANSFER_AMOUNT, nonce, deadline, witness);
@@ -207,8 +207,8 @@ contract X402UptoPermit2ProxyForkTest is Test {
address attacker = makeAddr("attacker");
- x402BasePermit2Proxy.Witness memory signedWitness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402UptoPermit2Proxy.Witness memory signedWitness =
+ x402UptoPermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
bytes memory sig = _sign(address(token), TRANSFER_AMOUNT, nonce, deadline, signedWitness);
@@ -218,7 +218,7 @@ contract X402UptoPermit2ProxyForkTest is Test {
deadline: deadline
});
- x402BasePermit2Proxy.Witness memory tamperedWitness = x402BasePermit2Proxy.Witness({
+ x402UptoPermit2Proxy.Witness memory tamperedWitness = x402UptoPermit2Proxy.Witness({
to: attacker,
facilitator: signedWitness.facilitator,
validAfter: signedWitness.validAfter
@@ -235,8 +235,8 @@ contract X402UptoPermit2ProxyForkTest is Test {
uint256 permitted = TRANSFER_AMOUNT;
uint256 requested = permitted / 2;
- x402BasePermit2Proxy.Witness memory witness =
- x402BasePermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
+ x402UptoPermit2Proxy.Witness memory witness =
+ x402UptoPermit2Proxy.Witness({to: recipient, facilitator: address(this), validAfter: t - 60});
bytes memory sig = _sign(address(token), permitted, nonce, deadline, witness);
diff --git a/contracts/evm/test/x402UptoPermit2Proxy.t.sol b/contracts/evm/test/x402UptoPermit2Proxy.t.sol
index c90a99d3b2..8d41928340 100644
--- a/contracts/evm/test/x402UptoPermit2Proxy.t.sol
+++ b/contracts/evm/test/x402UptoPermit2Proxy.t.sol
@@ -60,8 +60,8 @@ contract X402UptoPermit2ProxyTest is Test {
address to,
address facilitator,
uint256 validAfter
- ) internal pure returns (x402BasePermit2Proxy.Witness memory) {
- return x402BasePermit2Proxy.Witness({to: to, facilitator: facilitator, validAfter: validAfter});
+ ) internal pure returns (x402UptoPermit2Proxy.Witness memory) {
+ return x402UptoPermit2Proxy.Witness({to: to, facilitator: facilitator, validAfter: validAfter});
}
function _sig() internal pure returns (bytes memory) {
@@ -131,7 +131,7 @@ contract X402UptoPermit2ProxyTest is Test {
uint256 t = block.timestamp;
address attacker = makeAddr("attacker");
vm.prank(attacker);
- vm.expectRevert(x402BasePermit2Proxy.UnauthorizedFacilitator.selector);
+ vm.expectRevert(x402UptoPermit2Proxy.UnauthorizedFacilitator.selector);
proxy.settle(
_permit(TRANSFER_AMOUNT, 0, t + 3600),
TRANSFER_AMOUNT,
diff --git a/contracts/evm/vanity-miner/src/main.rs b/contracts/evm/vanity-miner/src/main.rs
index f5b37ae319..e9fb5d10bf 100644
--- a/contracts/evm/vanity-miner/src/main.rs
+++ b/contracts/evm/vanity-miner/src/main.rs
@@ -13,14 +13,19 @@ const PREFIX: [u8; 2] = [0x40, 0x20]; // 0x4020
const EXACT_SUFFIX: [u8; 2] = [0x00, 0x01]; // ...0001
const UPTO_SUFFIX: [u8; 2] = [0x00, 0x02]; // ...0002
-// Init code hashes (computed from contracts - no constructor args for chain portability)
-// Run `forge script script/ComputeAddress.s.sol` to verify these match
-// x402ExactPermit2Proxy
+// Init code hashes: keccak256(creationCode ++ abi.encode(PERMIT2))
+// Run `forge script script/ComputeAddress.s.sol` to verify these match.
+//
+// IMPORTANT: The Exact hash is from the ORIGINAL build (with CBOR metadata enabled).
+// Since that bytecode is already deployed, we preserve it via script/data/exact-proxy-initcode.hex.
+// The Upto hash is from the current build (cbor_metadata = false, bytecode_hash = "none").
+//
+// x402ExactPermit2Proxy (pre-built initCode, includes CBOR metadata)
const EXACT_INIT_CODE_HASH: [u8; 32] =
- hex_literal::hex!("61f007aac96be95995d250c70b750b0c239f3c8cbc28b7b0e89761f84bc0c2bb");
-// x402UptoPermit2Proxy
+ hex_literal::hex!("e774d1d5a07218946ab54efe010b300481478b86861bb17d69c98a57f68a604c");
+// x402UptoPermit2Proxy (deterministic build, no CBOR metadata)
const UPTO_INIT_CODE_HASH: [u8; 32] =
- hex_literal::hex!("6bc5ae76d294a4e82cf7857326e018e5d9cd6e306ccfd1ff1300c08697eed7b2");
+ hex_literal::hex!("74f7a29cbc3c55f87cdef7f7c551643189e8bb62eed9de67753aebc402b83797");
fn compute_create2_address(salt: &[u8; 32], init_code_hash: &[u8; 32]) -> [u8; 20] {
let mut hasher = Keccak::v256();
@@ -115,23 +120,37 @@ fn mine_vanity(
}
fn main() {
+ let args: Vec = std::env::args().collect();
+ let filter = args.get(1).map(|s| s.as_str());
+
+ let mine_exact = matches!(filter, None | Some("exact"));
+ let mine_upto = matches!(filter, None | Some("upto"));
+
println!("\n🔍 x402 Vanity Address Miner (Rust)");
println!(" Prefix: 0x{}", hex::encode(PREFIX));
- println!(" Exact suffix: 0x{}", hex::encode(EXACT_SUFFIX));
- println!(" Upto suffix: 0x{}", hex::encode(UPTO_SUFFIX));
+ if mine_exact {
+ println!(" Exact suffix: 0x{}", hex::encode(EXACT_SUFFIX));
+ }
+ if mine_upto {
+ println!(" Upto suffix: 0x{}", hex::encode(UPTO_SUFFIX));
+ }
println!(" CREATE2 Deployer: 0x{}", hex::encode(CREATE2_DEPLOYER));
- // Get number of threads
let num_threads = rayon::current_num_threads();
println!(" Using {} threads", num_threads);
- // Mine for Exact contract
- let exact_result = mine_vanity("x402ExactPermit2Proxy", &EXACT_INIT_CODE_HASH, &PREFIX, &EXACT_SUFFIX);
+ let exact_result = if mine_exact {
+ mine_vanity("x402ExactPermit2Proxy", &EXACT_INIT_CODE_HASH, &PREFIX, &EXACT_SUFFIX)
+ } else {
+ None
+ };
- // Mine for Upto contract
- let upto_result = mine_vanity("x402UptoPermit2Proxy", &UPTO_INIT_CODE_HASH, &PREFIX, &UPTO_SUFFIX);
+ let upto_result = if mine_upto {
+ mine_vanity("x402UptoPermit2Proxy", &UPTO_INIT_CODE_HASH, &PREFIX, &UPTO_SUFFIX)
+ } else {
+ None
+ };
- // Summary
println!("\n{}", "=".repeat(60));
println!("SUMMARY");
println!("{}", "=".repeat(60));
@@ -148,12 +167,13 @@ fn main() {
println!(" Address: 0x{}", hex::encode(addr));
}
- if exact_result.is_some() && upto_result.is_some() {
- let (exact_salt, _) = exact_result.unwrap();
- let (upto_salt, _) = upto_result.unwrap();
+ if let (Some((exact_salt, _)), Some((upto_salt, _))) = (exact_result, upto_result) {
println!("\n// Update Deploy.s.sol with these values:");
println!("bytes32 constant EXACT_SALT = 0x{};", hex::encode(exact_salt));
println!("bytes32 constant UPTO_SALT = 0x{};", hex::encode(upto_salt));
+ } else if let Some((salt, _)) = exact_result.or(upto_result) {
+ println!("\n// Update Deploy.s.sol:");
+ println!("bytes32 constant SALT = 0x{};", hex::encode(salt));
}
}
diff --git a/docs/advanced-concepts/lifecycle-hooks.mdx b/docs/advanced-concepts/lifecycle-hooks.mdx
index cb8ccd8eb5..c37bab531e 100644
--- a/docs/advanced-concepts/lifecycle-hooks.mdx
+++ b/docs/advanced-concepts/lifecycle-hooks.mdx
@@ -118,7 +118,31 @@ Register hooks for HTTP-specific request handling before payment processing. Use
*Coming soon.*
- *Coming soon.*
+ ```go
+ import (
+ "context"
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ )
+
+ // Create resource server
+ resourceServer := x402.Newx402ResourceServer(
+ x402.WithFacilitatorClient(facilitatorClient),
+ )
+
+ // Wrap with HTTP server and register hook
+ httpServer := x402http.Wrappedx402HTTPResourceServer(routes, resourceServer).
+ OnProtectedRequest(func(ctx context.Context, reqCtx x402http.HTTPRequestContext, routeConfig x402http.RouteConfig) (*x402http.ProtectedRequestHookResult, error) {
+ apiKey := reqCtx.Adapter.GetHeader("X-API-Key")
+
+ if apiKey != "" && isValidApiKey(apiKey) {
+ return &x402http.ProtectedRequestHookResult{GrantAccess: true}, nil
+ }
+
+ // No valid API key — continue to payment flow
+ return nil, nil
+ })
+ ```
diff --git a/docs/core-concepts/client-server.md b/docs/core-concepts/client-server.md
index b914c52f99..83c3478a13 100644
--- a/docs/core-concepts/client-server.md
+++ b/docs/core-concepts/client-server.md
@@ -48,6 +48,19 @@ Servers can include:
Servers do not need to manage client identities or maintain session state. Verification and settlement are handled per request.
+#### Duplicate Settlement on Solana
+
+If your server settles payments directly on Solana (without delegating to a facilitator), be aware of a race condition: the same signed payment transaction can be submitted multiple times before the first submission is confirmed on-chain. Solana's RPC will return "success" for each submission, since the network deduplicates at the consensus level. A malicious client can exploit this to obtain access to multiple resources while only paying once.
+
+To mitigate this, servers that settle Solana payments themselves **must** maintain a short-lived, in-memory cache of transaction payloads currently being settled:
+
+1. After verification succeeds, derive a cache key from the transaction payload (e.g., the base64-encoded transaction string).
+2. If the key is already present in the cache, reject the settlement with a `"duplicate_settlement"` error.
+3. If the key is not present, insert it into the cache and proceed with settlement.
+4. Evict entries older than 120 seconds (approximately twice the Solana blockhash lifetime).
+
+If you are using a facilitator, the x402 SVM libraries already include built-in duplicate settlement protection via a `SettlementCache`. See the [Exact SVM Scheme Specification](https://github.com/coinbase/x402/blob/main/specs/schemes/exact/scheme_exact_svm.md) for full details.
+
### Communication Flow
The typical flow between a client and a server in the x402 protocol is as follows:
diff --git a/docs/core-concepts/facilitator.md b/docs/core-concepts/facilitator.md
index 854d85f54f..4c96cba36f 100644
--- a/docs/core-concepts/facilitator.md
+++ b/docs/core-concepts/facilitator.md
@@ -51,6 +51,16 @@ 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.
+### Duplicate Settlement (Solana)
+
+On Solana, a race condition can occur when the same payment transaction is submitted to a facilitator's `/settle` endpoint multiple times before the first submission is confirmed on-chain. Because Solana's RPC returns "success" for duplicate submissions (the network deduplicates at the consensus level), the facilitator may return a successful settlement response for each call. A malicious client could exploit this to access multiple resources while only paying once.
+
+To mitigate this, the x402 SVM mechanism packages include a built-in `SettlementCache` — a short-lived, in-memory cache that detects and rejects duplicate settlement attempts for the same transaction payload. The cache requires no external storage and entries are automatically evicted after 120 seconds (approximately twice the Solana blockhash lifetime).
+
+This protection is enabled by default when using the standard SVM facilitator registration helpers in TypeScript and Python. In Go, a shared `SettlementCache` instance should be passed to both V1 and V2 SVM facilitator schemes during registration.
+
+**If you are a merchant settling payments directly (without a facilitator), you must implement equivalent duplicate detection yourself.** See the [Exact SVM Scheme Specification](https://github.com/coinbase/x402/blob/main/specs/schemes/exact/scheme_exact_svm.md) for the full specification.
+
### Summary
The facilitator acts as an independent verification and settlement layer within the x402 protocol. It helps servers confirm payments and submit transactions onchain without requiring direct blockchain infrastructure.
diff --git a/docs/core-concepts/network-and-token-support.mdx b/docs/core-concepts/network-and-token-support.mdx
index da14a7d96e..ffab259f2a 100644
--- a/docs/core-concepts/network-and-token-support.mdx
+++ b/docs/core-concepts/network-and-token-support.mdx
@@ -17,6 +17,8 @@ x402 V2 uses [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) standard network i
| `solana-devnet` | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | - | Solana Devnet |
| - | `aptos:1` | 1 | Aptos Mainnet |
| - | `aptos:2` | 2 | Aptos Testnet |
+| - | `stellar:pubnet` | - | Stellar Mainnet |
+| - | `stellar:testnet` | - | Stellar Testnet |
| `avalanche` | `eip155:43114` | 43114 | Avalanche C-Chain mainnet |
| `avalanche-fuji` | `eip155:43113` | 43113 | Avalanche Fuji testnet |
| `polygon` | `eip155:137` | 137 | Polygon mainnet |
@@ -29,6 +31,7 @@ x402 V2 uses [CAIP-2](https://chainagnostic.org/CAIPs/caip-2) standard network i
### Format Explanation
- **EVM networks**: `eip155:` where chainId is the numeric chain identifier
- **Solana**: `solana:` where genesisHash is the first 32 bytes of the genesis block hash
+- **Stellar**: `stellar:` where network is `pubnet` (mainnet) or `testnet`
- **Aptos**: `aptos:` where chainId is the numeric chain identifier
## Overview
@@ -50,34 +53,46 @@ Multiple production-ready facilitators are available supporting various networks
### Token Support
-x402 supports tokens on EVM, Solana, and Aptos networks:
+x402 supports tokens on EVM, Solana, Stellar, and Aptos networks:
-* **EVM**: Any ERC-20 token that implements the EIP-3009 standard
+* **EVM**: Any ERC-20 token (via EIP-3009 or Permit2)
* **Solana**: Any SPL or token-2022 token
+* **Stellar**: Any Soroban token implementing SEP-41
* **Aptos**: Any fungible asset using Aptos's native fungible asset framework
-**Important**: Facilitators support networks, not specific tokens — any EIP-3009 compatible token works on EVM networks, any SPL/token-2022 token works on Solana, and any fungible asset works on Aptos, for the facilitators that support those networks.
+**Important**: Facilitators support networks, not specific tokens — any ERC-20 token works on EVM networks (via EIP-3009 or Permit2), any SPL/token-2022 token works on Solana, any SEP-41 token works on Stellar, and any fungible asset works on Aptos, for the facilitators that support those networks.
-#### EVM: EIP-3009 Requirement
+#### EVM: Asset Transfer Methods
-Tokens must implement the `transferWithAuthorization` function from the EIP-3009 standard. This enables:
+x402 supports two asset transfer methods on EVM, selected automatically based on token capabilities:
-* **Gasless transfers**: The facilitator sponsors gas fees
+| Method | When Used | How It Works |
+|--------|-----------|--------------|
+| **EIP-3009** | Tokens with `transferWithAuthorization` (e.g., USDC) | Single off-chain signature, simplest flow |
+| **Permit2** | Any ERC-20 token | Uses Uniswap's [Permit2](https://docs.uniswap.org/contracts/v4/deployments) contract + `x402ExactPermit2Proxy` |
+
+Both methods provide:
+
+* **Gasless transfers**: The facilitator sponsors gas for the payment itself. For Permit2, the one-time approval step can also be made gasless via optional [gas sponsoring extensions](/extensions/eip2612-gas-sponsoring)
* **Signature-based authorization**: Users sign transfer authorizations off-chain
* **Secure payments**: Transfers are authorized by cryptographic signatures
+EIP-3009 is preferred when available (simpler, no approval step). Permit2 serves as the universal fallback, enabling any ERC-20 token to work with x402.
+
+**Permit2 approval**: Permit2 requires a one-time on-chain approval of the Permit2 contract. x402 supports two gas sponsoring extensions that make this step gasless — see [EIP-2612 Gas Sponsoring](/extensions/eip2612-gas-sponsoring) and [ERC-20 Approval Gas Sponsoring](/extensions/erc20-approval-gas-sponsoring).
+
#### Specifying Payment Amounts
When configuring payment requirements, you have two options:
1. **Price String** (e.g., `"$0.01"`) - The system infers USDC as the token
-2. **TokenAmount** - Specify exact atomic units of any EIP-3009 token
+2. **TokenAmount** - Specify exact atomic units of any ERC-20 token
-#### Using Custom EIP-3009 Tokens
+#### Using Custom ERC-20 Tokens
-To use a custom EIP-3009 token, you need three key pieces of information:
+To use a custom ERC-20 token, you need three key pieces of information:
-1. **Token Address**: The contract address of your EIP-3009 token
+1. **Token Address**: The contract address of your ERC-20 token
2. **EIP-712 Name**: The token's name for EIP-712 signatures
3. **EIP-712 Version**: The token's version for EIP-712 signatures
@@ -103,23 +118,25 @@ These values are used in the `eip712` nested object when configuring TokenAmount
On Solana, x402 supports all SPL tokens and Token 2022 tokens. When using facilitators that support Solana or Solana Devnet, payments can be made in any SPL/token-2022 token, including USDC (SPL). No EIP-712 configuration is required on Solana.
+#### Stellar: Soroban Tokens
+
+On Stellar, x402 supports all Soroban tokens implementing [SEP-41](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0041.md). Payments use the `transfer(from, to, amount)` function. The TypeScript SDK supports sponsored transactions where facilitators pay gas fees on behalf of clients. Stellar uses ledger-based expiration (default ~12 ledgers ≈ 60 seconds) instead of timestamps.
+
#### Aptos: Fungible Assets
On Aptos, x402 supports all fungible assets using Aptos's native fungible asset framework. Payments use the `primary_fungible_store::transfer` function for automatic store creation and management. The TypeScript SDK supports sponsored transactions where facilitators pay gas fees on behalf of clients.
-#### USDC - The Default Token
+#### Default Tokens
-* **Status**: Supported by default across all networks
-* **Why**: USDC implements EIP-3009 and is widely available
-* **Networks**: Available on `eip155:8453` (Base), `eip155:84532` (Base Sepolia), and all supported networks
+Each network defines its own default token. When you use a price string (e.g., `"$0.01"`), the system uses the network's configured default. USDC is the default on most EVM networks because it implements EIP-3009 (simplest flow) and is widely available, but other networks may define different defaults.
-#### Why EIP-3009?
+#### Why EIP-3009 + Permit2?
-The EIP-3009 standard is essential for x402 because it enables:
+These two transfer methods together give x402 full ERC-20 coverage on EVM:
1. **Gas abstraction**: Buyers don't need native tokens (ETH, MATIC, etc.) for gas
-2. **One-step payments**: No separate approval transactions required
-3. **Universal facilitator support**: Any EIP-3009 token works with any facilitator
+2. **EIP-3009**: One-step payments with no approval needed — ideal for tokens like USDC
+3. **Permit2**: Universal fallback for any ERC-20 token, with optional gas-sponsored approval via [extensions](/extensions/eip2612-gas-sponsoring)
### Quick Reference
@@ -129,7 +146,7 @@ The EIP-3009 standard is essential for x402 because it enables:
| [Production Facilitators](https://www.x402.org/ecosystem?filter=facilitators) | Various (Base, Solana, Polygon, Avalanche, etc.) | Yes | Varies |
| Self-hosted | Any EVM network (CAIP-2 format) | Yes | Technical setup |
-**Note**: On EVM networks, facilitators support any EIP-3009 compatible token; on Solana, facilitators support any SPL/Token-2022 token.
+**Note**: On EVM networks, facilitators support any ERC-20 token (via EIP-3009 or Permit2); on Solana, facilitators support any SPL/Token-2022 token.
### Adding Support for New Networks
@@ -255,7 +272,7 @@ Key takeaways:
* Base (`eip155:8453`), Base Sepolia (`eip155:84532`), Solana (`solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp`), and Solana Devnet (`solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`) have the best out-of-the-box support
* Any EVM network can be supported with a custom facilitator using CAIP-2 format
-* Any EIP-3009 token (with `transferWithAuthorization`) works on any facilitator
+* Any ERC-20 token works on any facilitator (via EIP-3009 or Permit2)
* Use price strings for USDC or TokenAmount for custom tokens
* Network choice affects gas costs and payment economics
* V2 uses CAIP-2 network identifiers for unambiguous cross-chain support
diff --git a/docs/docs.json b/docs/docs.json
index ad1a0c407b..90d8e39cb0 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -65,9 +65,13 @@
{
"group": "Extensions",
"pages": [
+ "extensions/overview",
"extensions/bazaar",
+ "extensions/payment-identifier",
"extensions/sign-in-with-x",
- "extensions/payment-identifier"
+ "extensions/offer-receipt",
+ "extensions/eip2612-gas-sponsoring",
+ "extensions/erc20-approval-gas-sponsoring"
]
},
{
diff --git a/docs/extensions/bazaar.mdx b/docs/extensions/bazaar.mdx
index 5d4e9b5dce..e84dbf09bd 100644
--- a/docs/extensions/bazaar.mdx
+++ b/docs/extensions/bazaar.mdx
@@ -21,6 +21,43 @@ Facilitators that support the Bazaar extension may provide a `/discovery/resourc
**Note:** The spec for marketplace items is open and part of the x402 scheme, meaning any facilitator can implement their own discovery layer.
+#### Settlement Response Header
+
+After processing a payment that includes the `bazaar` extension, facilitators **may** return an `EXTENSION-RESPONSES` HTTP header to communicate extension-specific outcomes to the client.
+
+**Header name:** `EXTENSION-RESPONSES`
+
+**Header value:** A base64-encoded JSON object keyed by extension name. The `bazaar` key contains the bazaar extension's response:
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `bazaar.status` | string | Yes | One of `"success"`, `"processing"`, or `"rejected"` |
+| `bazaar.rejectedReason` | string | No | Human-readable explanation. Only present when `status` is `"rejected"` |
+
+**Status values:**
+
+| Value | Meaning |
+|-------|---------|
+| `"success"` | The discovery info was validated and successfully cataloged |
+| `"processing"` | The discovery info was accepted and is being cataloged asynchronously |
+| `"rejected"` | The discovery info was rejected (e.g., failed schema validation). See `rejectedReason` for details |
+
+**Example (success):**
+
+```
+EXTENSION-RESPONSES: eyJiYXphYXIiOnsic3RhdHVzIjoic3VjY2VzcyJ9fQ==
+```
+*(base64 of `{"bazaar":{"status":"success"}}`)*
+
+**Example (rejected):**
+
+```
+EXTENSION-RESPONSES: eyJiYXphYXIiOnsic3RhdHVzIjoicmVqZWN0ZWQiLCJyZWplY3RlZFJlYXNvbiI6ImluZm8gZmFpbGVkIHNjaGVtYSB2YWxpZGF0aW9uIn19
+```
+*(base64 of `{"bazaar":{"status":"rejected","rejectedReason":"info failed schema validation"}}`)*
+
+Clients that understand the `bazaar` extension should read the `bazaar` key of this header to confirm cataloging succeeded and surface any rejection reason for debugging.
+
#### Basic Flow
1. **Discovery**: Clients query the `/discovery/resources` endpoint to find available services
@@ -132,6 +169,55 @@ Fetch the list of available x402 services using the facilitator client:
);
```
+
+ ```go
+ package main
+
+ import (
+ "context"
+ "fmt"
+
+ "github.com/coinbase/x402/go/extensions/bazaar"
+ x402http "github.com/coinbase/x402/go/http"
+ )
+
+ func main() {
+ ctx := context.Background()
+
+ // Create facilitator client and extend with bazaar
+ facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: "https://x402.org/facilitator",
+ })
+ client := bazaar.WithBazaar(facilitatorClient)
+
+ // List discovery resources
+ response, err := client.ListDiscoveryResources(ctx, &bazaar.ListDiscoveryResourcesParams{
+ Type: "http",
+ Limit: 20,
+ Offset: 0,
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ // Filter services under $0.10
+ usdcAsset := "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
+ maxPrice := 100000 // $0.10 in USDC atomic units (6 decimals)
+
+ for _, item := range response.Items {
+ for _, paymentReq := range item.Accepts {
+ // Parse asset and maxAmountRequired from payment requirements
+ if asset, ok := paymentReq["asset"].(string); ok && asset == usdcAsset {
+ if maxAmount, ok := paymentReq["maxAmountRequired"].(string); ok {
+ // Convert and compare price
+ fmt.Printf("Found service: %s\n", item.Resource)
+ }
+ }
+ }
+ }
+ }
+ ```
+
```python
from x402.http import FacilitatorConfig, HTTPFacilitatorClient
@@ -249,6 +335,18 @@ Add the bazaar extension to your route configuration to make your API or MCP too
- **HTTP Endpoints** - Standard REST APIs (GET, POST, PUT, PATCH, DELETE, HEAD)
- **MCP Tools** - Model Context Protocol tools for AI agent integration
+#### Dynamic Routes
+
+The Bazaar extension supports parameterized routes (e.g., `/users/[userId]`, `/weather/[country]/[city]`). When you use route parameters:
+
+- The extension automatically extracts concrete parameter values into `pathParams` (e.g., `{ "userId": "123" }`)
+- A `routeTemplate` field is added using `:param` syntax (e.g., `/users/:userId`)
+- Facilitators use `routeTemplate` as the catalog key, consolidating all requests to the same route pattern into a single discovery entry
+
+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.
+
#### Adding Metadata
To enhance your listing with descriptions and schemas, include them when setting up your x402 middleware. **You should include descriptions for each parameter to make it clear for agents to call your endpoints**:
diff --git a/docs/extensions/eip2612-gas-sponsoring.mdx b/docs/extensions/eip2612-gas-sponsoring.mdx
new file mode 100644
index 0000000000..7d90ad8a9d
--- /dev/null
+++ b/docs/extensions/eip2612-gas-sponsoring.mdx
@@ -0,0 +1,142 @@
+---
+title: "EIP-2612 Gas Sponsoring"
+description: "Gasless Permit2 approval for ERC-20 tokens that implement EIP-2612. The facilitator submits the permit on-chain, so the client never pays gas for the approval step."
+---
+
+The EIP-2612 gas sponsoring extension enables gasless Permit2 approval for tokens that implement the [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) `permit()` function (e.g., USDC). The client signs an off-chain permit authorizing the Permit2 contract, and the facilitator submits it atomically during settlement via `x402ExactPermit2Proxy.settleWithPermit`.
+
+## Overview
+
+When using the Permit2 asset transfer method, the user must first approve the Permit2 contract to spend their tokens. For tokens that support EIP-2612, this approval can be done entirely off-chain:
+
+* **For Buyers**: No gas needed — sign a permit off-chain and the facilitator handles the rest
+* **For Sellers**: Advertise this extension so clients using EIP-2612 tokens get a seamless experience
+* **For Facilitators**: Accept the permit signature and call `settleWithPermit` to atomically approve + settle in one transaction
+
+## How It Works
+
+1. **Server** advertises `eip2612GasSponsoring` in the `PaymentRequired` response extensions
+2. **Client** checks if Permit2 allowance is insufficient; if so, signs an EIP-2612 permit off-chain
+3. **Client** includes the permit data in the `extensions.eip2612GasSponsoring.info` field of the payment payload
+4. **Facilitator** calls `x402ExactPermit2Proxy.settleWithPermit()`, which atomically submits the EIP-2612 permit and executes the Permit2 transfer in a single transaction
+
+## Server Usage
+
+Advertise support for this extension in your route configuration:
+
+
+
+ ```typescript
+ import { declareEip2612GasSponsoringExtension } from "@x402/extensions/eip2612-gas-sponsoring";
+
+ const routes = {
+ "GET /api/data": {
+ accepts: [{
+ scheme: "exact",
+ network: "eip155:84532",
+ price: "$0.01",
+ payTo: "0xYourAddress",
+ }],
+ extensions: {
+ ...declareEip2612GasSponsoringExtension(),
+ },
+ },
+ };
+ ```
+
+
+ ```go
+ import (
+ "github.com/coinbase/x402/go/extensions/eip2612gassponsor"
+ )
+
+ extensions := eip2612gassponsor.DeclareEip2612GasSponsoringExtension()
+ // Include in your route's Extensions field
+ ```
+
+
+ ```python
+ from x402.extensions.eip2612_gas_sponsoring import declare_eip2612_gas_sponsoring_extension
+
+ routes = {
+ "GET /api/data": {
+ "accepts": {
+ "scheme": "exact",
+ "network": "eip155:84532",
+ "price": "$0.01",
+ "payTo": "0xYourAddress",
+ },
+ "extensions": {
+ **declare_eip2612_gas_sponsoring_extension(),
+ },
+ },
+ }
+ ```
+
+
+
+## Client Usage
+
+The `ExactEvmScheme` handles EIP-2612 gas sponsoring automatically. When the server advertises this extension and the client's Permit2 allowance is insufficient, the scheme signs the EIP-2612 permit and includes it in the payment payload.
+
+
+
+ ```typescript
+ import { ExactEvmScheme } from "@x402/evm/exact/client";
+
+ // readContract capability is required for EIP-2612 (to check allowance and nonce)
+ const scheme = new ExactEvmScheme(signer);
+ client.register("eip155:*", scheme);
+
+ // The scheme automatically signs an EIP-2612 permit when:
+ // 1. The server advertises eip2612GasSponsoring
+ // 2. The asset transfer method is "permit2"
+ // 3. The client's Permit2 allowance is insufficient
+ ```
+
+
+ ```go
+ import (
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/client"
+ )
+
+ scheme := evm.NewExactEvmScheme(signer)
+ client.Register("eip155:*", scheme)
+
+ // EIP-2612 permit signing is handled automatically
+ // when the server advertises the extension
+ ```
+
+
+ ```python
+ from x402 import x402Client
+ from x402.mechanisms.evm import EthAccountSignerWithRPC
+ from x402.mechanisms.evm.exact import register_exact_evm_client
+
+ account = Account.from_key("0xYourPrivateKey")
+ signer = EthAccountSignerWithRPC(account, rpc_url="https://sepolia.base.org")
+
+ client = x402Client()
+ register_exact_evm_client(client, signer)
+ # Automatically signs EIP-2612 permit when needed
+ ```
+
+
+
+## When to Use
+
+Use this extension when:
+
+- Your payment token implements EIP-2612 (has a `permit()` function)
+- You want fully gasless Permit2 onboarding for your users
+- You're using the `permit2` asset transfer method
+
+Common EIP-2612 tokens include USDC, DAI, and many modern ERC-20 tokens.
+
+## SDK Support
+
+| SDK | Supported |
+|-----|-----------|
+| TypeScript | ✅ |
+| Go | ✅ |
+| Python | ✅ |
diff --git a/docs/extensions/erc20-approval-gas-sponsoring.mdx b/docs/extensions/erc20-approval-gas-sponsoring.mdx
new file mode 100644
index 0000000000..63ddbfadcd
--- /dev/null
+++ b/docs/extensions/erc20-approval-gas-sponsoring.mdx
@@ -0,0 +1,150 @@
+---
+title: "ERC-20 Approval Gas Sponsoring"
+description: "Gasless Permit2 approval for any ERC-20 token, including those without EIP-2612. The facilitator sponsors the gas for the approval transaction."
+---
+
+The ERC-20 approval gas sponsoring extension enables gasless Permit2 approval for **any ERC-20 token**, including those that do not implement EIP-2612. The client signs (but does not broadcast) a standard `approve(Permit2, MaxUint256)` transaction, and the facilitator broadcasts it atomically before settling the Permit2 payment.
+
+## Overview
+
+This is the universal fallback for gasless Permit2 onboarding. While [EIP-2612 gas sponsoring](/extensions/eip2612-gas-sponsoring) is preferred for tokens that support it, this extension works with every ERC-20 token:
+
+* **For Buyers**: No gas needed — sign an approval transaction off-chain and the facilitator broadcasts it
+* **For Sellers**: Advertise this extension to support the widest range of ERC-20 tokens
+* **For Facilitators**: Broadcast the pre-signed approval and settle in an atomic batch, optionally funding the client's gas if needed
+
+## How It Works
+
+1. **Server** advertises `erc20ApprovalGasSponsoring` in the `PaymentRequired` response extensions
+2. **Client** checks if Permit2 allowance is insufficient; if so, signs a raw `approve(Permit2, MaxUint256)` transaction without broadcasting it
+3. **Client** includes the signed transaction in `extensions.erc20ApprovalGasSponsoring.info.signedTransaction`
+4. **Facilitator** executes an atomic batch:
+ - Funds the client's wallet with gas (if needed)
+ - Broadcasts the client's signed approval transaction
+ - Calls `x402ExactPermit2Proxy.settle()` to complete the payment
+
+The atomic batch ensures the approval and settlement happen together — there is no window for front-running between the approval and the payment.
+
+## Server Usage
+
+Advertise support for this extension in your route configuration:
+
+
+
+ ```typescript
+ import { declareErc20ApprovalGasSponsoringExtension } from "@x402/extensions/erc20-approval-gas-sponsoring";
+
+ const routes = {
+ "GET /api/data": {
+ accepts: [{
+ scheme: "exact",
+ network: "eip155:84532",
+ price: "$0.01",
+ payTo: "0xYourAddress",
+ }],
+ extensions: {
+ ...declareErc20ApprovalGasSponsoringExtension(),
+ },
+ },
+ };
+ ```
+
+
+ ```go
+ import (
+ "github.com/coinbase/x402/go/extensions/erc20approvalgassponsor"
+ )
+
+ extensions := erc20approvalgassponsor.DeclareExtension()
+ // Include in your route's Extensions field
+ ```
+
+
+ ```python
+ from x402.extensions.erc20_approval_gas_sponsoring import (
+ declare_erc20_approval_gas_sponsoring_extension,
+ )
+
+ routes = {
+ "GET /api/data": {
+ "accepts": {
+ "scheme": "exact",
+ "network": "eip155:84532",
+ "price": "$0.01",
+ "payTo": "0xYourAddress",
+ },
+ "extensions": {
+ **declare_erc20_approval_gas_sponsoring_extension(),
+ },
+ },
+ }
+ ```
+
+
+
+## Client Usage
+
+The `ExactEvmScheme` handles ERC-20 approval gas sponsoring automatically as a fallback when EIP-2612 is not available.
+
+
+
+ ```typescript
+ import { ExactEvmScheme } from "@x402/evm/exact/client";
+
+ // signTransaction and getTransactionCount capabilities are required
+ const scheme = new ExactEvmScheme(signer);
+ client.register("eip155:*", scheme);
+
+ // The scheme automatically signs an ERC-20 approval when:
+ // 1. The server advertises erc20ApprovalGasSponsoring
+ // 2. The asset transfer method is "permit2"
+ // 3. The client's Permit2 allowance is insufficient
+ // 4. EIP-2612 gas sponsoring is not available or not supported by the token
+ ```
+
+
+ ```go
+ import (
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/client"
+ )
+
+ scheme := evm.NewExactEvmScheme(signer)
+ client.Register("eip155:*", scheme)
+
+ // ERC-20 approval gas sponsoring is handled automatically
+ // as a fallback when EIP-2612 is not available
+ ```
+
+
+ ```python
+ from x402 import x402Client
+ from x402.mechanisms.evm import EthAccountSignerWithRPC
+ from x402.mechanisms.evm.exact import register_exact_evm_client
+
+ account = Account.from_key("0xYourPrivateKey")
+ signer = EthAccountSignerWithRPC(account, rpc_url="https://sepolia.base.org")
+
+ client = x402Client()
+ register_exact_evm_client(client, signer)
+ # Automatically signs ERC-20 approval transaction when needed
+ ```
+
+
+
+## When to Use
+
+Use this extension when:
+
+- You want to support any ERC-20 token, not just those with EIP-2612
+- You're using the `permit2` asset transfer method
+- You want fully gasless onboarding for your users regardless of the token
+
+This extension is typically advertised alongside [EIP-2612 gas sponsoring](/extensions/eip2612-gas-sponsoring). The client automatically selects the best option: EIP-2612 if the token supports it, ERC-20 approval otherwise.
+
+## SDK Support
+
+| SDK | Supported |
+|-----|-----------|
+| TypeScript | ✅ |
+| Go | ✅ |
+| Python | ✅ |
diff --git a/docs/extensions/offer-receipt.mdx b/docs/extensions/offer-receipt.mdx
new file mode 100644
index 0000000000..b7d116e837
--- /dev/null
+++ b/docs/extensions/offer-receipt.mdx
@@ -0,0 +1,424 @@
+---
+title: "Signed Offers & Receipts"
+description: "Sign offers on 402 responses and receipts on 200 responses, producing cryptographic proof-of-interaction artifacts that clients can use for reputation, auditing, or dispute resolution."
+---
+
+The Offer & Receipt extension adds cryptographic proof-of-interaction to x402 payment flows. When enabled, your server automatically signs an **offer** on every `402` response (committing to payment terms) and a **receipt** on every `200` response (confirming service delivery). No changes to your business logic.
+
+## Why Enable Offer & Receipt Signing?
+
+Signed offers and receipts are portable, verifiable artifacts that any third party can check. They enable:
+
+* **Reputation systems** — Clients can attach receipts to on-chain attestations as proof they actually paid for and received a service. This is the "Verified Purchase" equivalent for the open web.
+* **Dispute resolution** — Offers prove the server committed to specific terms; receipts prove delivery. If either party disputes a transaction, the signed artifacts provide evidence.
+* **Auditing** — Receipts create a verifiable trail of service delivery without exposing transaction details (the transaction hash is optional).
+* **Client confidence** — Services with verifiable proof-of-interaction build stronger trust signals, making new clients more likely to use the service.
+
+## Prerequisites
+
+- An existing x402 resource server (or a new Express.js project)
+- Node.js 18+
+- A facilitator URL (see [Quickstart for Sellers](/getting-started/quickstart-for-sellers))
+
+## Installation
+
+```bash
+npm install @x402/express @x402/extensions @x402/evm @x402/core viem
+```
+
+## Signing Formats
+
+The extension supports two signature formats. Choose based on your key management setup:
+
+| Format | Key Type | Identity | Best For |
+|--------|----------|----------|----------|
+| **EIP-712** | secp256k1 (Ethereum) | `did:pkh` (address recovered from signature) | Wallet-based signing. Simpler setup, especially with managed wallet providers. |
+| **JWS** | Any asymmetric key (EC P-256, Ed25519, secp256k1) | `did:web` (resolved via `/.well-known/did.json`) | Server-side signing with KMS/HSM. Also supports Solana keys (Ed25519), so if your infrastructure is Solana-native, JWS may be the more natural fit. |
+
+Both formats produce equivalent proof artifacts. Clients and verifiers handle both transparently.
+
+## Quick Start: EIP-712 with Environment Variables
+
+This example uses EIP-712 signing with a raw private key from an environment variable. This is the simplest way to get started.
+
+> **Not for Production:** Storing private keys in environment variables is acceptable for local development and testing. For production deployments, use a key management service (KMS), hardware security module (HSM), or a managed wallet provider. See [Production Key Management](#production-key-management) below.
+
+> **Signing Key ≠ Payment Address:** The signing key used for offers and receipts should be a dedicated signing key, not the wallet that receives payments (`payTo`). Separating signing from payment receipt limits exposure if the signing key is compromised.
+
+### Environment Variables
+
+Create a `.env` file:
+
+```bash
+# Wallet address that receives payments
+EVM_ADDRESS=0xYourPaymentWalletAddress
+
+# Private key for signing offers and receipts (EIP-712)
+# This should be a DEDICATED SIGNING KEY, not the payment wallet's key
+# For production deployments, do not store private keys in an environment variable
+SIGNING_PRIVATE_KEY=0xYourDedicatedSigningPrivateKey
+
+# x402 facilitator URL
+FACILITATOR_URL=https://facilitator.x402.org
+```
+
+### Server Setup (EIP-712)
+
+```typescript
+import { config } from "dotenv";
+import express from "express";
+import { paymentMiddleware, x402ResourceServer } from "@x402/express";
+import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { HTTPFacilitatorClient } from "@x402/core/server";
+import {
+ createOfferReceiptExtension,
+ createEIP712OfferReceiptIssuer,
+ declareOfferReceiptExtension,
+} from "@x402/extensions/offer-receipt";
+import { privateKeyToAccount } from "viem/accounts";
+
+config();
+
+const evmAddress = process.env.EVM_ADDRESS as `0x${string}`;
+const signingPrivateKey = process.env.SIGNING_PRIVATE_KEY as `0x${string}`; // not for production
+const facilitatorUrl = process.env.FACILITATOR_URL!;
+
+// Create EIP-712 signer from the dedicated signing key
+const signingAccount = privateKeyToAccount(signingPrivateKey);
+const kid = `did:pkh:eip155:1:${signingAccount.address}#key-1`;
+
+const offerReceiptIssuer = createEIP712OfferReceiptIssuer(
+ kid,
+ signingAccount.signTypedData.bind(signingAccount),
+);
+
+// Set up the resource server with the extension
+const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+const resourceServer = new x402ResourceServer(facilitatorClient)
+ .register("eip155:84532", new ExactEvmScheme())
+ .registerExtension(createOfferReceiptExtension(offerReceiptIssuer));
+
+const app = express();
+
+// Configure payment routes with offer-receipt enabled
+app.use(
+ paymentMiddleware(
+ {
+ "GET /api/data": {
+ accepts: [
+ {
+ scheme: "exact",
+ price: "$0.001",
+ network: "eip155:84532",
+ payTo: evmAddress, // Payment goes here (different from signing key)
+ },
+ ],
+ description: "Premium data endpoint",
+ mimeType: "application/json",
+ extensions: {
+ ...declareOfferReceiptExtension({ includeTxHash: false }),
+ },
+ },
+ },
+ resourceServer,
+ ),
+);
+
+// Your business logic — unchanged
+app.get("/api/data", (req, res) => {
+ res.json({ data: "your premium content" });
+});
+
+app.listen(4021, () => {
+ console.log("Server listening on http://localhost:4021");
+ console.log("Offer-receipt extension enabled (EIP-712)");
+});
+```
+
+### What Happens Automatically
+
+Once configured, the extension hooks into the x402 payment flow:
+
+1. **On `402` responses**: The extension signs an offer for each entry in `accepts[]` and includes them in the response's `extensions` field. Each offer contains the payment terms (`scheme`, `network`, `amount`, `payTo`) and a `validUntil` timestamp.
+
+2. **On `200` responses** (after successful payment): The extension signs a receipt containing the `resourceUrl`, `payer` address, `network`, and `issuedAt` timestamp. The receipt is included in the `PAYMENT-RESPONSE` header's `extensions` field.
+
+No changes to your route handlers are needed. The extension is composable middleware.
+
+## Alternative: JWS Signing with `did:web`
+
+JWS signing uses a `did:web` identifier, which means your server must host a DID document at `/.well-known/did.json`. Clients and verifiers resolve this document to find your public key so they can verify the signature.
+
+JWS supports a wider range of key types than EIP-712 (secp256k1 only), including secp256r1 (EC P-256), Ed25519, and secp256k1 (ES256K). If your infrastructure is enterprise-oriented or Solana-native (Ed25519), JWS lets you use your existing key infrastructure.
+
+### Environment Variables
+
+```bash
+EVM_ADDRESS=0xYourPaymentWalletAddress
+FACILITATOR_URL=https://facilitator.x402.org
+
+# Base64-encoded PKCS#8 private key (EC P-256)
+# For production deployments, do not store private keys in an environment variable
+SIGNING_PRIVATE_KEY=base64EncodedPrivateKey
+
+# Your server's domain (URL-encoded for did:web)
+# e.g., "api.example.com" or "localhost%3A4021" for local dev
+SERVER_DOMAIN=api.example.com
+```
+
+### Server Setup (JWS)
+
+```typescript
+import * as crypto from "crypto";
+import {
+ createOfferReceiptExtension,
+ createJWSOfferReceiptIssuer,
+ declareOfferReceiptExtension,
+ type JWSSigner,
+} from "@x402/extensions/offer-receipt";
+
+const serverDomain = process.env.SERVER_DOMAIN!;
+const signingPrivateKey = process.env.SIGNING_PRIVATE_KEY!; // not for production
+
+const did = `did:web:${serverDomain}`;
+const kid = `${did}#key-1`;
+
+// Create JWS signer from PKCS#8 private key
+const privateKeyPem = `-----BEGIN PRIVATE KEY-----\n${signingPrivateKey}\n-----END PRIVATE KEY-----`;
+const keyObject = crypto.createPrivateKey(privateKeyPem);
+const publicKeyJwk = keyObject.export({ format: "jwk" });
+delete (publicKeyJwk as Record).d; // Remove private component
+
+const jwsSigner: JWSSigner = {
+ kid,
+ format: "jws",
+ algorithm: "ES256",
+ async sign(payload: Uint8Array): Promise {
+ const sign = crypto.createSign("SHA256");
+ sign.update(payload);
+ const signature = sign.sign(privateKeyPem);
+ return Buffer.from(derToRaw(signature)).toString("base64url");
+ },
+};
+
+const offerReceiptIssuer = createJWSOfferReceiptIssuer(kid, jwsSigner);
+
+// Register with x402ResourceServer the same way as the EIP-712 example:
+// resourceServer.registerExtension(createOfferReceiptExtension(offerReceiptIssuer));
+```
+
+### Hosting the DID Document
+
+For JWS verification, clients resolve your `did:web` to find the public key. Serve the DID document at `/.well-known/did.json`:
+
+```typescript
+app.get("/.well-known/did.json", (req, res) => {
+ res.setHeader("Content-Type", "application/did+json");
+ res.json({
+ "@context": [
+ "https://www.w3.org/ns/did/v1",
+ "https://w3id.org/security/suites/jws-2020/v1",
+ ],
+ id: did,
+ verificationMethod: [
+ {
+ id: kid,
+ type: "JsonWebKey2020",
+ controller: did,
+ publicKeyJwk,
+ },
+ ],
+ assertionMethod: [kid],
+ });
+});
+```
+
+## Configuration
+
+The `declareOfferReceiptExtension` function accepts an optional configuration object:
+
+```typescript
+declareOfferReceiptExtension({
+ // Include the blockchain transaction hash in receipts.
+ // Default: false (for privacy — the payer address is still included).
+ // Set to true if verifiability is more important than privacy.
+ includeTxHash: false,
+
+ // How long offers remain valid, in seconds.
+ // Default: 300 (5 minutes). Falls back to the route's maxTimeoutSeconds.
+ offerValiditySeconds: 300,
+});
+```
+
+Configuration is per-route — different endpoints can have different settings.
+
+
+## What Gets Signed
+
+### Offer Payload
+
+Each offer is signed when the server returns a `402 Payment Required` response:
+
+| Field | Description |
+|-------|-------------|
+| `resourceUrl` | The URL the client is requesting |
+| `offerType` | The payment scheme (e.g., `exact`) |
+| `network` | The blockchain network (e.g., `eip155:84532`) |
+| `amount` | The payment amount in the token's smallest unit |
+| `payTo` | The server's payment address |
+| `validUntil` | Unix timestamp after which the offer expires |
+
+### Receipt Payload
+
+Each receipt is signed when the server returns a `200` response after successful payment:
+
+| Field | Description |
+|-------|-------------|
+| `resourceUrl` | The URL the client requested |
+| `payer` | The client's wallet address (from the payment) |
+| `network` | The blockchain network used for payment |
+| `issuedAt` | Unix timestamp when the receipt was issued |
+| `txHash` | *(optional)* The blockchain transaction hash, included only if `includeTxHash: true` |
+
+Both payloads are signed using the format configured on the server (EIP-712 or JWS). The signed artifacts are self-contained — a verifier only needs the artifact and the signer's public key to verify.
+
+## Production Key Management
+
+> The examples above use environment variables for signing keys. This is fine for development but not for production. Private keys in environment variables can leak through process inspection, logging, crash dumps, and container metadata endpoints.
+
+For production, use a signing backend that keeps keys in secure hardware or managed infrastructure. The extension's signer interface is pluggable — you only need to implement the `sign()` function (for JWS) or `signTypedData()` function (for EIP-712) using your provider's SDK. The `OfferReceiptIssuer` interface handles the rest.
+
+When using a managed wallet provider, you won't have access to the raw private key. Instead, you call the provider's signing API. Here's what the EIP-712 setup looks like with a server wallet (conceptual example):
+
+```typescript
+import {
+ createOfferReceiptExtension,
+ createEIP712OfferReceiptIssuer,
+} from "@x402/extensions/offer-receipt";
+
+// The provider's SDK gives you a signTypedData function
+// that calls their API — the private key never leaves their infrastructure
+const signerAddress = "0xYourServerWalletAddress";
+const kid = `did:pkh:eip155:1:${signerAddress}#key-1`;
+
+const offerReceiptIssuer = createEIP712OfferReceiptIssuer(kid, async (params) => {
+ // Call your wallet provider's signing API
+ return await yourWalletProvider.signTypedData({
+ domain: params.domain,
+ types: params.types,
+ primaryType: params.primaryType,
+ message: params.message,
+ });
+});
+
+// Register as usual
+resourceServer.registerExtension(createOfferReceiptExtension(offerReceiptIssuer));
+```
+
+The key difference from the environment variable example: you never construct a `privateKeyToAccount` — instead, you pass a function that delegates signing to the provider's API. Any managed wallet provider that supports `signTypedData` (for EIP-712) or raw signing (for JWS) works as a drop-in replacement.
+
+## Binding Your Signing Key to Your Service Identity
+
+Signing offers and receipts is only half the story. For verifiers to trust that your signatures are legitimate, they need to confirm that your signing key is authorized to act on behalf of your service's identity (`did:web:yourdomain.com`).
+
+### DID Document (`did.json`)
+
+If you're using JWS signing, you're already hosting a DID document at `/.well-known/did.json` (see [JWS setup above](#hosting-the-did-document)). This document declares which keys are authorized for your `did:web` identity. Verifiers resolve your DID and check that the signing key is listed in `verificationMethod`.
+
+If you're using EIP-712 signing, you can host a `did.json` as well — list your EIP-712 signing address as a `verificationMethod` so verifiers can confirm the key is authorized for your domain.
+
+This is a W3C standard mechanism and is sufficient for many use cases. However, the DID document is mutable — if you remove the key later, verifiers checking at that point won't find it.
+
+### Additional Binding Mechanisms
+
+For production services that need stronger guarantees — immutable on-chain attestations, DNS-based verification, temporal anchoring, or key lifecycle management (expiration, rotation, revocation) — ecosystem partners offer additional trust layers on top of `did.json`. See the [Infrastructure & Tooling category](https://www.x402.org/ecosystem?filter=ecosystem-infrastructure) on the ecosystem page for reputation and identity services that integrate with the offer-receipt extension.
+
+## Client-Side: Extracting Offers and Receipts
+
+The `@x402/extensions` package provides client utilities for extracting and verifying the signed artifacts your server produces.
+
+### Extract Offers from a `402` Response
+
+```typescript
+import {
+ extractOffersFromPaymentRequired,
+ decodeSignedOffers,
+ verifyOfferSignatureJWS,
+ verifyOfferSignatureEIP712,
+ isJWSSignedOffer,
+} from "@x402/extensions/offer-receipt";
+
+// After receiving a 402 response:
+const paymentRequiredBody = await response.json();
+const signedOffers = extractOffersFromPaymentRequired(paymentRequiredBody);
+const decodedOffers = decodeSignedOffers(signedOffers);
+
+// Verify an offer signature
+for (const decoded of decodedOffers) {
+ if (isJWSSignedOffer(decoded.signedOffer)) {
+ await verifyOfferSignatureJWS(decoded.signedOffer);
+ } else {
+ await verifyOfferSignatureEIP712(decoded.signedOffer);
+ }
+}
+```
+
+### Extract a Receipt from a `200` Response
+
+```typescript
+import {
+ extractReceiptFromResponse,
+ verifyReceiptMatchesOffer,
+ verifyReceiptSignatureJWS,
+ verifyReceiptSignatureEIP712,
+ isJWSSignedReceipt,
+} from "@x402/extensions/offer-receipt";
+
+// After a successful payment response:
+const signedReceipt = extractReceiptFromResponse(paidResponse);
+
+// Verify the receipt signature
+if (isJWSSignedReceipt(signedReceipt)) {
+ await verifyReceiptSignatureJWS(signedReceipt);
+} else {
+ await verifyReceiptSignatureEIP712(signedReceipt);
+}
+
+// Verify the receipt matches the offer you accepted
+const verified = verifyReceiptMatchesOffer(
+ signedReceipt,
+ selectedOffer,
+ [yourWalletAddress],
+);
+```
+
+`verifyReceiptMatchesOffer` checks that:
+- `resourceUrl` matches the offer
+- `network` matches the offer
+- `payer` matches one of your wallet addresses
+- `issuedAt` is recent (within 1 hour by default)
+
+### What Can Clients Do with These Artifacts?
+
+Signed offers and receipts are portable, verifiable artifacts. Clients can:
+
+- **Attach them to reputation attestations** as proof-of-interaction (e.g., "Verified Purchase" reviews)
+- **Store them for auditing** — receipts create a verifiable trail of service delivery
+- **Use them in dispute resolution** — offers prove the server committed to terms; receipts prove delivery
+- **Share them with aggregators** — trust scoring engines can verify the signatures independently
+
+Ecosystem partners (see the [Infrastructure & Tooling category](https://www.x402.org/ecosystem?filter=ecosystem-infrastructure) on the ecosystem page) build on these artifacts to provide reputation systems, trust scoring, and other value-added services.
+
+## Working Examples
+
+Complete working examples are available in the x402 repository:
+
+- [Server Example (Express.js)](https://github.com/coinbase/x402/tree/main/examples/typescript/servers/offer-receipt) — Resource server with offer-receipt enabled, showing both EIP-712 and JWS configurations
+- [Client Example](https://github.com/coinbase/x402/tree/main/examples/typescript/clients/offer-receipt) — Complete client flow: offer extraction, payment, receipt capture, and verification
+
+## Further Reading
+
+- [Extensions Overview](./overview) — How the x402 extension system works
+- [Offer & Receipt Extension Specification](https://github.com/coinbase/x402/blob/main/specs/extensions/extension-offer-and-receipt.md) — Full protocol spec with payload schemas, EIP-712 types, verification rules, and wire format examples
+- [@x402/extensions package](https://github.com/coinbase/x402/tree/main/typescript/packages/extensions/src/offer-receipt) — TypeScript implementation source
+- [SDK Features](/sdk-features) — Extension support across TypeScript, Go, and Python
diff --git a/docs/extensions/overview.mdx b/docs/extensions/overview.mdx
new file mode 100644
index 0000000000..a6f7ea9454
--- /dev/null
+++ b/docs/extensions/overview.mdx
@@ -0,0 +1,160 @@
+---
+title: "Extensions Overview"
+description: "x402 extensions are composable, optional capabilities that plug into the payment lifecycle. They enrich 402 responses, settlement responses, or both — without changing your business logic."
+---
+
+Extensions are the composable layer on top of x402's core payment protocol. They let resource servers, facilitators, and clients add optional capabilities — discovery, authentication, receipts, gas sponsoring — without modifying the core payment flow.
+
+## How Extensions Work
+
+x402 has two extension points that serve different roles in the payment flow:
+
+### Resource Server Extensions
+
+These run on the resource server (the service accepting payments) and hook into the HTTP payment lifecycle. A `ResourceServerExtension` can intervene at three points:
+
+1. **Declaration** (`enrichDeclaration`) — Called at route registration time. The extension can modify or narrow the route's extension declaration based on transport context (e.g., Bazaar narrows the HTTP method).
+2. **402 Response** (`enrichPaymentRequiredResponse`) — Called when the server returns `402 Payment Required`. The extension can add data to the response (e.g., signed offers, discovery metadata).
+3. **Settlement Response** (`enrichSettlementResponse`) — Called after successful payment. The extension can add data to the `PAYMENT-RESPONSE` header (e.g., signed receipts, payment identifiers).
+
+All three hooks are optional. Most extensions use one or two — not all three.
+
+### Facilitator Extensions
+
+These run on the facilitator (the service that verifies and settles payments on behalf of the resource server). A `FacilitatorExtension` provides a `key` and is stored for use by mechanism implementations during verification and settlement. Gas sponsoring extensions are the primary example — they inject batch signing capabilities into the settlement flow so the facilitator can sponsor gas on behalf of the payer.
+
+## Registering an Extension (Server)
+
+Extensions implement the `ResourceServerExtension` interface and are registered via `registerExtension`:
+
+```typescript
+import { x402ResourceServer } from "@x402/express";
+
+const resourceServer = new x402ResourceServer(facilitatorClient)
+ .register("eip155:84532", new ExactEvmScheme())
+ .registerExtension(myExtension) // Add one extension
+ .registerExtension(anotherExtension); // Stack another
+```
+
+Each extension has a unique `key` that identifies it in route declarations and response payloads.
+
+## The ResourceServerExtension Interface
+
+```typescript
+interface ResourceServerExtension {
+ /** Unique identifier for this extension */
+ key: string;
+
+ /** Enrich the extension declaration at route registration time */
+ enrichDeclaration?: (declaration: unknown, transportContext: unknown) => unknown;
+
+ /** Add data to the 402 PaymentRequired response */
+ enrichPaymentRequiredResponse?: (
+ declaration: unknown,
+ context: PaymentRequiredContext,
+ ) => Promise;
+
+ /** Add data to the settlement response after successful payment */
+ enrichSettlementResponse?: (
+ declaration: unknown,
+ context: SettleResultContext,
+ ) => Promise;
+}
+```
+
+## Declaring Extensions on Routes
+
+Extensions are declared per-route in the payment middleware configuration. Each extension's declaration goes under `extensions` keyed by the extension's `key`:
+
+```typescript
+app.use(
+ paymentMiddleware(
+ {
+ "GET /api/data": {
+ accepts: [{ scheme: "exact", price: "$0.01", network: "eip155:84532", payTo: address }],
+ description: "Premium data",
+ mimeType: "application/json",
+ extensions: {
+ // Each key matches a registered extension
+ "offer-receipt": { includeTxHash: false },
+ "bazaar": { /* bazaar config */ },
+ },
+ },
+ },
+ resourceServer,
+ ),
+);
+```
+
+If an extension is declared on a route but not registered on the server, it is silently ignored.
+
+## Which Hooks Do Extensions Use?
+
+Not all extensions use the same hooks. Here's how the built-in extensions map to the extension points:
+
+| Extension | `enrichDeclaration` | `enrichPaymentRequiredResponse` | `enrichSettlementResponse` | Facilitator |
+|-----------|:---:|:---:|:---:|:---:|
+| Bazaar | ✅ (narrows HTTP method) | — | — | ✅ (discovery cataloging) |
+| EIP-2612 Gas Sponsoring | — | — | — | ✅ (batch signing) |
+| ERC-20 Approval Gas Sponsoring | — | — | — | ✅ (batch signing) |
+| Payment Identifier | — | ✅ | ✅ | — |
+| Sign-In-With-X | — | — | — | — |
+| Signed Offers & Receipts | — | ✅ (signs offers) | ✅ (signs receipts) | — |
+
+Bazaar is unique in that it spans both sides: the resource server extension enriches declarations, while the facilitator component handles discovery cataloging and validation. Sign-In-With-X manages its own session lifecycle outside the standard hooks.
+
+## Available Extensions
+
+
+| Extension | Type | Description | SDK Support |
+|-----------|------|-------------|-------------|
+| [Bazaar](./bazaar) | Server + Facilitator | Discovery layer for x402 endpoints and MCP tools. Makes your services findable by AI agents and developers. | TypeScript, Go, Python |
+| [EIP-2612 Gas Sponsoring](./eip2612-gas-sponsoring) | Facilitator | Sponsors gas for EIP-2612 permit-based token transfers. | TypeScript, Go, Python |
+| [ERC-20 Approval Gas Sponsoring](./erc20-approval-gas-sponsoring) | Facilitator | Sponsors gas for ERC-20 approval-based token transfers. | TypeScript, Go, Python |
+| [Payment Identifier](./payment-identifier) | Server + Client | Attaches a unique identifier to each payment for tracking, reconciliation, and idempotency. | TypeScript, Go, Python |
+| [Sign-In-With-X](./sign-in-with-x) | Server + Client | CAIP-122 wallet authentication. Lets clients prove wallet ownership to access previously purchased content without repaying. | TypeScript |
+| [Signed Offers & Receipts](./offer-receipt) | Server + Client | Signs offers on 402 responses and receipts on 200 responses, producing cryptographic proof-of-interaction artifacts. | TypeScript |
+
+## Building a Custom Extension
+
+To create your own extension:
+
+1. **Define the extension object** implementing `ResourceServerExtension`
+2. **Choose a unique key** — this identifies your extension in route declarations and response payloads
+3. **Implement the hooks you need** — `enrichDeclaration`, `enrichPaymentRequiredResponse`, `enrichSettlementResponse`
+4. **Create a declare function** — a helper that returns the route-level configuration for your extension
+5. **Register it** on the `x402ResourceServer` via `registerExtension`
+6. **Submit a pull request** to [coinbase/x402](https://github.com/coinbase/x402) — extensions must be reviewed and approved by the x402 maintainers before they are included in the SDK
+
+Here's a minimal example:
+
+```typescript
+import type { ResourceServerExtension } from "@x402/core";
+
+const myExtension: ResourceServerExtension = {
+ key: "my-extension",
+
+ enrichPaymentRequiredResponse: async (declaration, context) => {
+ // Add custom data to the 402 response
+ return { customField: "value", timestamp: Date.now() };
+ },
+
+ enrichSettlementResponse: async (declaration, context) => {
+ // Add custom data after successful payment
+ return { settled: true, processedAt: Date.now() };
+ },
+};
+
+// Declare helper for route config
+function declareMyExtension(config: { customOption: boolean }) {
+ return { "my-extension": config };
+}
+```
+
+The data returned from each hook is included in the response under `extensions["my-extension"]`.
+
+## Further Reading
+
+- [x402 SDK Features](/sdk-features) — Extension support across TypeScript, Go, and Python
+- [Extension Specs](https://github.com/coinbase/x402/tree/main/specs/extensions) — Protocol-level extension specifications
+- [@x402/extensions package](https://github.com/coinbase/x402/tree/main/typescript/packages/extensions) — TypeScript implementation source
diff --git a/docs/extensions/payment-identifier.mdx b/docs/extensions/payment-identifier.mdx
index 3d72890086..e5ea85f00e 100644
--- a/docs/extensions/payment-identifier.mdx
+++ b/docs/extensions/payment-identifier.mdx
@@ -33,6 +33,10 @@ import { generatePaymentId } from "@x402/extensions/payment-identifier";
const paymentId = generatePaymentId();
// Example: "pay_7d5d747be160e280504c099d984bcfe0"
+
+// Custom prefix
+const orderId = generatePaymentId("order_");
+// Example: "order_7d5d747be160e280504c099d984bcfe0"
```
### Step 2: Add Payment ID to Extensions
@@ -41,7 +45,10 @@ Hook into the payment flow to add the payment ID before payload creation:
```typescript
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
-import { appendPaymentIdentifierToExtensions } from "@x402/extensions/payment-identifier";
+import {
+ appendPaymentIdentifierToExtensions,
+ generatePaymentId,
+} from "@x402/extensions/payment-identifier";
const client = new x402Client();
// ... register schemes ...
@@ -51,10 +58,10 @@ const paymentId = generatePaymentId();
// Hook into payment flow to add the payment ID
client.onBeforePaymentCreation(async ({ paymentRequired }) => {
- if (!paymentRequired.extensions) {
- paymentRequired.extensions = {};
+ if (paymentRequired.extensions) {
+ // Only appends if server declared the extension
+ appendPaymentIdentifierToExtensions(paymentRequired.extensions, paymentId);
}
- appendPaymentIdentifierToExtensions(paymentRequired.extensions, paymentId);
});
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
@@ -78,6 +85,10 @@ from x402.extensions.payment_identifier import generate_payment_id
payment_id = generate_payment_id()
# Example: "pay_7d5d747be160e280504c099d984bcfe0"
+
+# Custom prefix
+order_id = generate_payment_id("order_")
+# Example: "order_7d5d747be160e280504c099d984bcfe0"
```
### Step 2: Add Payment ID to Extensions
@@ -86,8 +97,12 @@ Hook into the payment flow to add the payment ID before payload creation:
```python
from x402 import x402Client
-from x402.extensions.payment_identifier import append_payment_identifier_to_extensions
+from x402.extensions.payment_identifier import (
+ append_payment_identifier_to_extensions,
+ generate_payment_id,
+)
from x402.http.clients import x402HttpxClient
+from x402.schemas import PaymentCreationContext
client = x402Client()
# ... register schemes ...
@@ -96,9 +111,10 @@ client = x402Client()
payment_id = generate_payment_id()
# Hook into payment flow to add the payment ID
-async def before_payment_creation(context):
+async def before_payment_creation(context: PaymentCreationContext) -> None:
extensions = context.payment_required.extensions
if extensions is not None:
+ # Only appends if server declared the extension
append_payment_identifier_to_extensions(extensions, payment_id)
client.on_before_payment_creation(before_payment_creation)
@@ -106,19 +122,81 @@ client.on_before_payment_creation(before_payment_creation)
async with x402HttpxClient(client) as http:
# First request - payment is processed
response1 = await http.get(url)
-
+
# Retry with same payment ID - cached response returned (no payment)
response2 = await http.get(url)
```
+
+
+
+### Step 1: Generate a Payment ID
+
+Use the `GeneratePaymentID()` utility to create a unique identifier:
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+// Generate with default prefix "pay_"
+paymentID := paymentidentifier.GeneratePaymentID("")
+// Example: "pay_7d5d747be160e280504c099d984bcfe0"
+
+// Generate with custom prefix
+paymentID = paymentidentifier.GeneratePaymentID("order_")
+// Example: "order_7d5d747be160e280504c099d984bcfe0"
+```
+
+### Step 2: Add Payment ID to Extensions
+
+Hook into the payment flow to add the payment ID before payload creation:
+
+```go
+import (
+ x402 "github.com/coinbase/x402/go"
+ "github.com/coinbase/x402/go/extensions/paymentidentifier"
+)
+
+client := x402.Newx402Client()
+// ... register schemes ...
+
+// Generate a unique payment ID for this logical request
+paymentID := paymentidentifier.GeneratePaymentID("")
+
+// Hook into payment flow to add the payment ID
+client.OnBeforePaymentCreation(func(ctx x402.PaymentCreationContext) (*x402.BeforePaymentCreationHookResult, error) {
+ if ctx.Extensions == nil {
+ return nil, nil
+ }
+
+ // Only add if server declared the extension
+ if ctx.Extensions[paymentidentifier.PAYMENT_IDENTIFIER] == nil {
+ return nil, nil
+ }
+
+ err := paymentidentifier.AppendPaymentIdentifierToExtensions(ctx.Extensions, paymentID)
+ if err != nil {
+ return nil, err
+ }
+
+ return nil, nil
+})
+
+// First request - payment is processed
+response1, err := client.MakeRequest(url)
+
+// Retry with same payment ID - cached response returned (no payment)
+response2, err := client.MakeRequest(url)
+```
+
+
### Best Practices
1. **Generate payment IDs at the logical request level**, not per retry
2. **Persist payment IDs** for long-running operations so they survive restarts
-3. **Use descriptive prefixes** (e.g., `order_`, `sub_`) to identify payment types
+3. **Use descriptive prefixes** (e.g., `generatePaymentId("order_")`) to identify payment types
4. **Don't reuse payment IDs** across different logical requests
## Quickstart for Sellers (Servers)
@@ -131,17 +209,26 @@ async with x402HttpxClient(client) as http:
Declare the payment-identifier extension in your route configuration:
```typescript
-import { paymentMiddlewareFromHTTPServer, x402ResourceServer, x402HTTPResourceServer } from "@x402/express";
-import { declarePaymentIdentifierExtension, PAYMENT_IDENTIFIER } from "@x402/extensions/payment-identifier";
+import {
+ paymentMiddlewareFromHTTPServer,
+ x402ResourceServer,
+ x402HTTPResourceServer,
+} from "@x402/express";
+import {
+ declarePaymentIdentifierExtension,
+ PAYMENT_IDENTIFIER,
+} from "@x402/extensions/payment-identifier";
const routes = {
"GET /weather": {
- accepts: {
- scheme: "exact",
- price: "$0.001",
- network: "eip155:84532",
- payTo: address
- },
+ accepts: [
+ {
+ scheme: "exact",
+ price: "$0.001",
+ network: "eip155:84532",
+ payTo: address,
+ },
+ ],
extensions: {
[PAYMENT_IDENTIFIER]: declarePaymentIdentifierExtension(false), // optional
},
@@ -159,9 +246,9 @@ declarePaymentIdentifierExtension(false)
declarePaymentIdentifierExtension(true)
```
-### Step 2: Implement Idempotency Cache
+### Step 2: Cache Responses After Settlement
-Use the `extractPaymentIdentifier()` utility to check for cached responses:
+Store responses after successful payment settlement:
```typescript
import { extractPaymentIdentifier } from "@x402/extensions/payment-identifier";
@@ -170,35 +257,42 @@ import { extractPaymentIdentifier } from "@x402/extensions/payment-identifier";
const idempotencyCache = new Map();
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
-const httpServer = new x402HTTPResourceServer(resourceServer, routes)
- .onProtectedRequest(async (context) => {
- // Check if payment ID is in cache
- const paymentPayload = JSON.parse(Buffer.from(context.paymentHeader, "base64").toString());
+const resourceServer = new x402ResourceServer(facilitatorClient)
+ .register("eip155:84532", new ExactEvmScheme())
+ .onAfterSettle(async ({ paymentPayload }) => {
const paymentId = extractPaymentIdentifier(paymentPayload);
-
if (paymentId) {
- const cached = idempotencyCache.get(paymentId);
- if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
- return { grantAccess: true }; // Skip payment, grant access
- }
+ idempotencyCache.set(paymentId, {
+ timestamp: Date.now(),
+ response: { /* your response data */ },
+ });
}
});
```
-### Step 3: Cache Responses After Settlement
+### Step 3: Check Cache Before Payment
-Store responses after successful payment settlement:
+Use the `onProtectedRequest` hook to return cached responses and skip payment processing:
```typescript
-const resourceServer = new x402ResourceServer(facilitatorClient)
- .register("eip155:84532", new ExactEvmScheme())
- .onAfterSettle(async ({ paymentPayload }) => {
- const paymentId = extractPaymentIdentifier(paymentPayload);
- if (paymentId) {
- idempotencyCache.set(paymentId, {
- timestamp: Date.now(),
- response: { /* your response data */ }
- });
+const httpServer = new x402HTTPResourceServer(resourceServer, routes)
+ .onProtectedRequest(async (context) => {
+ if (!context.paymentHeader) return;
+
+ try {
+ const paymentPayload = JSON.parse(
+ Buffer.from(context.paymentHeader, "base64").toString("utf-8"),
+ );
+ const paymentId = extractPaymentIdentifier(paymentPayload);
+
+ if (paymentId) {
+ const cached = idempotencyCache.get(paymentId);
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
+ return { grantAccess: true }; // Skip payment, serve from cache
+ }
+ }
+ } catch {
+ // Invalid payment header, continue to normal payment flow
}
});
```
@@ -211,24 +305,30 @@ const resourceServer = new x402ResourceServer(facilitatorClient)
Declare the payment-identifier extension in your route configuration:
```python
-from x402.http.servers.fastapi import x402FastAPIResourceServer
+from x402.server import x402ResourceServer
+from x402.http import FacilitatorConfig, HTTPFacilitatorClient, PaymentOption
+from x402.http.middleware.fastapi import PaymentMiddlewareASGI
+from x402.http.types import RouteConfig
+from x402.mechanisms.evm.exact import ExactEvmServerScheme
from x402.extensions.payment_identifier import (
declare_payment_identifier_extension,
PAYMENT_IDENTIFIER,
)
routes = {
- "GET /weather": {
- "accepts": {
- "scheme": "exact",
- "price": "$0.001",
- "network": "eip155:84532",
- "payTo": address,
+ "GET /weather": RouteConfig(
+ accepts=[
+ PaymentOption(
+ scheme="exact",
+ price="$0.001",
+ network="eip155:84532",
+ pay_to=address,
+ ),
+ ],
+ extensions={
+ PAYMENT_IDENTIFIER: declare_payment_identifier_extension(required=False), # optional
},
- "extensions": {
- PAYMENT_IDENTIFIER: declare_payment_identifier_extension(False), # optional
- },
- },
+ ),
}
```
@@ -236,62 +336,148 @@ routes = {
```python
# Payment ID is optional (clients can omit it)
-declare_payment_identifier_extension(False)
+declare_payment_identifier_extension(required=False)
# Payment ID is required (clients must provide it or receive 400 Bad Request)
-declare_payment_identifier_extension(True)
+declare_payment_identifier_extension(required=True)
```
-### Step 2: Implement Idempotency Cache
+### Step 2: Cache Responses After Settlement
-Use the `extract_payment_identifier()` utility to check for cached responses:
+Store responses after successful payment settlement:
```python
-from x402.extensions.payment_identifier import extract_payment_identifier
import time
+from x402.schemas import SettleContext
+from x402.extensions.payment_identifier import extract_payment_identifier
# In-memory cache (use Redis in production)
-idempotency_cache = {}
-CACHE_TTL_MS = 60 * 60 * 1000 # 1 hour
-
-async def on_protected_request(context):
- # Check if payment ID is in cache
- payment_payload = context.payment_payload
- payment_id = extract_payment_identifier(payment_payload)
-
+idempotency_cache: dict = {}
+CACHE_TTL_SECONDS = 60 * 60 # 1 hour
+
+async def after_settle(ctx: SettleContext) -> None:
+ payment_id = extract_payment_identifier(ctx.payment_payload)
if payment_id:
- cached = idempotency_cache.get(payment_id)
- if cached and (time.time() * 1000 - cached["timestamp"]) < CACHE_TTL_MS:
- return {"grant_access": True} # Skip payment, grant access
+ idempotency_cache[payment_id] = {
+ "timestamp": time.time(),
+ "response": {}, # your response data
+ }
-http_server = x402FastAPIResourceServer(resource_server, routes)
-http_server.on_protected_request(on_protected_request)
+server = x402ResourceServer(facilitator)
+server.register("eip155:84532", ExactEvmServerScheme())
+server.on_after_settle(after_settle)
```
-### Step 3: Cache Responses After Settlement
+### Step 3: Check Cache Before Payment
-Store responses after successful payment settlement:
+Use FastAPI middleware to check the cache before the payment middleware processes the request:
```python
-from x402 import x402ResourceServer
-from x402.mechanisms.evm import ExactEvmScheme
+import base64
+import json
+from fastapi import Request, Response
+from x402.schemas import PaymentPayload
+
+@app.middleware("http")
+async def idempotency_middleware(request: Request, call_next):
+ payment_header = request.headers.get("X-Payment")
+ if payment_header:
+ try:
+ payment_data = json.loads(base64.b64decode(payment_header))
+ payment_payload = PaymentPayload.model_validate(payment_data)
+ payment_id = extract_payment_identifier(payment_payload)
+
+ if payment_id:
+ cached = idempotency_cache.get(payment_id)
+ if cached and time.time() - cached["timestamp"] < CACHE_TTL_SECONDS:
+ return Response(
+ content=json.dumps(cached["response"]),
+ media_type="application/json",
+ )
+ except Exception:
+ pass # Invalid payment header, continue to normal flow
+
+ return await call_next(request)
+
+# Add payment middleware AFTER idempotency middleware
+app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
+```
-async def after_settle(context):
- payment_id = extract_payment_identifier(context.payment_payload)
- if payment_id:
- idempotency_cache[payment_id] = {
- "timestamp": time.time() * 1000,
- "response": {} # your response data
- }
+
+
+
+### Step 1: Advertise Extension Support
+
+Declare the payment-identifier extension in your route configuration:
-resource_server = x402ResourceServer(facilitator_client)
-resource_server.register("eip155:84532", ExactEvmScheme())
-resource_server.on_after_settle(after_settle)
+```go
+import (
+ x402http "github.com/coinbase/x402/go/http"
+ "github.com/coinbase/x402/go/extensions/paymentidentifier"
+)
+
+// Optional or required payment identifier (pick one)
+paymentIdExtension := paymentidentifier.DeclarePaymentIdentifierExtension(false) // optional
+// paymentIdExtension = paymentidentifier.DeclarePaymentIdentifierExtension(true) // required
+
+routes := x402http.RoutesConfig{
+ "GET /weather": {
+ Accepts: []x402http.PaymentOption{
+ {
+ Scheme: "exact",
+ Price: "$0.001",
+ Network: "eip155:84532",
+ PayTo: address,
+ },
+ },
+ Extensions: map[string]interface{}{
+ paymentidentifier.PAYMENT_IDENTIFIER: paymentIdExtension,
+ },
+ },
+}
+```
+
+### Step 2: Extract Payment ID
+
+Use the `ExtractPaymentIdentifier()` utility to get the payment ID from the payload:
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+// In your handler
+payload := c.MustGet("x402_payload").(x402.PaymentPayload)
+
+paymentID, err := paymentidentifier.ExtractPaymentIdentifier(payload, true)
+if err != nil {
+ // Handle invalid payment ID
+ c.JSON(400, gin.H{"error": err.Error()})
+ return
+}
+
+// Check for duplicate
+if existingResponse, found := processedPayments[paymentID]; found {
+ // Return cached response
+ c.JSON(200, existingResponse)
+ return
+}
+```
+
+### Step 3: Cache Responses
+
+Store responses after successful payment processing:
+
+```go
+// In-memory cache (use Redis in production)
+var processedPayments = make(map[string]interface{})
+
+// After processing payment
+processedPayments[paymentID] = responseData
```
+
### Idempotency Behavior
| Scenario | Server Response |
@@ -305,7 +491,7 @@ resource_server.on_after_settle(after_settle)
#### Cache TTL
-Adjust `CACHE_TTL_MS` based on your use case:
+Adjust `CACHE_TTL_MS` (TypeScript/Go) or `CACHE_TTL_SECONDS` (Python) based on your use case:
- Short TTL (5-15 min): For time-sensitive resources
- Long TTL (1-24 hours): For static or infrequently changing resources
@@ -323,7 +509,7 @@ Adjust `CACHE_TTL_MS` based on your use case:
### Client Functions
-#### `generatePaymentId()`
+#### `generatePaymentId(prefix?)`
Generates a cryptographically secure unique payment identifier.
@@ -332,18 +518,21 @@ import { generatePaymentId } from "@x402/extensions/payment-identifier";
const paymentId = generatePaymentId();
// Returns: "pay_<32-character-hex-string>"
+
+const orderId = generatePaymentId("order_");
+// Returns: "order_<32-character-hex-string>"
```
-#### `appendPaymentIdentifierToExtensions(extensions, paymentId?)`
+#### `appendPaymentIdentifierToExtensions(extensions, id?)`
-Adds a payment identifier to the extensions object. If no payment ID is provided, one is generated automatically.
+Adds a payment identifier to the extensions object. Only modifies extensions if the server declared support for the extension. If no payment ID is provided, one is generated automatically.
```typescript
import { appendPaymentIdentifierToExtensions } from "@x402/extensions/payment-identifier";
-const extensions = {};
-appendPaymentIdentifierToExtensions(extensions, "pay_custom_id");
-// extensions now contains the payment-identifier extension
+const extensions = paymentRequired.extensions ?? {};
+appendPaymentIdentifierToExtensions(extensions, "pay_custom_id_1234567890abcdef");
+// extensions now contains the payment-identifier extension (only if server declared it)
```
#### `isValidPaymentId(id)`
@@ -353,21 +542,21 @@ Validates a payment identifier format.
```typescript
import { isValidPaymentId } from "@x402/extensions/payment-identifier";
-isValidPaymentId("pay_7d5d747be160e280"); // true
-isValidPaymentId("invalid"); // false
+isValidPaymentId("pay_7d5d747be160e280504c099d984bcfe0"); // true
+isValidPaymentId("invalid"); // false (too short)
```
### Server Functions
-#### `declarePaymentIdentifierExtension(required)`
+#### `declarePaymentIdentifierExtension(required?)`
Creates a payment-identifier extension declaration for resource servers.
```typescript
import { declarePaymentIdentifierExtension } from "@x402/extensions/payment-identifier";
-// Optional payment ID
-const extension = declarePaymentIdentifierExtension(false);
+// Optional payment ID (default)
+const extension = declarePaymentIdentifierExtension();
// Required payment ID
const extensionRequired = declarePaymentIdentifierExtension(true);
@@ -386,16 +575,17 @@ if (paymentId) {
}
```
-#### `validatePaymentIdentifier(paymentPayload)`
+#### `validatePaymentIdentifier(extension)`
-Validates the payment identifier in a payment payload.
+Validates the payment identifier extension object structure and ID format.
```typescript
import { validatePaymentIdentifier } from "@x402/extensions/payment-identifier";
-const result = validatePaymentIdentifier(paymentPayload);
+const extension = paymentPayload.extensions?.["payment-identifier"];
+const result = validatePaymentIdentifier(extension);
if (!result.valid) {
- console.error(result.error);
+ console.error(result.errors);
}
```
@@ -415,7 +605,7 @@ import {
### Client Functions
-#### `generate_payment_id()`
+#### `generate_payment_id(prefix="pay_")`
Generates a cryptographically secure unique payment identifier.
@@ -424,18 +614,21 @@ from x402.extensions.payment_identifier import generate_payment_id
payment_id = generate_payment_id()
# Returns: "pay_<32-character-hex-string>"
+
+order_id = generate_payment_id("order_")
+# Returns: "order_<32-character-hex-string>"
```
#### `append_payment_identifier_to_extensions(extensions, id=None)`
-Adds a payment identifier to the extensions object. If no payment ID is provided, one is generated automatically.
+Adds a payment identifier to the extensions object. Only modifies extensions if the server declared support for the extension. If no payment ID is provided, one is generated automatically.
```python
from x402.extensions.payment_identifier import append_payment_identifier_to_extensions
-extensions = {}
-append_payment_identifier_to_extensions(extensions, "pay_custom_id")
-# extensions now contains the payment-identifier extension
+extensions = payment_required.extensions or {}
+append_payment_identifier_to_extensions(extensions, "pay_custom_id_1234567890abcdef")
+# extensions now contains the payment-identifier extension (only if server declared it)
```
#### `is_valid_payment_id(id)`
@@ -445,8 +638,8 @@ Validates a payment identifier format.
```python
from x402.extensions.payment_identifier import is_valid_payment_id
-is_valid_payment_id("pay_7d5d747be160e280") # True
-is_valid_payment_id("invalid") # False
+is_valid_payment_id("pay_7d5d747be160e280504c099d984bcfe0") # True
+is_valid_payment_id("invalid") # False (too short)
```
### Server Functions
@@ -458,11 +651,11 @@ Creates a payment-identifier extension declaration for resource servers.
```python
from x402.extensions.payment_identifier import declare_payment_identifier_extension
-# Optional payment ID
-extension = declare_payment_identifier_extension(False)
+# Optional payment ID (default)
+extension = declare_payment_identifier_extension()
# Required payment ID
-extension_required = declare_payment_identifier_extension(True)
+extension_required = declare_payment_identifier_extension(required=True)
```
#### `extract_payment_identifier(payment_payload)`
@@ -478,16 +671,17 @@ if payment_id:
pass
```
-#### `validate_payment_identifier(payment_payload)`
+#### `validate_payment_identifier(extension)`
-Validates the payment identifier in a payment payload.
+Validates the payment identifier extension object structure and ID format.
```python
from x402.extensions.payment_identifier import validate_payment_identifier
-result = validate_payment_identifier(payment_payload)
+extension = payment_payload.extensions.get("payment-identifier")
+result = validate_payment_identifier(extension)
if not result.valid:
- print(result.error)
+ print(result.errors)
```
### Constants
@@ -497,8 +691,113 @@ from x402.extensions.payment_identifier import (
PAYMENT_IDENTIFIER, # "payment-identifier"
PAYMENT_ID_MIN_LENGTH, # 16
PAYMENT_ID_MAX_LENGTH, # 128
- PAYMENT_ID_PATTERN, # r"^[a-zA-Z0-9_-]+$"
+ PAYMENT_ID_PATTERN, # re.compile(r"^[a-zA-Z0-9_-]+$")
+)
+```
+
+
+
+
+### Client Functions
+
+#### `GeneratePaymentID(prefix string)`
+
+Generates a cryptographically secure unique payment identifier.
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+// Generate with default prefix "pay_"
+paymentID := paymentidentifier.GeneratePaymentID("")
+// Returns: "pay_<32-character-hex-string>"
+
+// Generate with custom prefix
+paymentID = paymentidentifier.GeneratePaymentID("order_")
+// Returns: "order_<32-character-hex-string>"
+```
+
+#### `AppendPaymentIdentifierToExtensions(extensions map[string]interface{}, id string) error`
+
+Adds a payment identifier to the extensions object. Only modifies extensions if the server declared support for the extension. Pass an empty string to auto-generate an ID.
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+extensions := make(map[string]interface{})
+err := paymentidentifier.AppendPaymentIdentifierToExtensions(extensions, "pay_custom_id_1234567890abcdef")
+// extensions now contains the payment-identifier extension (only if server declared it)
+```
+
+#### `IsValidPaymentID(id string) bool`
+
+Validates a payment identifier format.
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+valid := paymentidentifier.IsValidPaymentID("pay_7d5d747be160e280504c099d984bcfe0") // true
+valid = paymentidentifier.IsValidPaymentID("invalid") // false (too short)
+```
+
+### Server Functions
+
+#### `DeclarePaymentIdentifierExtension(required bool) PaymentIdentifierExtension`
+
+Creates a payment-identifier extension declaration for resource servers.
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+// Optional payment ID
+extension := paymentidentifier.DeclarePaymentIdentifierExtension(false)
+
+// Required payment ID
+extensionRequired := paymentidentifier.DeclarePaymentIdentifierExtension(true)
+```
+
+#### `ExtractPaymentIdentifier(payload PaymentPayload, validate bool) (string, error)`
+
+Extracts the payment identifier from a payment payload.
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+paymentID, err := paymentidentifier.ExtractPaymentIdentifier(payload, true)
+if err != nil {
+ // Handle error
+}
+if paymentID != "" {
+ // Check cache, implement idempotency logic
+}
+```
+
+#### `ValidatePaymentIdentifier(extension interface{}) ValidationResult`
+
+Validates the payment identifier extension object structure and ID format.
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+extension := payload.Extensions[paymentidentifier.PAYMENT_IDENTIFIER]
+result := paymentidentifier.ValidatePaymentIdentifier(extension)
+if !result.Valid {
+ // Handle validation errors
+ fmt.Println(result.Errors)
+}
+```
+
+### Constants
+
+```go
+import "github.com/coinbase/x402/go/extensions/paymentidentifier"
+
+const (
+ PAYMENT_IDENTIFIER = "payment-identifier"
+ PAYMENT_ID_MIN_LENGTH = 16
+ PAYMENT_ID_MAX_LENGTH = 128
)
+
+var PAYMENT_ID_PATTERN = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
```
@@ -508,15 +807,17 @@ from x402.extensions.payment_identifier import (
Full working examples are available in the x402 repository:
+**TypeScript:**
- [TypeScript Client Example](https://github.com/coinbase/x402/tree/main/examples/typescript/clients/payment-identifier)
- [TypeScript Server Example](https://github.com/coinbase/x402/tree/main/examples/typescript/servers/payment-identifier)
+
+**Python:**
- [Python Client Example](https://github.com/coinbase/x402/tree/main/examples/python/clients/payment-identifier)
- [Python Server Example](https://github.com/coinbase/x402/tree/main/examples/python/servers/payment-identifier)
-## Support
-
-- **GitHub**: [github.com/coinbase/x402](https://github.com/coinbase/x402)
-- **Discord**: [Join #x402 channel](https://discord.com/invite/cdp)
+**Go:**
+- [Go Client Example](https://github.com/coinbase/x402/tree/main/examples/go/clients/payment-identifier)
+- [Go Server Example](https://github.com/coinbase/x402/tree/main/examples/go/servers/payment-identifier)
## FAQ
@@ -530,4 +831,4 @@ A: This is configurable by the server. Typical TTLs range from 5 minutes to 24 h
A: Payment IDs must be 16-128 characters, alphanumeric with hyphens and underscores allowed. Use `isValidPaymentId()` to validate custom IDs.
**Q: What if the server doesn't support payment-identifier?**
-A: The extension is optional. If the server doesn't advertise support, clients can still make payments normally without idempotency.
\ No newline at end of file
+A: The extension is optional. If the server doesn't advertise support, clients can still make payments normally without idempotency.
diff --git a/docs/extensions/sign-in-with-x.mdx b/docs/extensions/sign-in-with-x.mdx
index 22257a775d..0344207164 100644
--- a/docs/extensions/sign-in-with-x.mdx
+++ b/docs/extensions/sign-in-with-x.mdx
@@ -7,10 +7,14 @@ The Sign-In-With-X (SIWX) extension implements [CAIP-122](https://chainagnostic.
## Overview
-SIWX solves a key problem in x402: **repeat access to purchased content**. Without SIWX, clients must pay every time they request a resource. With SIWX:
+SIWX solves two key problems in x402:
-* **For Buyers**: Sign in with your wallet to access content you've already paid for
-* **For Sellers**: Grant access to returning customers without requiring repayment
+1. **Repeat access to purchased content**: Without SIWX, clients must pay every time they request a resource. With SIWX, sign in with your wallet to access content you've already paid for.
+2. **Auth-only routes**: Protect resources with wallet authentication alone, without requiring payment.
+
+**Key Features:**
+* **For Buyers**: Sign in with your wallet to access content you've already paid for, or authenticate to access wallet-gated resources
+* **For Sellers**: Grant access to returning customers without requiring repayment, or create auth-only routes that require wallet signatures but no payment
* **Chain-Agnostic**: Works with EVM (Ethereum, Base, etc.) and Solana wallets
* **Standards-Based**: Built on CAIP-122, EIP-4361 (SIWE), and Sign-In-With-Solana
@@ -19,7 +23,9 @@ SIWX solves a key problem in x402: **repeat access to purchased content**. Witho
1. **Server** returns 402 with `sign-in-with-x` extension containing challenge parameters
2. **Client** signs the CAIP-122 message with their wallet
3. **Client** sends signed proof in `SIGN-IN-WITH-X` HTTP header
-4. **Server** verifies signature and grants access if wallet has previous payment
+4. **Server** verifies signature and grants access either because:
+ - The route is auth-only (requires signature but no payment), or
+ - The wallet has previously paid for the resource
This is a **Server ↔ Client** extension. The Facilitator is not involved in the authentication flow.
@@ -73,11 +79,19 @@ The easiest way to implement SIWX is using the provided hooks, which handle all
statement: 'Sign in to access your purchased content',
}),
},
+ 'GET /profile': {
+ accepts: [], // Auth-only route: no payment required
+ extensions: declareSIWxExtension({
+ network: NETWORK, // Required for auth-only routes (cannot be inferred from accepts)
+ statement: 'Sign in to view your profile',
+ expirationSeconds: 300,
+ }),
+ },
};
// 3. Verify incoming SIWX proofs
const httpServer = new x402HTTPResourceServer(resourceServer, routes)
- .onProtectedRequest(createSIWxRequestHook({ storage })); // Grants access if paid
+ .onProtectedRequest(createSIWxRequestHook({ storage })); // Grants access for auth-only or paid routes
```
@@ -85,7 +99,9 @@ The easiest way to implement SIWX is using the provided hooks, which handle all
The hooks automatically:
- **siwxResourceServerExtension**: Derives `network` from `accepts`, `domain`/`uri` from request URL, refreshes `nonce`/`issuedAt`/`expirationTime` per request
- **createSIWxSettleHook**: Records payment when settlement succeeds
-- **createSIWxRequestHook**: Validates and verifies SIWX proofs, grants access if wallet has paid
+- **createSIWxRequestHook**: Validates and verifies SIWX proofs, grants access for auth-only routes (`accepts: []`) or when wallet has paid
+
+**Auth-only routes** (declared with `accepts: []`) grant access based on a valid SIWX signature alone, without requiring payment. This is useful for wallet-gated content that doesn't need micropayments.
### Smart Wallet Support (EIP-1271 / EIP-6492)
@@ -166,10 +182,11 @@ For custom implementations, you can use the low-level functions directly:
}
// verification.address is the verified wallet
- // Check if this wallet has paid before
+ // Grant access for auth-only routes or if wallet has paid
+ const isAuthOnly = await checkIfAuthOnlyRoute(request);
const hasPaid = await checkPaymentHistory(verification.address);
- if (hasPaid) {
- // Grant access without payment
+ if (isAuthOnly || hasPaid) {
+ // Grant access
}
}
```
@@ -326,6 +343,8 @@ declareSIWxExtension({
})
```
+**Note for auth-only routes:** When using `accepts: []`, the `network` parameter cannot be inferred from payment requirements and must be provided explicitly.
+
### `parseSIWxHeader(header)`
Parses a base64-encoded SIGN-IN-WITH-X header into a payload object.
diff --git a/docs/faq.md b/docs/faq.md
index 4648d881b7..83c80990f8 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -63,8 +63,8 @@ Yes. x402 handles the _payment execution_. You can still meter usage, aggregate
| Network | CAIP-2 ID | Asset | Fees\* | Status |
| -------------- | --------- | ----- | -------- | ----------- |
-| Base | `eip155:8453` | Any EIP-3009 token | fee-free | **Mainnet** |
-| Base Sepolia | `eip155:84532` | Any EIP-3009 token | fee-free | **Testnet** |
+| Base | `eip155:8453` | Any ERC-20 token | fee-free | **Mainnet** |
+| Base Sepolia | `eip155:84532` | Any ERC-20 token | fee-free | **Testnet** |
| Solana | `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` | Any SPL token or Token-2022 token | fee-free | **Mainnet** |
| Solana Devnet | `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` | Any SPL token or Token-2022 | fee-free | **Testnet** |
@@ -119,7 +119,7 @@ Yes. Programmatic wallets (e.g., **CDP Wallet API**, **viem**, **ethers‑v6** H
Tracked in public GitHub issues + community RFCs. Major themes:
* Multi‑asset support
-* Additional schemes (`upto`, `stream`, `permit2`)
+* Additional schemes (`upto`, `stream`)
* Discovery layer for service search & reputation
**Why is x402 hosted in the Coinbase GitHub?**
@@ -131,7 +131,7 @@ We acknowledge that the repo is primarily under Coinbase ownership today. This i
#### I keep getting `402 Payment Required`, even after attaching `PAYMENT-SIGNATURE`. Why?
1. Signature is invalid (wrong chain ID or payload fields).
-2. Payment amount is less than the required `amount` in the payment requirements.
+2. Payment amount does not exactly match the required `amount` in the payment requirements (the exact scheme requires strict equality - no overpayment or underpayment).
3. Address has insufficient USDC or was flagged by KYT.\
Check the `error` field in the server's JSON response for details.
diff --git a/docs/getting-started/quickstart-for-buyers.mdx b/docs/getting-started/quickstart-for-buyers.mdx
index b95796ab83..003524dc8a 100644
--- a/docs/getting-started/quickstart-for-buyers.mdx
+++ b/docs/getting-started/quickstart-for-buyers.mdx
@@ -32,6 +32,9 @@ There are pre-configured [examples available in the x402 repo](https://github.co
# For Aptos support, also add:
npm install @x402/aptos
+
+ # For Stellar support, also add:
+ npm install @x402/stellar
```
@@ -135,6 +138,20 @@ const privateKey = new Ed25519PrivateKey(process.env.APTOS_PRIVATE_KEY!);
const aptosSigner = Account.fromPrivateKey({ privateKey });
```
+#### Stellar
+
+Use the Stellar SDK to instantiate a signer:
+
+```typescript
+import { createEd25519Signer } from "@x402/stellar";
+
+// Create signer from private key (S... format)
+const stellarSigner = createEd25519Signer(
+ process.env.STELLAR_PRIVATE_KEY!,
+ "stellar:testnet"
+);
+```
+
### 3. Make Paid Requests Automatically
@@ -387,6 +404,15 @@ You can register multiple payment schemes to handle different networks:
const aptosPrivateKey = new Ed25519PrivateKey(process.env.APTOS_PRIVATE_KEY!);
const aptosSigner = Account.fromPrivateKey({ privateKey: aptosPrivateKey });
client.register("aptos:*", new ExactAptosScheme(aptosSigner));
+
+ // For Stellar support, also add:
+ import { ExactStellarScheme, createEd25519Signer } from "@x402/stellar";
+
+ const stellarSigner = createEd25519Signer(
+ process.env.STELLAR_PRIVATE_KEY!,
+ "stellar:testnet"
+ );
+ client.register("stellar:*", new ExactStellarScheme(stellarSigner));
```
diff --git a/docs/getting-started/quickstart-for-sellers.mdx b/docs/getting-started/quickstart-for-sellers.mdx
index 3a304d78da..789d6097e8 100644
--- a/docs/getting-started/quickstart-for-sellers.mdx
+++ b/docs/getting-started/quickstart-for-sellers.mdx
@@ -40,6 +40,13 @@ There are pre-configured examples available in the x402 repo for both [Node.js](
npm install @x402/hono @x402/core @x402/evm @x402/svm
```
+
+ Install the [x402 Fastify middleware package](https://www.npmjs.com/package/@x402/fastify).
+
+ ```bash
+ npm install @x402/fastify @x402/core @x402/evm @x402/svm
+ ```
+
Add the x402 Go module to your project:
@@ -299,6 +306,63 @@ Integrate the payment middleware into your application. You will need to provide
serve({ fetch: app.fetch, port: 4021 });
```
+
+ Full example in the repo [here](https://github.com/coinbase/x402/tree/main/examples/typescript/servers/fastify).
+
+ ```typescript
+ import Fastify from "fastify";
+ import { paymentMiddleware, x402ResourceServer } from "@x402/fastify";
+ import { ExactEvmScheme } from "@x402/evm/exact/server";
+ import { ExactSvmScheme } from "@x402/svm/exact/server";
+ import { HTTPFacilitatorClient } from "@x402/core/server";
+
+ const app = Fastify();
+ const evmAddress = "0xYourEvmAddress";
+ const svmAddress = "YourSolanaAddress";
+
+ const facilitatorClient = new HTTPFacilitatorClient({
+ url: "https://x402.org/facilitator"
+ });
+
+ paymentMiddleware(
+ app,
+ {
+ "GET /weather": {
+ accepts: [
+ {
+ scheme: "exact",
+ price: "$0.001",
+ network: "eip155:84532", // Base Sepolia
+ payTo: evmAddress,
+ },
+ {
+ scheme: "exact",
+ price: "$0.001",
+ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", // Solana Devnet
+ payTo: svmAddress,
+ },
+ ],
+ description: "Weather data",
+ mimeType: "application/json",
+ },
+ },
+ new x402ResourceServer(facilitatorClient)
+ .register("eip155:84532", new ExactEvmScheme())
+ .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()),
+ );
+
+ app.get("/weather", async () => {
+ return {
+ report: {
+ weather: "sunny",
+ temperature: 70,
+ },
+ };
+ });
+
+ app.listen({ port: 4021 });
+ ```
+
Full example in the repo [here](https://github.com/coinbase/x402/tree/main/examples/go/servers/gin).
@@ -372,6 +436,85 @@ Integrate the payment middleware into your application. You will need to provide
}
```
+
+ Full example in the repo [here](https://github.com/coinbase/x402/tree/main/examples/go/servers/nethttp).
+
+ ```go
+ package main
+
+ import (
+ "encoding/json"
+ "net/http"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ nethttpmw "github.com/coinbase/x402/go/http/nethttp"
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+ )
+
+ func main() {
+ evmAddress := "0xYourEvmAddress"
+ svmAddress := "YourSolanaAddress"
+ evmNetwork := x402.Network("eip155:84532") // Base Sepolia
+ svmNetwork := x402.Network("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") // Solana Devnet
+
+ // Create facilitator client
+ facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: "https://x402.org/facilitator",
+ })
+
+ // Configure routes
+ routes := x402http.RoutesConfig{
+ "GET /weather": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ Price: "$0.001",
+ Network: "eip155:84532",
+ PayTo: evmAddress,
+ },
+ {
+ Scheme: "exact",
+ Price: "$0.001",
+ Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ PayTo: svmAddress,
+ },
+ },
+ Description: "Get weather data for a city",
+ MimeType: "application/json",
+ },
+ }
+
+ // Create ServeMux and register handlers
+ mux := http.NewServeMux()
+
+ // Protected endpoint
+ mux.HandleFunc("GET /weather", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(map[string]interface{}{
+ "weather": "sunny",
+ "temperature": 70,
+ })
+ })
+
+ // Apply x402 payment middleware
+ handler := nethttpmw.X402Payment(nethttpmw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []nethttpmw.SchemeConfig{
+ {Network: evmNetwork, Server: evm.NewExactEvmScheme()},
+ {Network: svmNetwork, Server: svm.NewExactSvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+ })(mux)
+
+ http.ListenAndServe(":4021", handler)
+ }
+ ```
+
Full example in the repo [here](https://github.com/coinbase/x402/tree/main/examples/python/servers/fastapi).
@@ -686,7 +829,7 @@ Change from testnet to mainnet network identifiers:
For multi-network support, register both EVM and SVM schemes:
-
+
```typescript
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { ExactSvmScheme } from "@x402/svm/exact/server";
diff --git a/docs/introduction.md b/docs/introduction.md
index 708ac07994..86d16a34c1 100644
--- a/docs/introduction.md
+++ b/docs/introduction.md
@@ -15,16 +15,16 @@ With x402, any web service can require payment before serving a response, using
### Why Use x402?
-x402 offers key advantages over traditional payment systems:
+x402 offers:
-* **Low fees and minimal friction** compared to traditional credit cards and fiat payment processors
-* **Native support for machine-to-machine payments**, enabling seamless use by AI agents
-* **Built-in micropayment support**, making it easy to monetize usage-based services
+- **No fees and minimal friction** x402 as a standard has 0 fees built in.
+- **Native support for machine-to-machine payments**, enabling seamless use by AI agents
+- **Built-in micropayment support**, making it easy to monetize usage-based services
### Who is x402 for?
-* **Sellers:** Service providers who want to monetize their APIs or content. x402 enables direct, programmatic payments from clients with minimal setup.
-* **Buyers:** Human developers and AI agents seeking to access paid services without accounts or manual payment flows.
+- **Sellers:** Service providers who want to monetize their APIs or content. x402 enables direct, programmatic payments from clients with minimal setup.
+- **Buyers:** Human developers and AI agents seeking to access paid services without accounts or manual payment flows.
Both sellers and buyers interact directly through HTTP requests, with payment handled transparently through the protocol.
@@ -32,11 +32,11 @@ Both sellers and buyers interact directly through HTTP requests, with payment ha
x402 enables a range of use cases, including:
-* API services paid per request
-* AI agents that autonomously pay for API access
-* [Paywalls](https://x.com/MurrLincoln/status/1935406976881803601) for digital content
-* Microservices and tooling monetized via microtransactions
-* Proxy services that aggregate and resell API capabilities
+- API services paid per request
+- AI agents that autonomously pay for API access
+- [Paywalls](https://x.com/MurrLincoln/status/1935406976881803601) for digital content
+- Microservices and tooling monetized via microtransactions
+- Proxy services that aggregate and resell API capabilities
### How Does It Work?
@@ -50,9 +50,9 @@ At a high level, the flow is simple:
For more detail, see:
-* [Client / Server](/core-concepts/client-server)
-* [Facilitator](/core-concepts/facilitator)
-* [HTTP 402](/core-concepts/http-402)
+- [Client / Server](/core-concepts/client-server)
+- [Facilitator](/core-concepts/facilitator)
+- [HTTP 402](/core-concepts/http-402)
The goal is to make programmatic commerce accessible, permissionless, and developer-friendly.
@@ -60,7 +60,7 @@ The goal is to make programmatic commerce accessible, permissionless, and develo
Ready to build? Start here:
-* [Quickstart for Sellers](/getting-started/quickstart-for-sellers)
-* [Quickstart for Buyers](/getting-started/quickstart-for-buyers)
-* [Explore Core Concepts](/core-concepts/http-402)
-* [Join our community on Discord](https://discord.gg/invite/cdp)
+- [Quickstart for Sellers](/getting-started/quickstart-for-sellers)
+- [Quickstart for Buyers](/getting-started/quickstart-for-buyers)
+- [Explore Core Concepts](/core-concepts/http-402)
+- [Join our community on Discord](https://discord.gg/invite/cdp)
diff --git a/docs/sdk-features.md b/docs/sdk-features.md
index b63ede970e..1d94740a15 100644
--- a/docs/sdk-features.md
+++ b/docs/sdk-features.md
@@ -19,7 +19,7 @@ This page tracks which features are implemented in each SDK (TypeScript, Go, Pyt
| Role | TypeScript | Go | Python |
|------|------------|-----|--------|
-| Server | Express, Hono, Next.js | Gin | FastAPI, Flask |
+| Server | Express, Hono, Next.js | Gin, net/http | FastAPI, Flask |
| Client | Fetch, Axios | net/http | httpx, requests |
## Networks
@@ -28,6 +28,7 @@ This page tracks which features are implemented in each SDK (TypeScript, Go, Pyt
|---------|------------|-----|--------|
| evm (EIP-155) | ✅ | ✅ | ✅ |
| svm (Solana) | ✅ | ✅ | ✅ |
+| stellar | ✅ | ❌ | ❌ |
| aptos | ✅ | ❌ | ❌ |
## Mechanisms
@@ -35,16 +36,23 @@ This page tracks which features are implemented in each SDK (TypeScript, Go, Pyt
| Mechanism | TypeScript | Go | Python |
|-----------|------------|-----|--------|
| exact/evm (EIP-3009) | ✅ | ✅ | ✅ |
+| exact/evm (Permit2) | ✅ | ✅ | ✅ |
| exact/svm (SPL) | ✅ | ✅ | ✅ |
+| exact/stellar (Soroban) | ✅ | ❌ | ❌ |
| exact/aptos (Fungible Assets) | ✅ | ❌ | ❌ |
+| upto/evm (Permit2) | ✅ | ✅ | ❌ |
## Extensions
| Extension | TypeScript | Go | Python |
|-----------|------------|-----|--------|
-| bazaar | ✅ | ✅ | ✅ |
+| bazaar (server) | ✅ | ✅ | ✅ |
+| bazaar (facilitator client) | ✅ | ✅ | ✅ |
| sign-in-with-x | ✅ | ❌ | ❌ |
-| payment-identifier | ✅ | ❌ | ✅ |
+| payment-identifier | ✅ | ✅ | ✅ |
+| offer-receipt | ✅ | ❌ | ❌ |
+| eip2612-gas-sponsoring | ✅ | ✅ | ✅ |
+| erc20-approval-gas-sponsoring | ✅ | ✅ | ✅ |
## Client Hooks
@@ -65,7 +73,7 @@ This page tracks which features are implemented in each SDK (TypeScript, Go, Pyt
| onBeforeSettle | ✅ | ✅ | ✅ |
| onAfterSettle | ✅ | ✅ | ✅ |
| onSettleFailure | ✅ | ✅ | ✅ |
-| onProtectedRequest (HTTP) | ✅ | ❌ | ❌ |
+| onProtectedRequest (HTTP) | ✅ | ✅ | ❌ |
## Facilitator Hooks
diff --git a/e2e/.env-local b/e2e/.env-local
index d5cfb089e4..77e443bb11 100644
--- a/e2e/.env-local
+++ b/e2e/.env-local
@@ -1,7 +1,10 @@
# E2E Test Configuration
SERVER_EVM_ADDRESS=
SERVER_SVM_ADDRESS=
+SERVER_STELLAR_ADDRESS=
CLIENT_EVM_PRIVATE_KEY=
CLIENT_SVM_PRIVATE_KEY=
+CLIENT_STELLAR_PRIVATE_KEY=
FACILITATOR_EVM_PRIVATE_KEY=
FACILITATOR_SVM_PRIVATE_KEY=
+FACILITATOR_STELLAR_PRIVATE_KEY=
diff --git a/e2e/README.md b/e2e/README.md
index 3b0a6fe4d4..9a54295429 100644
--- a/e2e/README.md
+++ b/e2e/README.md
@@ -85,27 +85,62 @@ Add the `-v` flag to any command for verbose output:
Useful for debugging test failures or understanding the payment flow.
+## Wallet Safety Warning
+
+**Use dedicated test wallets only. Do NOT use wallets that hold real funds.**
+
+The test suite moves ETH between the configured wallets during a run. Funds stay
+within the set of wallets defined in `.env`, but individual wallet balances will
+change unpredictably:
+
+- **ETH is transferred** from the facilitator wallet to the client wallet so the
+ client can pay gas for granting and revoking Permit2 approvals between tests.
+- **ETH is swept** from the client wallet back to the facilitator after revocation
+ to create a zero-balance state, which is required to exercise the facilitator's
+ gasless funding step.
+- **Token approvals are granted and revoked** on the client wallet as part of
+ normal test flow.
+
+While no funds leave the configured wallet set, the client wallet's ETH balance
+will be drained to near-zero between tests. Do not rely on any particular wallet
+having a stable balance during or after a run.
+
## Environment Variables
Required environment variables (set in `.env` file):
```bash
-# Client wallets
+# Client wallets (⚠️ TEST WALLETS ONLY — balances will be swept during runs)
CLIENT_EVM_PRIVATE_KEY=0x... # EVM private key for client payments
CLIENT_SVM_PRIVATE_KEY=... # Solana private key for client payments
CLIENT_APTOS_PRIVATE_KEY=... # Aptos private key for client payments (hex string)
+CLIENT_STELLAR_PRIVATE_KEY=... # Stellar private key for client payments
# Server payment addresses
SERVER_EVM_ADDRESS=0x... # Where servers receive EVM payments
SERVER_SVM_ADDRESS=... # Where servers receive Solana payments
SERVER_APTOS_ADDRESS=0x... # Where servers receive Aptos payments
+SERVER_STELLAR_ADDRESS=... # Where servers receive Stellar payments
-# Facilitator wallets (for payment verification/settlement)
+# Facilitator wallets (⚠️ TEST WALLETS ONLY — used to fund/drain client between tests)
FACILITATOR_EVM_PRIVATE_KEY=0x... # EVM private key for facilitator
FACILITATOR_SVM_PRIVATE_KEY=... # Solana private key for facilitator
FACILITATOR_APTOS_PRIVATE_KEY=... # Aptos private key for facilitator (hex string)
+FACILITATOR_STELLAR_PRIVATE_KEY=... # Stellar private key for facilitator
```
+### Account Setup Instructions
+
+#### Stellar Testnet
+
+You need **three separate Stellar accounts** for e2e tests (client, server, facilitator):
+
+1. Go to [Stellar Laboratory](https://lab.stellar.org/account/create) ➡️ Generate keypair ➡️ Fund account with Friendbot, then copy the `Secret` and `Public` keys so you can use them.
+2. Add USDC trustline (required for client and server): go to [Fund Account](https://lab.stellar.org/account/fund) ➡️ Paste your `Public Key` ➡️ Add USDC Trustline ➡️ paste your `Secret key` ➡️ Sign transaction ➡️ Add Trustline.
+3. Get testnet USDC from [Circle Faucet](https://faucet.circle.com/) (select Stellar network).
+
+> **Note:** The facilitator account only needs XLM (step 1). Client and server accounts need all three steps.
+
## Example Session
```bash
@@ -118,7 +153,7 @@ $ pnpm test --min
✔ Select servers › express, hono, legacy-express
✔ Select clients › axios, fetch, httpx
✔ Select extensions › bazaar
-✔ Select protocol families › EVM, SVM, Aptos
+✔ Select protocol families › EVM, SVM, Aptos, Stellar
📊 Coverage-Based Minimization
Total scenarios: 156
diff --git a/e2e/clients/axios/.env-local b/e2e/clients/axios/.env-local
index 73a01182c4..910973a20d 100644
--- a/e2e/clients/axios/.env-local
+++ b/e2e/clients/axios/.env-local
@@ -3,3 +3,4 @@ ENDPOINT_PATH=/weather
EVM_PRIVATE_KEY=
SVM_PRIVATE_KEY=
APTOS_PRIVATE_KEY=
+STELLAR_PRIVATE_KEY=
diff --git a/e2e/clients/axios/README.md b/e2e/clients/axios/README.md
index 463fb72b1f..8da87a9794 100644
--- a/e2e/clients/axios/README.md
+++ b/e2e/clients/axios/README.md
@@ -1,13 +1,13 @@
-# E2E Test Client: TypeScript Fetch
+# E2E Test Client: TypeScript Axios
-This client demonstrates and tests the `@x402/fetch` package with both EVM and SVM payment support.
+This client demonstrates and tests the `@x402/axios` package with EVM, SVM, and Stellar payment support.
## What It Tests
### Core Functionality
- ✅ **V2 Protocol** - Modern x402 protocol with CAIP-2 networks
- ✅ **V1 Protocol** - Legacy x402 protocol with simple network names
-- ✅ **Multi-chain Support** - Both EVM and SVM in a single client
+- ✅ **Multi-chain Support** - EVM, SVM, and (optional) Stellar in a single client
- ✅ **Automatic Payment Handling** - Transparent 402 response handling
- ✅ **Payment Response Decoding** - Extracts settlement information from headers
@@ -16,40 +16,44 @@ This client demonstrates and tests the `@x402/fetch` package with both EVM and S
- ✅ **EVM V1** - `base-sepolia` and `base` networks
- ✅ **SVM V2** - `solana:*` wildcard scheme
- ✅ **SVM V1** - `solana-devnet` and `solana` networks
+- ✅ **Stellar V2** - `stellar:*` wildcard scheme (optional)
## What It Demonstrates
### Usage Pattern
```typescript
-import { wrapFetchWithPayment } from "@x402/fetch";
+import axios from "axios";
+import { wrapAxiosWithPayment } from "@x402/axios";
import { x402Client } from "@x402/core/client";
import { ExactEvmClient } from "@x402/evm";
import { ExactEvmClientV1 } from "@x402/evm/v1";
import { ExactSvmClient } from "@x402/svm";
import { ExactSvmClientV1 } from "@x402/svm/v1";
+import { ExactStellarClient } from "@x402/stellar";
// Build x402 client with direct registration
const client = new x402Client()
.register("eip155:*", new ExactEvmClient(evmAccount))
.register("solana:*", new ExactSvmClient(svmSigner))
+ .register("stellar:*", new ExactStellarClient(stellarSigner))
.registerV1("base-sepolia", new ExactEvmClientV1(evmAccount))
.registerV1("base", new ExactEvmClientV1(evmAccount))
.registerV1("solana-devnet", new ExactSvmClientV1(svmSigner))
.registerV1("solana", new ExactSvmClientV1(svmSigner));
-// Wrap fetch with payment handling
-const fetchWithPayment = wrapFetchWithPayment(fetch, client);
+// Wrap axios with payment handling
+const axiosWithPayment = wrapAxiosWithPayment(axios.create(), client);
// Make request - 402 responses handled automatically
-const response = await fetchWithPayment(url, { method: "GET" });
+const response = await axiosWithPayment.get(url);
```
### Key Concepts Shown
1. **Builder Pattern** - Fluent API for registering multiple schemes
2. **Multi-Version Support** - V1 and V2 protocols side-by-side
-3. **Multi-Chain Support** - EVM and SVM in one client
+3. **Multi-Chain Support** - EVM, SVM, and (optional) Stellar in one client
4. **Network Flexibility** - Wildcards for V2, specific networks for V1
5. **Transparent Payment** - No manual 402 handling needed
@@ -58,8 +62,8 @@ const response = await fetchWithPayment(url, { method: "GET" });
This client is tested against:
- **Servers:** Express (TypeScript), Gin (Go)
- **Facilitators:** TypeScript, Go
-- **Endpoints:** `/protected` (EVM), `/protected-svm` (SVM)
-- **Networks:** Base Sepolia (EVM), Solana Devnet (SVM)
+- **Endpoints:** `/protected` (EVM), `/protected-svm` (SVM), `/protected-stellar` (Stellar)
+- **Networks:** Base Sepolia (EVM), Solana Devnet (SVM), Stellar Testnet (Stellar)
### Success Criteria
- ✅ Request succeeds with 200 status
@@ -72,24 +76,29 @@ This client is tested against:
```bash
# Via e2e test suite
cd e2e
-pnpm test --client=fetch
+pnpm test --client=axios
# Direct execution (requires environment variables)
-cd e2e/clients/fetch
+cd e2e/clients/axios
export RESOURCE_SERVER_URL="http://localhost:4022"
export ENDPOINT_PATH="/protected"
export EVM_PRIVATE_KEY="0x..."
export SVM_PRIVATE_KEY="..."
+export STELLAR_PRIVATE_KEY="S..." # optional
pnpm start
```
## Environment Variables
+### Required
- `RESOURCE_SERVER_URL` - Server base URL
- `ENDPOINT_PATH` - Path to protected endpoint
- `EVM_PRIVATE_KEY` - Ethereum private key (hex with 0x prefix)
- `SVM_PRIVATE_KEY` - Solana private key (base58 encoded)
+### Optional
+- `STELLAR_PRIVATE_KEY` - Stellar private key (S... format) - enables Stellar support
+
## Output Format
```json
@@ -108,11 +117,12 @@ pnpm start
## Package Dependencies
-- `@x402/fetch` - HTTP wrapper with payment handling
+- `@x402/axios` - Axios wrapper with payment handling
- `@x402/core` - Core x402 client and types
- `@x402/evm` - EVM payment mechanisms (V2)
- `@x402/evm/v1` - EVM payment mechanisms (V1)
- `@x402/svm` - SVM payment mechanisms (V2)
- `@x402/svm/v1` - SVM payment mechanisms (V1)
+- `@x402/stellar` - Stellar payment mechanisms (V2)
- `viem` - Ethereum library for account creation
- `@solana/kit` - Solana keypair utilities
diff --git a/e2e/clients/axios/index.ts b/e2e/clients/axios/index.ts
index cee6f944e5..31f00ddfca 100644
--- a/e2e/clients/axios/index.ts
+++ b/e2e/clients/axios/index.ts
@@ -3,14 +3,17 @@ import axios from "axios";
import { wrapAxiosWithPayment, decodePaymentResponseHeader } from "@x402/axios";
import { createPublicClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
-import { baseSepolia } from "viem/chains";
-import { ExactEvmScheme } from "@x402/evm/exact/client";
+import { base, baseSepolia } from "viem/chains";
+import { ExactEvmScheme, type ExactEvmSchemeOptions } from "@x402/evm/exact/client";
+import { UptoEvmScheme as UptoEvmClientScheme, type UptoEvmSchemeOptions } from "@x402/evm/upto/client";
import { ExactEvmSchemeV1 } from "@x402/evm/v1";
import { toClientEvmSigner } from "@x402/evm";
import { ExactSvmScheme } from "@x402/svm/exact/client";
import { ExactSvmSchemeV1 } from "@x402/svm/v1";
import { ExactAptosScheme } from "@x402/aptos/exact/client";
import { Account, Ed25519PrivateKey, PrivateKey, PrivateKeyVariants } from "@aptos-labs/ts-sdk";
+import { ExactStellarScheme } from "@x402/stellar/exact/client";
+import { createEd25519Signer, type Ed25519Signer } from "@x402/stellar";
import { base58 } from "@scure/base";
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { x402Client } from "@x402/core/client";
@@ -25,23 +28,45 @@ const svmSigner = await createKeyPairSignerFromBytes(
base58.decode(process.env.SVM_PRIVATE_KEY as string),
);
+const evmNetwork = process.env.EVM_NETWORK || "eip155:84532";
+const evmRpcUrl = process.env.EVM_RPC_URL;
+const evmChain = evmNetwork === "eip155:8453" ? base : baseSepolia;
+
const publicClient = createPublicClient({
- chain: baseSepolia,
- transport: http(),
+ chain: evmChain,
+ transport: http(evmRpcUrl),
});
const evmSigner = toClientEvmSigner(evmAccount, publicClient);
+const evmSchemeOptions: ExactEvmSchemeOptions | undefined = process.env.EVM_RPC_URL
+ ? { rpcUrl: process.env.EVM_RPC_URL }
+ : undefined;
+
+const uptoSchemeOptions: UptoEvmSchemeOptions | undefined = process.env.EVM_RPC_URL
+ ? { rpcUrl: process.env.EVM_RPC_URL }
+ : undefined;
+
// Initialize Aptos signer if key is provided
let aptosAccount: Account | undefined;
if (process.env.APTOS_PRIVATE_KEY) {
- const formattedKey = PrivateKey.formatPrivateKey(process.env.APTOS_PRIVATE_KEY, PrivateKeyVariants.Ed25519);
+ const formattedKey = PrivateKey.formatPrivateKey(
+ process.env.APTOS_PRIVATE_KEY,
+ PrivateKeyVariants.Ed25519,
+ );
const aptosPrivateKey = new Ed25519PrivateKey(formattedKey);
aptosAccount = Account.fromPrivateKey({ privateKey: aptosPrivateKey });
}
+// Initialize Stellar signer if key is provided
+let stellarSigner: Ed25519Signer | undefined;
+if (process.env.STELLAR_PRIVATE_KEY) {
+ stellarSigner = createEd25519Signer(process.env.STELLAR_PRIVATE_KEY);
+}
+
const client = new x402Client()
- .register("eip155:*", new ExactEvmScheme(evmSigner))
+ .register("eip155:*", new ExactEvmScheme(evmSigner, evmSchemeOptions))
+ .register("eip155:*", new UptoEvmClientScheme(evmSigner, uptoSchemeOptions))
.registerV1("base-sepolia", new ExactEvmSchemeV1(evmSigner))
.registerV1("base", new ExactEvmSchemeV1(evmSigner))
.register("solana:*", new ExactSvmScheme(svmSigner))
@@ -50,6 +75,9 @@ const client = new x402Client()
if (aptosAccount) {
client.register("aptos:*", new ExactAptosScheme(aptosAccount));
}
+if (stellarSigner) {
+ client.register("stellar:*", new ExactStellarScheme(stellarSigner));
+}
const axiosWithPayment = wrapAxiosWithPayment(axios.create(), client);
diff --git a/e2e/clients/axios/package.json b/e2e/clients/axios/package.json
index 32a92fdf10..6c97ae132c 100644
--- a/e2e/clients/axios/package.json
+++ b/e2e/clients/axios/package.json
@@ -17,6 +17,7 @@
"@x402/axios": "workspace:*",
"@x402/core": "workspace:*",
"@x402/evm": "workspace:*",
+ "@x402/stellar": "workspace:*",
"@x402/svm": "workspace:*",
"axios": "^1.7.9",
"dotenv": "^16.4.7",
diff --git a/e2e/clients/axios/test.config.json b/e2e/clients/axios/test.config.json
index 321b192c54..ff7b7accbf 100644
--- a/e2e/clients/axios/test.config.json
+++ b/e2e/clients/axios/test.config.json
@@ -5,7 +5,8 @@
"protocolFamilies": [
"evm",
"svm",
- "aptos"
+ "aptos",
+ "stellar"
],
"x402Versions": [
1,
@@ -14,7 +15,8 @@
"evm": {
"transferMethods": [
"eip3009",
- "permit2"
+ "permit2",
+ "upto"
]
},
"extensions": [
@@ -29,7 +31,8 @@
"ENDPOINT_PATH"
],
"optional": [
- "APTOS_PRIVATE_KEY"
+ "APTOS_PRIVATE_KEY",
+ "STELLAR_PRIVATE_KEY"
]
}
}
\ No newline at end of file
diff --git a/e2e/clients/fetch/README.md b/e2e/clients/fetch/README.md
index 463fb72b1f..56ba7ef7eb 100644
--- a/e2e/clients/fetch/README.md
+++ b/e2e/clients/fetch/README.md
@@ -1,13 +1,13 @@
# E2E Test Client: TypeScript Fetch
-This client demonstrates and tests the `@x402/fetch` package with both EVM and SVM payment support.
+This client demonstrates and tests the `@x402/fetch` package with EVM, SVM, and Stellar payment support.
## What It Tests
### Core Functionality
- ✅ **V2 Protocol** - Modern x402 protocol with CAIP-2 networks
- ✅ **V1 Protocol** - Legacy x402 protocol with simple network names
-- ✅ **Multi-chain Support** - Both EVM and SVM in a single client
+- ✅ **Multi-chain Support** - EVM, SVM, and (optional) Stellar in a single client
- ✅ **Automatic Payment Handling** - Transparent 402 response handling
- ✅ **Payment Response Decoding** - Extracts settlement information from headers
@@ -16,6 +16,7 @@ This client demonstrates and tests the `@x402/fetch` package with both EVM and S
- ✅ **EVM V1** - `base-sepolia` and `base` networks
- ✅ **SVM V2** - `solana:*` wildcard scheme
- ✅ **SVM V1** - `solana-devnet` and `solana` networks
+- ✅ **Stellar V2** - `stellar:*` wildcard scheme (optional)
## What It Demonstrates
@@ -28,11 +29,13 @@ import { ExactEvmClient } from "@x402/evm";
import { ExactEvmClientV1 } from "@x402/evm/v1";
import { ExactSvmClient } from "@x402/svm";
import { ExactSvmClientV1 } from "@x402/svm/v1";
+import { ExactStellarClient } from "@x402/stellar";
// Build x402 client with direct registration
const client = new x402Client()
.register("eip155:*", new ExactEvmClient(evmAccount))
.register("solana:*", new ExactSvmClient(svmSigner))
+ .register("stellar:*", new ExactStellarClient(stellarSigner))
.registerV1("base-sepolia", new ExactEvmClientV1(evmAccount))
.registerV1("base", new ExactEvmClientV1(evmAccount))
.registerV1("solana-devnet", new ExactSvmClientV1(svmSigner))
@@ -49,7 +52,7 @@ const response = await fetchWithPayment(url, { method: "GET" });
1. **Builder Pattern** - Fluent API for registering multiple schemes
2. **Multi-Version Support** - V1 and V2 protocols side-by-side
-3. **Multi-Chain Support** - EVM and SVM in one client
+3. **Multi-Chain Support** - EVM, SVM, and (optional) Stellar in one client
4. **Network Flexibility** - Wildcards for V2, specific networks for V1
5. **Transparent Payment** - No manual 402 handling needed
@@ -58,8 +61,8 @@ const response = await fetchWithPayment(url, { method: "GET" });
This client is tested against:
- **Servers:** Express (TypeScript), Gin (Go)
- **Facilitators:** TypeScript, Go
-- **Endpoints:** `/protected` (EVM), `/protected-svm` (SVM)
-- **Networks:** Base Sepolia (EVM), Solana Devnet (SVM)
+- **Endpoints:** `/protected` (EVM), `/protected-svm` (SVM), `/protected-stellar` (Stellar)
+- **Networks:** Base Sepolia (EVM), Solana Devnet (SVM), Stellar Testnet (Stellar)
### Success Criteria
- ✅ Request succeeds with 200 status
@@ -80,16 +83,21 @@ export RESOURCE_SERVER_URL="http://localhost:4022"
export ENDPOINT_PATH="/protected"
export EVM_PRIVATE_KEY="0x..."
export SVM_PRIVATE_KEY="..."
+export STELLAR_PRIVATE_KEY="S..." # optional
pnpm start
```
## Environment Variables
+### Required
- `RESOURCE_SERVER_URL` - Server base URL
- `ENDPOINT_PATH` - Path to protected endpoint
- `EVM_PRIVATE_KEY` - Ethereum private key (hex with 0x prefix)
- `SVM_PRIVATE_KEY` - Solana private key (base58 encoded)
+### Optional
+- `STELLAR_PRIVATE_KEY` - Stellar private key (S... format) - enables Stellar support
+
## Output Format
```json
@@ -114,5 +122,6 @@ pnpm start
- `@x402/evm/v1` - EVM payment mechanisms (V1)
- `@x402/svm` - SVM payment mechanisms (V2)
- `@x402/svm/v1` - SVM payment mechanisms (V1)
+- `@x402/stellar` - Stellar payment mechanisms (V2)
- `viem` - Ethereum library for account creation
- `@solana/kit` - Solana keypair utilities
diff --git a/e2e/clients/fetch/index.ts b/e2e/clients/fetch/index.ts
index 9544bb3f68..e88320d3d5 100644
--- a/e2e/clients/fetch/index.ts
+++ b/e2e/clients/fetch/index.ts
@@ -1,15 +1,18 @@
import { config } from "dotenv";
-import { wrapFetchWithPayment, decodePaymentResponseHeader } from "@x402/fetch";
+import { wrapFetchWithPayment } from "@x402/fetch";
import { createPublicClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
-import { baseSepolia } from "viem/chains";
-import { ExactEvmScheme } from "@x402/evm/exact/client";
+import { base, baseSepolia } from "viem/chains";
+import { ExactEvmScheme, type ExactEvmSchemeOptions } from "@x402/evm/exact/client";
+import { UptoEvmScheme as UptoEvmClientScheme, type UptoEvmSchemeOptions } from "@x402/evm/upto/client";
import { ExactEvmSchemeV1 } from "@x402/evm/v1";
import { toClientEvmSigner } from "@x402/evm";
import { ExactSvmScheme } from "@x402/svm/exact/client";
import { ExactSvmSchemeV1 } from "@x402/svm/v1";
import { ExactAptosScheme } from "@x402/aptos/exact/client";
import { Account, Ed25519PrivateKey, PrivateKey, PrivateKeyVariants } from "@aptos-labs/ts-sdk";
+import { ExactStellarScheme } from "@x402/stellar/exact/client";
+import { createEd25519Signer, Ed25519Signer } from "@x402/stellar";
import { base58 } from "@scure/base";
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { x402Client, x402HTTPClient } from "@x402/core/client";
@@ -22,13 +25,25 @@ const url = `${baseURL}${endpointPath}`;
const evmAccount = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const svmSigner = await createKeyPairSignerFromBytes(base58.decode(process.env.SVM_PRIVATE_KEY as string));
+const evmNetwork = process.env.EVM_NETWORK || "eip155:84532";
+const evmRpcUrl = process.env.EVM_RPC_URL;
+const evmChain = evmNetwork === "eip155:8453" ? base : baseSepolia;
+
const publicClient = createPublicClient({
- chain: baseSepolia,
- transport: http(),
+ chain: evmChain,
+ transport: http(evmRpcUrl),
});
const evmSigner = toClientEvmSigner(evmAccount, publicClient);
+const evmSchemeOptions: ExactEvmSchemeOptions | undefined = process.env.EVM_RPC_URL
+ ? { rpcUrl: process.env.EVM_RPC_URL }
+ : undefined;
+
+const uptoSchemeOptions: UptoEvmSchemeOptions | undefined = process.env.EVM_RPC_URL
+ ? { rpcUrl: process.env.EVM_RPC_URL }
+ : undefined;
+
// Initialize Aptos signer if key is provided
let aptosAccount: Account | undefined;
if (process.env.APTOS_PRIVATE_KEY) {
@@ -37,8 +52,15 @@ if (process.env.APTOS_PRIVATE_KEY) {
aptosAccount = Account.fromPrivateKey({ privateKey: aptosPrivateKey });
}
+// Initialize Stellar signer if key is provided
+let stellarSigner: Ed25519Signer | undefined;
+if (process.env.STELLAR_PRIVATE_KEY) {
+ stellarSigner = createEd25519Signer(process.env.STELLAR_PRIVATE_KEY);
+}
+
const client = new x402Client()
- .register("eip155:*", new ExactEvmScheme(evmSigner))
+ .register("eip155:*", new ExactEvmScheme(evmSigner, evmSchemeOptions))
+ .register("eip155:*", new UptoEvmClientScheme(evmSigner, uptoSchemeOptions))
.registerV1("base-sepolia", new ExactEvmSchemeV1(evmSigner))
.registerV1("base", new ExactEvmSchemeV1(evmSigner))
.register("solana:*", new ExactSvmScheme(svmSigner))
@@ -47,6 +69,9 @@ const client = new x402Client()
if (aptosAccount) {
client.register("aptos:*", new ExactAptosScheme(aptosAccount));
}
+if (stellarSigner) {
+ client.register("stellar:*", new ExactStellarScheme(stellarSigner));
+}
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
diff --git a/e2e/clients/fetch/package.json b/e2e/clients/fetch/package.json
index 1a051cc038..ae2c29cc60 100644
--- a/e2e/clients/fetch/package.json
+++ b/e2e/clients/fetch/package.json
@@ -17,6 +17,7 @@
"@x402/core": "workspace:*",
"@x402/evm": "workspace:*",
"@x402/fetch": "workspace:*",
+ "@x402/stellar": "workspace:*",
"@x402/svm": "workspace:*",
"axios": "^1.7.9",
"dotenv": "^16.4.7",
diff --git a/e2e/clients/fetch/test.config.json b/e2e/clients/fetch/test.config.json
index 2c49b460d1..42b9be92a8 100644
--- a/e2e/clients/fetch/test.config.json
+++ b/e2e/clients/fetch/test.config.json
@@ -5,7 +5,8 @@
"protocolFamilies": [
"evm",
"svm",
- "aptos"
+ "aptos",
+ "stellar"
],
"x402Versions": [
1,
@@ -14,7 +15,8 @@
"evm": {
"transferMethods": [
"eip3009",
- "permit2"
+ "permit2",
+ "upto"
]
},
"extensions": [
@@ -29,7 +31,8 @@
"ENDPOINT_PATH"
],
"optional": [
- "APTOS_PRIVATE_KEY"
+ "APTOS_PRIVATE_KEY",
+ "STELLAR_PRIVATE_KEY"
]
}
}
diff --git a/e2e/clients/go-http/go.mod b/e2e/clients/go-http/go.mod
index edae46db65..be39d8a56e 100644
--- a/e2e/clients/go-http/go.mod
+++ b/e2e/clients/go-http/go.mod
@@ -36,7 +36,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -54,11 +54,11 @@ require (
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/ratelimit v0.2.0 // indirect
go.uber.org/zap v1.21.0 // indirect
- golang.org/x/crypto v0.41.0 // indirect
- golang.org/x/sync v0.16.0 // indirect
- golang.org/x/sys v0.36.0 // indirect
- golang.org/x/term v0.34.0 // indirect
- golang.org/x/time v0.9.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/term v0.38.0 // indirect
+ golang.org/x/time v0.14.0 // indirect
)
replace github.com/coinbase/x402/go => ../../../go
diff --git a/e2e/clients/go-http/go.sum b/e2e/clients/go-http/go.sum
index 8235c8df27..f02dcff073 100644
--- a/e2e/clients/go-http/go.sum
+++ b/e2e/clients/go-http/go.sum
@@ -137,9 +137,8 @@ github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzW
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
@@ -227,6 +226,12 @@ github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
@@ -253,8 +258,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -267,13 +272,13 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -284,27 +289,26 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
-golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
+golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
diff --git a/e2e/clients/go-http/main.go b/e2e/clients/go-http/main.go
index c912626ddf..96ef06ab3f 100644
--- a/e2e/clients/go-http/main.go
+++ b/e2e/clients/go-http/main.go
@@ -12,8 +12,9 @@ import (
x402 "github.com/coinbase/x402/go"
x402http "github.com/coinbase/x402/go/http"
- evm "github.com/coinbase/x402/go/mechanisms/evm/exact/client"
- evmv1 "github.com/coinbase/x402/go/mechanisms/evm/exact/v1/client"
+ exactevm "github.com/coinbase/x402/go/mechanisms/evm/exact/client"
+ exactevmv1 "github.com/coinbase/x402/go/mechanisms/evm/exact/v1/client"
+ uptoevm "github.com/coinbase/x402/go/mechanisms/evm/upto/client"
svm "github.com/coinbase/x402/go/mechanisms/svm/exact/client"
svmv1 "github.com/coinbase/x402/go/mechanisms/svm/exact/v1/client"
evmsigners "github.com/coinbase/x402/go/signers/evm"
@@ -74,14 +75,22 @@ func main() {
return
}
- // Create x402 client with fluent API
- // EIP-2612 gas sponsoring is handled internally by the EVM scheme
- // when the server advertises support - no separate extension registration needed.
+ var evmConfig *exactevm.ExactEvmSchemeConfig
+ if evmRpcURL != "" {
+ evmConfig = &exactevm.ExactEvmSchemeConfig{RPCURL: evmRpcURL}
+ }
+
+ var uptoConfig *uptoevm.UptoEvmSchemeConfig
+ if evmRpcURL != "" {
+ uptoConfig = &uptoevm.UptoEvmSchemeConfig{RPCURL: evmRpcURL}
+ }
+
x402Client := x402.Newx402Client().
- Register("eip155:*", evm.NewExactEvmScheme(evmSigner)).
+ Register("eip155:*", exactevm.NewExactEvmScheme(evmSigner, evmConfig)).
+ Register("eip155:*", uptoevm.NewUptoEvmScheme(evmSigner, uptoConfig)).
Register("solana:*", svm.NewExactSvmScheme(svmSigner)).
- RegisterV1("base-sepolia", evmv1.NewExactEvmSchemeV1(evmSigner)).
- RegisterV1("base", evmv1.NewExactEvmSchemeV1(evmSigner)).
+ RegisterV1("base-sepolia", exactevmv1.NewExactEvmSchemeV1(evmSigner)).
+ RegisterV1("base", exactevmv1.NewExactEvmSchemeV1(evmSigner)).
RegisterV1("solana-devnet", svmv1.NewExactSvmSchemeV1(svmSigner)).
RegisterV1("solana", svmv1.NewExactSvmSchemeV1(svmSigner))
diff --git a/e2e/clients/go-http/test.config.json b/e2e/clients/go-http/test.config.json
index 8c6b600592..07d5f8cb4d 100644
--- a/e2e/clients/go-http/test.config.json
+++ b/e2e/clients/go-http/test.config.json
@@ -10,10 +10,12 @@
1,
2
],
+ "schemes": ["exact", "upto"],
"evm": {
"transferMethods": [
"eip3009",
- "permit2"
+ "permit2",
+ "upto"
]
},
"extensions": [
diff --git a/e2e/clients/httpx/build.sh b/e2e/clients/httpx/build.sh
index f5fbe5e8f8..c1bb071bbe 100755
--- a/e2e/clients/httpx/build.sh
+++ b/e2e/clients/httpx/build.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Python doesn't require a build step
-# This file is intentionally empty
-exit 0
+set -e
+# Rebuild the local x402 editable dependency so the venv reflects source changes
+uv sync --reinstall-package x402
diff --git a/e2e/clients/httpx/main.py b/e2e/clients/httpx/main.py
index fdf976316a..424f0530ae 100644
--- a/e2e/clients/httpx/main.py
+++ b/e2e/clients/httpx/main.py
@@ -1,16 +1,20 @@
"""httpx e2e test client using x402 v2 SDK."""
+import logging
import os
import json
import asyncio
from dotenv import load_dotenv
from eth_account import Account
-# Import from new x402 package
+logging.basicConfig(level=logging.INFO, format="%(name)s %(levelname)s: %(message)s", stream=__import__('sys').stderr)
+logging.getLogger("x402.signers").setLevel(logging.DEBUG)
+logging.getLogger("x402.permit2").setLevel(logging.DEBUG)
+
from x402 import x402Client
from x402.http import decode_payment_response_header
from x402.http.clients import x402_httpx_transport
-from x402.mechanisms.evm import EthAccountSigner
+from x402.mechanisms.evm import EthAccountSignerWithRPC
from x402.mechanisms.evm.exact import register_exact_evm_client
from x402.mechanisms.svm import KeypairSigner
from x402.mechanisms.svm.exact import register_exact_svm_client
@@ -22,6 +26,7 @@
# Get environment variables
evm_private_key = os.getenv("EVM_PRIVATE_KEY")
svm_private_key = os.getenv("SVM_PRIVATE_KEY")
+evm_rpc_url = os.getenv("EVM_RPC_URL", "https://sepolia.base.org")
base_url = os.getenv("RESOURCE_SERVER_URL")
endpoint_path = os.getenv("ENDPOINT_PATH")
@@ -45,8 +50,8 @@ async def main():
# Register EVM exact scheme if private key is available
if evm_private_key:
- account = Account.from_key(evm_private_key)
- evm_signer = EthAccountSigner(account)
+ evm_account = Account.from_key(evm_private_key)
+ evm_signer = EthAccountSignerWithRPC(evm_account, rpc_url=evm_rpc_url)
register_exact_evm_client(client, evm_signer)
# Register SVM exact scheme if private key is available
diff --git a/e2e/clients/httpx/run.sh b/e2e/clients/httpx/run.sh
index 31c1f93486..c653d856a9 100755
--- a/e2e/clients/httpx/run.sh
+++ b/e2e/clients/httpx/run.sh
@@ -1,4 +1,3 @@
#!/bin/bash
-# Ensure dependencies are synced before running
-uv sync --quiet
+uv sync --reinstall-package x402 --quiet
uv run python main.py
diff --git a/e2e/clients/httpx/test.config.json b/e2e/clients/httpx/test.config.json
index fcdeb61f96..26fecdf809 100644
--- a/e2e/clients/httpx/test.config.json
+++ b/e2e/clients/httpx/test.config.json
@@ -11,8 +11,12 @@
2
],
"evm": {
- "transferMethods": ["eip3009"]
+ "transferMethods": ["eip3009", "permit2"]
},
+ "extensions": [
+ "eip2612GasSponsoring",
+ "erc20ApprovalGasSponsoring"
+ ],
"description": "Python httpx client with x402 v2 payment hooks",
"environment": {
"required": [
@@ -21,7 +25,8 @@
],
"optional": [
"EVM_PRIVATE_KEY",
- "SVM_PRIVATE_KEY"
+ "SVM_PRIVATE_KEY",
+ "EVM_RPC_URL"
]
}
-}
\ No newline at end of file
+}
diff --git a/e2e/clients/httpx/uv.lock b/e2e/clients/httpx/uv.lock
index 2121a54895..1d634e6156 100644
--- a/e2e/clients/httpx/uv.lock
+++ b/e2e/clients/httpx/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -1907,7 +1907,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.2.0"
+version = "2.5.0"
source = { editable = "../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
@@ -1937,7 +1937,7 @@ svm = [
[package.metadata]
requires-dist = [
{ name = "eth-abi", marker = "extra == 'evm'", specifier = ">=5.0.0" },
- { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.12.0" },
+ { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.13.0" },
{ name = "eth-keys", marker = "extra == 'evm'", specifier = ">=0.5.0" },
{ name = "eth-utils", marker = "extra == 'evm'", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], marker = "extra == 'fastapi'", specifier = ">=0.115.0" },
@@ -1964,7 +1964,7 @@ provides-extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp",
dev = [
{ name = "black", specifier = ">=23.0.0" },
{ name = "eth-abi", specifier = ">=5.0.0" },
- { name = "eth-account", specifier = ">=0.12.0" },
+ { name = "eth-account", specifier = ">=0.13.0" },
{ name = "eth-keys", specifier = ">=0.5.0" },
{ name = "eth-utils", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
diff --git a/e2e/clients/mcp-go/go.mod b/e2e/clients/mcp-go/go.mod
index c439a05d14..3899ac9b0c 100644
--- a/e2e/clients/mcp-go/go.mod
+++ b/e2e/clients/mcp-go/go.mod
@@ -10,23 +10,31 @@ require (
)
require (
+ github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
+ github.com/StackExchange/wmi v1.2.1 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/consensys/gnark-crypto v0.18.0 // indirect
github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
+ github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
github.com/ethereum/go-ethereum v1.16.7 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
+ github.com/go-ole/go-ole v1.3.0 // indirect
github.com/google/jsonschema-go v0.4.2 // indirect
+ github.com/gorilla/websocket v1.4.2 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
+ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
+ github.com/tklauser/numcpus v0.6.1 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
- golang.org/x/crypto v0.41.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
- golang.org/x/sync v0.16.0 // indirect
- golang.org/x/sys v0.36.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
)
replace github.com/coinbase/x402/go => ../../../go
diff --git a/e2e/clients/mcp-go/go.sum b/e2e/clients/mcp-go/go.sum
index f897f19fa1..917e66bca7 100644
--- a/e2e/clients/mcp-go/go.sum
+++ b/e2e/clients/mcp-go/go.sum
@@ -1,17 +1,45 @@
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
+github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
+github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
+github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
+github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
+github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
+github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
+github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
+github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
+github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
+github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
+github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
@@ -20,18 +48,29 @@ github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
+github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
+github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ=
github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
+github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
+github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
+github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -40,50 +79,122 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
+github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
+github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330=
+github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg=
+github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
+github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
+github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
+github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
+github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
+github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
+github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
+github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/modelcontextprotocol/go-sdk v1.3.0 h1:gMfZkv3DzQF5q/DcQePo5rahEY+sguyPfXDfNBcT0Zs=
github.com/modelcontextprotocol/go-sdk v1.3.0/go.mod h1:AnQ//Qc6+4nIyyrB4cxBU7UW9VibK4iOZBeyP/rF1IE=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
+github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
+github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
+github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
+github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
+github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
+github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
+github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
+github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
+github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
+github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
+github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
+github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
+github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
+github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
-golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
+google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
+google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/e2e/clients/mcp-go/main.go b/e2e/clients/mcp-go/main.go
index 52cd2a41b2..4befd7e31d 100644
--- a/e2e/clients/mcp-go/main.go
+++ b/e2e/clients/mcp-go/main.go
@@ -69,9 +69,13 @@ func main() {
}
defer session.Close()
- // Use X402MCPClient - payment is transparent in CallTool
+ var evmConfig *evm.ExactEvmSchemeConfig
+ if rpcURL := os.Getenv("EVM_RPC_URL"); rpcURL != "" {
+ evmConfig = &evm.ExactEvmSchemeConfig{RPCURL: rpcURL}
+ }
+
paymentClient := x402.Newx402Client()
- paymentClient.Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ paymentClient.Register("eip155:*", evm.NewExactEvmScheme(evmSigner, evmConfig))
x402Mcp := mcp402.NewX402MCPClient(session, paymentClient, mcp402.Options{AutoPayment: mcp402.BoolPtr(true)})
result, err := x402Mcp.CallTool(ctx, endpointPath, map[string]any{
diff --git a/e2e/clients/mcp-python/uv.lock b/e2e/clients/mcp-python/uv.lock
index a1964f57e4..d4768a6ad5 100644
--- a/e2e/clients/mcp-python/uv.lock
+++ b/e2e/clients/mcp-python/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -2137,7 +2137,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.1.0"
+version = "2.5.0"
source = { editable = "../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
@@ -2160,7 +2160,7 @@ mcp = [
[package.metadata]
requires-dist = [
{ name = "eth-abi", marker = "extra == 'evm'", specifier = ">=5.0.0" },
- { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.12.0" },
+ { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.13.0" },
{ name = "eth-keys", marker = "extra == 'evm'", specifier = ">=0.5.0" },
{ name = "eth-utils", marker = "extra == 'evm'", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], marker = "extra == 'fastapi'", specifier = ">=0.115.0" },
@@ -2187,7 +2187,7 @@ provides-extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp",
dev = [
{ name = "black", specifier = ">=23.0.0" },
{ name = "eth-abi", specifier = ">=5.0.0" },
- { name = "eth-account", specifier = ">=0.12.0" },
+ { name = "eth-account", specifier = ">=0.13.0" },
{ name = "eth-keys", specifier = ">=0.5.0" },
{ name = "eth-utils", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
diff --git a/e2e/clients/mcp-typescript/index.ts b/e2e/clients/mcp-typescript/index.ts
index 10d2d2c35b..9029b796a5 100644
--- a/e2e/clients/mcp-typescript/index.ts
+++ b/e2e/clients/mcp-typescript/index.ts
@@ -8,7 +8,7 @@
*/
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
-import { ExactEvmScheme } from "@x402/evm/exact/client";
+import { ExactEvmScheme, type ExactEvmSchemeOptions } from "@x402/evm/exact/client";
import { createx402MCPClient } from "@x402/mcp";
import { privateKeyToAccount } from "viem/accounts";
@@ -35,12 +35,14 @@ if (!serverUrl || !endpointPath || !evmPrivateKey) {
async function main(): Promise {
const evmSigner = privateKeyToAccount(evmPrivateKey);
+ const evmSchemeOptions: ExactEvmSchemeOptions | undefined = process.env.EVM_RPC_URL
+ ? { rpcUrl: process.env.EVM_RPC_URL }
+ : undefined;
- // Create x402 MCP client with auto-payment enabled
const x402Mcp = createx402MCPClient({
name: "x402-mcp-e2e-client",
version: "1.0.0",
- schemes: [{ network: "eip155:84532", client: new ExactEvmScheme(evmSigner) }],
+ schemes: [{ network: "eip155:84532", client: new ExactEvmScheme(evmSigner, evmSchemeOptions) }],
autoPayment: true,
onPaymentRequested: async () => true, // Auto-approve all payments for e2e
});
diff --git a/e2e/clients/requests/build.sh b/e2e/clients/requests/build.sh
index f5fbe5e8f8..c1bb071bbe 100755
--- a/e2e/clients/requests/build.sh
+++ b/e2e/clients/requests/build.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Python doesn't require a build step
-# This file is intentionally empty
-exit 0
+set -e
+# Rebuild the local x402 editable dependency so the venv reflects source changes
+uv sync --reinstall-package x402
diff --git a/e2e/clients/requests/main.py b/e2e/clients/requests/main.py
index e1bcef0b6b..c5eb3b291d 100644
--- a/e2e/clients/requests/main.py
+++ b/e2e/clients/requests/main.py
@@ -5,11 +5,10 @@
from dotenv import load_dotenv
from eth_account import Account
-# Import from new x402 package (sync variant for requests)
from x402 import x402ClientSync
from x402.http import decode_payment_response_header
from x402.http.clients import x402_requests
-from x402.mechanisms.evm import EthAccountSigner
+from x402.mechanisms.evm import EthAccountSignerWithRPC
from x402.mechanisms.evm.exact import register_exact_evm_client
from x402.mechanisms.svm import KeypairSigner
from x402.mechanisms.svm.exact import register_exact_svm_client
@@ -20,6 +19,7 @@
# Get environment variables
evm_private_key = os.getenv("EVM_PRIVATE_KEY")
svm_private_key = os.getenv("SVM_PRIVATE_KEY")
+evm_rpc_url = os.getenv("EVM_RPC_URL", "https://sepolia.base.org")
base_url = os.getenv("RESOURCE_SERVER_URL")
endpoint_path = os.getenv("ENDPOINT_PATH")
@@ -43,8 +43,8 @@ def main():
# Register EVM exact scheme if private key is available
if evm_private_key:
- account = Account.from_key(evm_private_key)
- evm_signer = EthAccountSigner(account)
+ evm_account = Account.from_key(evm_private_key)
+ evm_signer = EthAccountSignerWithRPC(evm_account, rpc_url=evm_rpc_url)
register_exact_evm_client(client, evm_signer)
# Register SVM exact scheme if private key is available
diff --git a/e2e/clients/requests/run.sh b/e2e/clients/requests/run.sh
index 31c1f93486..c653d856a9 100644
--- a/e2e/clients/requests/run.sh
+++ b/e2e/clients/requests/run.sh
@@ -1,4 +1,3 @@
#!/bin/bash
-# Ensure dependencies are synced before running
-uv sync --quiet
+uv sync --reinstall-package x402 --quiet
uv run python main.py
diff --git a/e2e/clients/requests/test.config.json b/e2e/clients/requests/test.config.json
index 009e3eb0d1..783ba8467f 100644
--- a/e2e/clients/requests/test.config.json
+++ b/e2e/clients/requests/test.config.json
@@ -11,8 +11,12 @@
2
],
"evm": {
- "transferMethods": ["eip3009"]
+ "transferMethods": ["eip3009", "permit2"]
},
+ "extensions": [
+ "eip2612GasSponsoring",
+ "erc20ApprovalGasSponsoring"
+ ],
"description": "Python requests client with x402 v2 HTTP adapter",
"environment": {
"required": [
@@ -21,7 +25,8 @@
],
"optional": [
"EVM_PRIVATE_KEY",
- "SVM_PRIVATE_KEY"
+ "SVM_PRIVATE_KEY",
+ "EVM_RPC_URL"
]
}
-}
\ No newline at end of file
+}
diff --git a/e2e/clients/requests/uv.lock b/e2e/clients/requests/uv.lock
index 11e3e49ebb..3fa9f0e1c2 100644
--- a/e2e/clients/requests/uv.lock
+++ b/e2e/clients/requests/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -1907,7 +1907,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.2.0"
+version = "2.5.0"
source = { editable = "../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
@@ -1937,7 +1937,7 @@ svm = [
[package.metadata]
requires-dist = [
{ name = "eth-abi", marker = "extra == 'evm'", specifier = ">=5.0.0" },
- { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.12.0" },
+ { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.13.0" },
{ name = "eth-keys", marker = "extra == 'evm'", specifier = ">=0.5.0" },
{ name = "eth-utils", marker = "extra == 'evm'", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], marker = "extra == 'fastapi'", specifier = ">=0.115.0" },
@@ -1964,7 +1964,7 @@ provides-extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp",
dev = [
{ name = "black", specifier = ">=23.0.0" },
{ name = "eth-abi", specifier = ">=5.0.0" },
- { name = "eth-account", specifier = ">=0.12.0" },
+ { name = "eth-account", specifier = ">=0.13.0" },
{ name = "eth-keys", specifier = ">=0.5.0" },
{ name = "eth-utils", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
diff --git a/e2e/facilitators/go/bazaar.go b/e2e/facilitators/go/bazaar.go
index a0b4313ee2..45d9e762b7 100644
--- a/e2e/facilitators/go/bazaar.go
+++ b/e2e/facilitators/go/bazaar.go
@@ -15,6 +15,7 @@ type DiscoveredResource struct {
X402Version int `json:"x402Version"`
Accepts []x402.PaymentRequirements `json:"accepts"`
DiscoveryInfo *exttypes.DiscoveryInfo `json:"discoveryInfo,omitempty"`
+ RouteTemplate string `json:"routeTemplate,omitempty"`
LastUpdated string `json:"lastUpdated"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
@@ -37,10 +38,14 @@ func (c *BazaarCatalog) CatalogResource(
x402Version int,
discoveryInfo *exttypes.DiscoveryInfo,
paymentRequirements x402.PaymentRequirements,
+ routeTemplate string,
) {
log.Printf("📝 Discovered resource: %s", resourceURL)
log.Printf(" Method: %s", method)
log.Printf(" x402 Version: %d", x402Version)
+ if routeTemplate != "" {
+ log.Printf(" Route template: %s", routeTemplate)
+ }
c.mutex.Lock()
defer c.mutex.Unlock()
@@ -51,6 +56,7 @@ func (c *BazaarCatalog) CatalogResource(
X402Version: x402Version,
Accepts: []x402.PaymentRequirements{paymentRequirements},
DiscoveryInfo: discoveryInfo,
+ RouteTemplate: routeTemplate,
LastUpdated: time.Now().Format(time.RFC3339),
Metadata: make(map[string]interface{}),
}
diff --git a/e2e/facilitators/go/go.mod b/e2e/facilitators/go/go.mod
index 889ac08171..439612494a 100644
--- a/e2e/facilitators/go/go.mod
+++ b/e2e/facilitators/go/go.mod
@@ -49,7 +49,7 @@ require (
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -75,16 +75,16 @@ require (
go.uber.org/ratelimit v0.2.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/arch v0.20.0 // indirect
- golang.org/x/crypto v0.41.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
- golang.org/x/mod v0.27.0 // indirect
- golang.org/x/net v0.43.0 // indirect
- golang.org/x/sync v0.16.0 // indirect
- golang.org/x/sys v0.36.0 // indirect
- golang.org/x/term v0.34.0 // indirect
- golang.org/x/text v0.28.0 // indirect
- golang.org/x/time v0.9.0 // indirect
- golang.org/x/tools v0.36.0 // indirect
+ golang.org/x/mod v0.30.0 // indirect
+ golang.org/x/net v0.48.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/term v0.38.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
+ golang.org/x/time v0.14.0 // indirect
+ golang.org/x/tools v0.39.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
)
diff --git a/e2e/facilitators/go/go.sum b/e2e/facilitators/go/go.sum
index da343d1029..0fca838fd6 100644
--- a/e2e/facilitators/go/go.sum
+++ b/e2e/facilitators/go/go.sum
@@ -161,9 +161,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
@@ -299,15 +298,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
-golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
+golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -315,13 +314,13 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -332,34 +331,33 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
-golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
+golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/e2e/facilitators/go/main.go b/e2e/facilitators/go/main.go
index 99ed985563..b9d7ad2f59 100644
--- a/e2e/facilitators/go/main.go
+++ b/e2e/facilitators/go/main.go
@@ -22,8 +22,9 @@ import (
"github.com/coinbase/x402/go/extensions/erc20approvalgassponsor"
exttypes "github.com/coinbase/x402/go/extensions/types"
evmmech "github.com/coinbase/x402/go/mechanisms/evm"
- evm "github.com/coinbase/x402/go/mechanisms/evm/exact/facilitator"
- evmv1 "github.com/coinbase/x402/go/mechanisms/evm/exact/v1/facilitator"
+ exactevm "github.com/coinbase/x402/go/mechanisms/evm/exact/facilitator"
+ exactevmv1 "github.com/coinbase/x402/go/mechanisms/evm/exact/v1/facilitator"
+ uptoevm "github.com/coinbase/x402/go/mechanisms/evm/upto/facilitator"
svmmech "github.com/coinbase/x402/go/mechanisms/svm"
svm "github.com/coinbase/x402/go/mechanisms/svm/exact/facilitator"
svmv1 "github.com/coinbase/x402/go/mechanisms/svm/exact/v1/facilitator"
@@ -42,11 +43,6 @@ import (
"github.com/gin-gonic/gin"
)
-// NOTE: Facilitator signer helpers (go/signers/evm and go/signers/svm) are not yet implemented.
-// When available, this will reduce 300+ lines of facilitator signer code to just a few lines.
-// For now, facilitator signers still require manual implementation.
-// See PROPOSAL_SIGNER_HELPERS.md for the planned facilitator signer helpers.
-
const (
DefaultPort = "4022"
)
@@ -212,56 +208,37 @@ func (s *realFacilitatorEvmSigner) ReadContract(
return nil, fmt.Errorf("failed to parse ABI: %w", err)
}
+ methodObj, exists := contractABI.Methods[method]
+ if !exists {
+ return nil, fmt.Errorf("method %s not found in ABI", method)
+ }
+
// Pack the method call
data, err := contractABI.Pack(method, args...)
if err != nil {
return nil, fmt.Errorf("failed to pack method call: %w", err)
}
- // Make the call
+ // Set From to the facilitator address — required by the upto proxy which enforces
+ // msg.sender == witness.facilitator in settle().
to := common.HexToAddress(contractAddress)
-
- // Check if contract exists at this address
- code, err := s.client.CodeAt(ctx, to, nil)
- if err != nil {
- log.Printf("Failed to check contract code: contract=%s, error=%v", contractAddress, err)
- } else if len(code) == 0 {
- log.Printf("WARNING: No contract code at address %s", contractAddress)
- }
-
msg := ethereum.CallMsg{
+ From: s.address,
To: &to,
Data: data,
}
result, err := s.client.CallContract(ctx, msg, nil)
if err != nil {
- log.Printf("Contract call failed: method=%s, contract=%s, error=%v", method, contractAddress, err)
return nil, fmt.Errorf("failed to call contract: %w", err)
}
- log.Printf("Contract call: method=%s, contract=%s, dataLen=%d, resultLen=%d, result=%x", method, contractAddress, len(data), len(result), result)
-
- // Handle empty result (some contract calls return nothing or revert)
- if len(result) == 0 {
- // For authorizationState, empty means false (nonce not used)
- if method == "authorizationState" {
- return false, nil
- }
- // For balanceOf or allowance, empty might mean 0
- if method == "balanceOf" || method == "allowance" {
- return big.NewInt(0), nil
- }
- return nil, fmt.Errorf("empty result from contract call")
+ if len(methodObj.Outputs) == 0 {
+ return nil, nil
}
// Unpack the result based on method
- method_obj, exists := contractABI.Methods[method]
- if !exists {
- return nil, fmt.Errorf("method %s not found in ABI", method)
- }
-
- output, err := method_obj.Outputs.Unpack(result)
+ output, err := methodObj.Outputs.Unpack(result)
if err != nil {
return nil, fmt.Errorf("failed to unpack result: %w", err)
}
@@ -429,24 +406,106 @@ func (s *realFacilitatorEvmSigner) GetCode(ctx context.Context, address string)
return code, nil
}
-func (s *realFacilitatorEvmSigner) SendRawTransaction(ctx context.Context, signedTx string) (string, error) {
- txBytes, err := hexutil.Decode(signedTx)
+func (s *realFacilitatorEvmSigner) decodeRawTransaction(serialized string) (*types.Transaction, error) {
+ txBytes, err := hexutil.Decode(serialized)
if err != nil {
- return "", fmt.Errorf("failed to decode signed transaction: %w", err)
+ return nil, fmt.Errorf("failed to decode signed transaction: %w", err)
}
-
tx := new(types.Transaction)
if err := tx.UnmarshalBinary(txBytes); err != nil {
- return "", fmt.Errorf("failed to unmarshal transaction: %w", err)
+ return nil, fmt.Errorf("failed to unmarshal transaction: %w", err)
}
+ return tx, nil
+}
+func (s *realFacilitatorEvmSigner) sendRawTransaction(ctx context.Context, tx *types.Transaction) (string, error) {
if err := s.client.SendTransaction(ctx, tx); err != nil {
return "", fmt.Errorf("failed to send raw transaction: %w", err)
}
-
return tx.Hash().Hex(), nil
}
+func (s *realFacilitatorEvmSigner) fundPayerGasIfNeeded(ctx context.Context, decodedTx *types.Transaction) error {
+ chainSigner := types.LatestSignerForChainID(s.chainID)
+ payerAddr, err := types.Sender(chainSigner, decodedTx)
+ if err != nil {
+ return fmt.Errorf("failed to recover sender: %w", err)
+ }
+
+ gasFeeCap := decodedTx.GasFeeCap()
+ if gasFeeCap == nil {
+ gasFeeCap = decodedTx.GasPrice()
+ }
+ gasCost := new(big.Int).Mul(new(big.Int).SetUint64(decodedTx.Gas()), gasFeeCap)
+
+ payerBalance, err := s.client.BalanceAt(ctx, payerAddr, nil)
+ if err != nil {
+ return fmt.Errorf("failed to get payer balance: %w", err)
+ }
+ if payerBalance.Cmp(gasCost) >= 0 {
+ return nil
+ }
+
+ deficit := new(big.Int).Sub(gasCost, payerBalance)
+ log.Printf("⛽ Funding payer %s with %s wei for gas", payerAddr.Hex(), deficit.String())
+
+ fundNonce, err := s.client.PendingNonceAt(ctx, s.address)
+ if err != nil {
+ return fmt.Errorf("failed to get funding nonce: %w", err)
+ }
+ fundGasPrice, err := s.client.SuggestGasPrice(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to get gas price: %w", err)
+ }
+
+ fundTx := types.NewTransaction(fundNonce, payerAddr, deficit, 21000, fundGasPrice, nil)
+ signedFundTx, err := types.SignTx(fundTx, chainSigner, s.privateKey)
+ if err != nil {
+ return fmt.Errorf("failed to sign funding tx: %w", err)
+ }
+ if err := s.client.SendTransaction(ctx, signedFundTx); err != nil {
+ return fmt.Errorf("failed to send funding tx: %w", err)
+ }
+
+ fundReceipt, err := s.WaitForTransactionReceipt(ctx, signedFundTx.Hash().Hex())
+ if err != nil || fundReceipt.Status != evmmech.TxStatusSuccess {
+ return fmt.Errorf("gas funding failed: %s", signedFundTx.Hash().Hex())
+ }
+ log.Printf("⛽ Gas funding confirmed: %s", signedFundTx.Hash().Hex())
+ return nil
+}
+
+func (s *realFacilitatorEvmSigner) SendTransactions(ctx context.Context, transactions []erc20approvalgassponsor.TransactionRequest) ([]string, error) {
+ var hashes []string
+ for _, tx := range transactions {
+ var hash string
+ var err error
+ if tx.Serialized != "" {
+ decodedTx, decErr := s.decodeRawTransaction(tx.Serialized)
+ if decErr != nil {
+ return hashes, fmt.Errorf("transaction_failed: %w", decErr)
+ }
+ if fundErr := s.fundPayerGasIfNeeded(ctx, decodedTx); fundErr != nil {
+ return hashes, fmt.Errorf("transaction_failed: %w", fundErr)
+ }
+ hash, err = s.sendRawTransaction(ctx, decodedTx)
+ } else if tx.Call != nil {
+ hash, err = s.WriteContract(ctx, tx.Call.Address, tx.Call.ABI, tx.Call.Function, tx.Call.Args...)
+ } else {
+ return hashes, fmt.Errorf("transaction_failed: empty transaction request")
+ }
+ if err != nil {
+ return hashes, fmt.Errorf("transaction_failed: %w", err)
+ }
+ receipt, err := s.WaitForTransactionReceipt(ctx, hash)
+ if err != nil || receipt.Status != evmmech.TxStatusSuccess {
+ return hashes, fmt.Errorf("transaction_failed: %s", hash)
+ }
+ hashes = append(hashes, hash)
+ }
+ return hashes, nil
+}
+
// Helper functions for type conversion
func getStringFromInterface(v interface{}) string {
if v == nil {
@@ -491,6 +550,11 @@ func createPaymentHash(paymentPayload x402.PaymentPayload) string {
return hex.EncodeToString(hash[:])
}
+func hashBytes(data []byte) string {
+ hash := sha256.Sum256(data)
+ return hex.EncodeToString(hash[:])
+}
+
// Real SVM facilitator signer
type realFacilitatorSvmSigner struct {
privateKey solana.PrivateKey
@@ -773,16 +837,20 @@ func main() {
// Register EVM schemes with dynamic network
// Enable smart wallet deployment via EIP-6492
- evmConfig := &evm.ExactEvmSchemeConfig{
+ evmConfig := &exactevm.ExactEvmSchemeConfig{
DeployERC4337WithEIP6492: true,
}
- evmFacilitatorScheme := evm.NewExactEvmScheme(evmSigner, evmConfig)
+ evmFacilitatorScheme := exactevm.NewExactEvmScheme(evmSigner, evmConfig)
facilitator.Register([]x402.Network{x402.Network(evmNetwork)}, evmFacilitatorScheme)
- evmV1Config := &evmv1.ExactEvmSchemeV1Config{
+ // Register upto EVM scheme
+ uptoEvmFacilitatorScheme := uptoevm.NewUptoEvmScheme(evmSigner, nil)
+ facilitator.Register([]x402.Network{x402.Network(evmNetwork)}, uptoEvmFacilitatorScheme)
+
+ evmV1Config := &exactevmv1.ExactEvmSchemeV1Config{
DeployERC4337WithEIP6492: true,
}
- evmFacilitatorV1Scheme := evmv1.NewExactEvmSchemeV1(evmSigner, evmV1Config)
+ evmFacilitatorV1Scheme := exactevmv1.NewExactEvmSchemeV1(evmSigner, evmV1Config)
facilitator.RegisterV1([]x402.Network{x402.Network(getV1EvmNetwork(evmNetwork))}, evmFacilitatorV1Scheme)
// Register SVM schemes with dynamic network
@@ -807,11 +875,7 @@ func main() {
OnAfterVerify(func(ctx x402.FacilitatorVerifyResultContext) error {
// Hook 1: Track verified payment for verify→settle flow validation
if ctx.Result.IsValid {
- // Hooks now use view interfaces - create hash from payload view
- paymentHash := fmt.Sprintf("v%d-%s-%s",
- ctx.Payload.GetVersion(),
- ctx.Payload.GetScheme(),
- ctx.Payload.GetNetwork())
+ paymentHash := hashBytes(ctx.PayloadBytes)
verificationMutex.Lock()
verifiedPayments[paymentHash] = time.Now().Unix()
verificationMutex.Unlock()
@@ -840,6 +904,7 @@ func main() {
version,
discovered.DiscoveryInfo,
requirements,
+ discovered.RouteTemplate,
)
}
} else if version == 1 {
@@ -861,6 +926,7 @@ func main() {
version,
discovered.DiscoveryInfo,
requirements,
+ discovered.RouteTemplate,
)
}
}
@@ -870,10 +936,7 @@ func main() {
}).
OnBeforeSettle(func(ctx x402.FacilitatorSettleContext) (*x402.FacilitatorBeforeHookResult, error) {
// Hook 3: Validate payment was previously verified
- paymentHash := fmt.Sprintf("v%d-%s-%s",
- ctx.Payload.GetVersion(),
- ctx.Payload.GetScheme(),
- ctx.Payload.GetNetwork())
+ paymentHash := hashBytes(ctx.PayloadBytes)
verificationMutex.RLock()
verificationTimestamp, verified := verifiedPayments[paymentHash]
verificationMutex.RUnlock()
@@ -902,10 +965,7 @@ func main() {
}).
OnAfterSettle(func(ctx x402.FacilitatorSettleResultContext) error {
// Hook 4: Clean up verified payment tracking after successful settlement
- paymentHash := fmt.Sprintf("v%d-%s-%s",
- ctx.Payload.GetVersion(),
- ctx.Payload.GetScheme(),
- ctx.Payload.GetNetwork())
+ paymentHash := hashBytes(ctx.PayloadBytes)
verificationMutex.Lock()
delete(verifiedPayments, paymentHash)
verificationMutex.Unlock()
@@ -917,10 +977,7 @@ func main() {
}).
OnSettleFailure(func(ctx x402.FacilitatorSettleFailureContext) (*x402.FacilitatorSettleFailureHookResult, error) {
// Hook 5: Clean up verified payment tracking on failure too
- paymentHash := fmt.Sprintf("v%d-%s-%s",
- ctx.Payload.GetVersion(),
- ctx.Payload.GetScheme(),
- ctx.Payload.GetNetwork())
+ paymentHash := hashBytes(ctx.PayloadBytes)
verificationMutex.Lock()
delete(verifiedPayments, paymentHash)
verificationMutex.Unlock()
@@ -937,12 +994,6 @@ func main() {
// POST /verify - Verify a payment against requirements
// Note: Payment tracking and bazaar discovery are handled by lifecycle hooks
router.POST("/verify", func(c *gin.Context) {
- // First, peek at the version to determine which struct to use
- var versionCheck struct {
- X402Version int `json:"x402Version"`
- }
-
- // Read body into buffer so we can parse it twice
bodyBytes, err := c.GetRawData()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
@@ -951,14 +1002,6 @@ func main() {
return
}
- // Parse version
- if err := json.Unmarshal(bodyBytes, &versionCheck); err != nil {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": fmt.Sprintf("Failed to parse version: %v", err),
- })
- return
- }
-
var req VerifyRequest
if err := json.Unmarshal(bodyBytes, &req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
@@ -992,12 +1035,6 @@ func main() {
// POST /settle - Settle a payment on-chain
// Note: Verification validation and cleanup are handled by lifecycle hooks
router.POST("/settle", func(c *gin.Context) {
- // First, peek at the version to determine which struct to use
- var versionCheck struct {
- X402Version int `json:"x402Version"`
- }
-
- // Read body into buffer so we can parse it twice
bodyBytes, err := c.GetRawData()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
@@ -1006,17 +1043,6 @@ func main() {
return
}
- // Debug: Log raw request body
- log.Printf("🔍 [FACILITATOR SETTLE] Received raw body: %s", string(bodyBytes))
-
- // Parse version
- if err := json.Unmarshal(bodyBytes, &versionCheck); err != nil {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": fmt.Sprintf("Failed to parse version: %v", err),
- })
- return
- }
-
var req SettleRequest
if err := json.Unmarshal(bodyBytes, &req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
@@ -1033,9 +1059,6 @@ func main() {
[]byte(req.PaymentRequirements),
)
- // Debug: Log response
- log.Printf("🔍 [FACILITATOR SETTLE] Response: %+v", response)
- log.Printf("🔍 [FACILITATOR SETTLE] Error: %v", err)
if err != nil {
log.Printf("Settle error: %v", err)
diff --git a/e2e/facilitators/go/test.config.json b/e2e/facilitators/go/test.config.json
index c549d55ae1..55ec7fc5f7 100644
--- a/e2e/facilitators/go/test.config.json
+++ b/e2e/facilitators/go/test.config.json
@@ -15,6 +15,7 @@
"eip2612GasSponsoring",
"erc20ApprovalGasSponsoring"
],
+ "schemes": ["exact", "upto"],
"evm": {
"transferMethods": ["eip3009", "permit2"]
},
diff --git a/e2e/facilitators/python/bazaar.py b/e2e/facilitators/python/bazaar.py
index cc5accf6ba..4c7c466d55 100644
--- a/e2e/facilitators/python/bazaar.py
+++ b/e2e/facilitators/python/bazaar.py
@@ -17,6 +17,7 @@ def __init__(
x402_version: int,
accepts: list[dict[str, Any]],
discovery_info: dict[str, Any] | None = None,
+ route_template: str | None = None,
metadata: dict[str, Any] | None = None,
) -> None:
self.resource = resource
@@ -24,6 +25,7 @@ def __init__(
self.x402_version = x402_version
self.accepts = accepts
self.discovery_info = discovery_info
+ self.route_template = route_template
self.last_updated = datetime.now().isoformat()
self.metadata = metadata or {}
@@ -39,6 +41,8 @@ def to_dict(self) -> dict[str, Any]:
}
if self.discovery_info:
result["discoveryInfo"] = self.discovery_info
+ if self.route_template:
+ result["routeTemplate"] = self.route_template
return result
@@ -55,6 +59,7 @@ def catalog_resource(
x402_version: int,
discovery_info: dict[str, Any] | None,
payment_requirements: dict[str, Any],
+ route_template: str | None = None,
) -> None:
"""Add a discovered resource to the catalog.
@@ -64,10 +69,13 @@ def catalog_resource(
x402_version: The x402 protocol version.
discovery_info: Optional discovery metadata.
payment_requirements: The payment requirements for this resource.
+ route_template: Optional route template for dynamic routes.
"""
print(f"📝 Discovered resource: {resource_url}")
print(f" Method: {method}")
print(f" x402 Version: {x402_version}")
+ if route_template:
+ print(f" Route template: {route_template}")
self._resources[resource_url] = DiscoveredResource(
resource=resource_url,
@@ -75,6 +83,7 @@ def catalog_resource(
x402_version=x402_version,
accepts=[payment_requirements],
discovery_info=discovery_info,
+ route_template=route_template,
metadata={},
)
diff --git a/e2e/facilitators/python/build.sh b/e2e/facilitators/python/build.sh
index 9889f505ad..c1bb071bbe 100755
--- a/e2e/facilitators/python/build.sh
+++ b/e2e/facilitators/python/build.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Python build - no compilation needed
-# This file exists for consistency with the e2e setup process
-exit 0
+set -e
+# Rebuild the local x402 editable dependency so the venv reflects source changes
+uv sync --reinstall-package x402
diff --git a/e2e/facilitators/python/main.py b/e2e/facilitators/python/main.py
index 9160860027..97d55e8f0b 100644
--- a/e2e/facilitators/python/main.py
+++ b/e2e/facilitators/python/main.py
@@ -7,15 +7,22 @@
- EVM networks (Base Sepolia) via web3.py
- SVM networks (Solana Devnet) via solders
- Bazaar discovery extension for resource cataloging
+- EIP-2612 gas sponsoring extension (gasless Permit2 approval via permit)
+- ERC-20 approval gas sponsoring extension (gasless Permit2 via signed tx relay)
- V1 and V2 protocol versions
Run with: uv run uvicorn main:app --port 4022
"""
+import logging
import os
import sys
from typing import Any
+logging.basicConfig(level=logging.INFO, format="%(name)s %(levelname)s: %(message)s")
+logging.getLogger("x402.permit2").setLevel(logging.DEBUG)
+logging.getLogger("x402.signers").setLevel(logging.DEBUG)
+
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
@@ -23,8 +30,15 @@
from x402 import x402Facilitator
from x402.extensions.bazaar import extract_discovery_info
+from x402.extensions.eip2612_gas_sponsoring import EIP2612_GAS_SPONSORING
+from x402.extensions.erc20_approval_gas_sponsoring import (
+ Erc20ApprovalFacilitatorExtension,
+ WriteContractCall,
+)
from x402.mechanisms.evm import FacilitatorWeb3Signer
+from x402.mechanisms.evm.constants import TX_STATUS_SUCCESS
from x402.mechanisms.evm.exact import register_exact_evm_facilitator
+from x402.mechanisms.evm.types import TransactionReceipt
from x402.mechanisms.svm import FacilitatorKeypairSigner
from x402.mechanisms.svm.exact import register_exact_svm_facilitator
@@ -62,6 +76,71 @@
print(f"SVM Facilitator account: {svm_signer.get_addresses()[0]}")
+class Erc20ApprovalSigner:
+ """Wraps FacilitatorWeb3Signer with send_transactions for ERC-20 approval sponsoring.
+
+ Broadcasts pre-signed approval txs and settles via the proxy contract,
+ matching the Go/TS facilitator pattern.
+ """
+
+ def __init__(self, base_signer: FacilitatorWeb3Signer):
+ self._signer = base_signer
+
+ def send_transactions(self, transactions: list) -> list[str]:
+ hashes: list[str] = []
+ for tx in transactions:
+ if isinstance(tx, str):
+ raw_bytes = bytes.fromhex(tx[2:] if tx.startswith("0x") else tx)
+ w3 = self._signer._w3
+
+ payer_address = w3.eth.account.recover_transaction(tx)
+ # Use the same gas constants as the library's approve tx builder
+ gas_cost = 70_000 * 1_000_000_000 # ERC20_APPROVE_GAS_LIMIT * DEFAULT_MAX_FEE_PER_GAS
+
+ payer_balance = w3.eth.get_balance(payer_address)
+ if payer_balance < gas_cost:
+ deficit = gas_cost - payer_balance
+ print(f"⛽ Funding payer {payer_address} with {deficit} wei for gas")
+ fund_tx = {
+ "to": payer_address,
+ "value": deficit,
+ "gas": 21000,
+ "gasPrice": w3.eth.gas_price,
+ "nonce": w3.eth.get_transaction_count(self._signer._account.address),
+ "chainId": w3.eth.chain_id,
+ }
+ signed_fund = self._signer._account.sign_transaction(fund_tx)
+ fund_hash = w3.eth.send_raw_transaction(signed_fund.raw_transaction).hex()
+ fund_receipt = w3.eth.wait_for_transaction_receipt(fund_hash)
+ if fund_receipt["status"] != 1:
+ raise RuntimeError(f"gas_funding_failed: {fund_hash}")
+ print(f"⛽ Gas funding confirmed: {fund_hash}")
+
+ tx_hash = w3.eth.send_raw_transaction(raw_bytes).hex()
+ elif isinstance(tx, dict) or isinstance(tx, WriteContractCall):
+ if isinstance(tx, dict):
+ call = WriteContractCall(**tx)
+ else:
+ call = tx
+ tx_hash = self._signer.write_contract(
+ call.address, call.abi, call.function, *call.args
+ )
+ else:
+ raise ValueError(f"Unsupported transaction type: {type(tx)}")
+
+ receipt = self._signer.wait_for_transaction_receipt(tx_hash)
+ if receipt.status != TX_STATUS_SUCCESS:
+ raise RuntimeError(f"transaction_failed: {tx_hash}")
+ hashes.append(tx_hash)
+ return hashes
+
+ def wait_for_transaction_receipt(self, tx_hash: str) -> TransactionReceipt:
+ return self._signer.wait_for_transaction_receipt(tx_hash)
+
+
+erc20_approval_signer = Erc20ApprovalSigner(evm_signer)
+
+
def _handle_after_verify(ctx: Any) -> None:
"""Handle after verify hook - extract discovery info and catalog."""
print("✅ Payment verified")
@@ -97,6 +176,7 @@ def _handle_after_verify(ctx: Any) -> None:
payment_requirements=ctx.requirements.model_dump(by_alias=True)
if hasattr(ctx.requirements, "model_dump")
else ctx.requirements,
+ route_template=getattr(discovered, "route_template", None),
)
print(" ✅ Added to bazaar catalog")
except Exception as err:
@@ -129,6 +209,12 @@ def _handle_after_verify(ctx: Any) -> None:
networks="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", # Devnet
)
+# Register gas sponsoring extensions
+facilitator.register_extension(EIP2612_GAS_SPONSORING)
+facilitator.register_extension(
+ Erc20ApprovalFacilitatorExtension(signer=erc20_approval_signer)
+)
+
# Pydantic models for request/response
class VerifyRequest(BaseModel):
@@ -179,13 +265,14 @@ async def verify(request: VerifyRequest):
# - Extract and catalog discovery info (on_after_verify)
response = await facilitator.verify(payload, requirements)
- return {
- "isValid": response.is_valid,
- "payer": response.payer,
- "invalidReason": response.invalid_reason,
- }
+ if not response.is_valid:
+ print(f" ❌ Verify rejected: {response.invalid_reason} (payer={response.payer})")
+
+ return response.model_dump(by_alias=True, exclude_none=True)
except Exception as e:
+ import traceback
print(f"Verify error: {e}")
+ traceback.print_exc()
raise HTTPException(status_code=500, detail=str(e))
@@ -216,28 +303,23 @@ async def settle(request: SettleRequest):
# - Clean up tracking (on_after_settle / on_settle_failure)
response = await facilitator.settle(payload, requirements)
- return {
- "success": response.success,
- "transaction": response.transaction,
- "network": response.network,
- "payer": response.payer,
- "errorReason": response.error_reason,
- }
+ return response.model_dump(by_alias=True, exclude_none=True)
except Exception as e:
print(f"Settle error: {e}")
# Check if this was an abort from hook
if "aborted" in str(e).lower() or "Settlement aborted" in str(e):
- # Return a proper SettleResponse instead of 500 error
- return {
- "success": False,
- "errorReason": str(e).replace("Settlement aborted: ", ""),
- "network": request.paymentPayload.get("accepted", {}).get(
+ from x402.schemas import SettleResponse
+
+ abort = SettleResponse(
+ success=False,
+ error_reason=str(e).replace("Settlement aborted: ", ""),
+ network=request.paymentPayload.get("accepted", {}).get(
"network", "unknown"
),
- "transaction": "",
- "payer": None,
- }
+ transaction="",
+ )
+ return abort.model_dump(by_alias=True, exclude_none=True)
raise HTTPException(status_code=500, detail=str(e))
@@ -253,15 +335,7 @@ async def supported():
response = facilitator.get_supported()
return {
- "kinds": [
- {
- "x402Version": k.x402_version,
- "scheme": k.scheme,
- "network": k.network,
- "extra": k.extra,
- }
- for k in response.kinds
- ],
+ "kinds": [k.model_dump(by_alias=True, exclude_none=True) for k in response.kinds],
"extensions": response.extensions,
"signers": response.signers,
}
@@ -292,7 +366,7 @@ async def health():
"network": "eip155:84532",
"facilitator": "python",
"version": "2.0.0",
- "extensions": ["bazaar"],
+ "extensions": facilitator.get_extensions(),
"discoveredResources": bazaar_catalog.get_count(),
}
@@ -322,7 +396,7 @@ async def shutdown():
║ Server: http://localhost:{PORT} ║
║ Network: eip155:84532 ║
║ Address: {evm_signer.get_addresses()[0]} ║
-║ Extensions: bazaar ║
+║ Extensions: bazaar, eip2612, erc20approval ║
║ ║
║ Endpoints: ║
║ • POST /verify (verify payment) ║
diff --git a/e2e/facilitators/python/run.sh b/e2e/facilitators/python/run.sh
index d3a07c0c9e..c653d856a9 100755
--- a/e2e/facilitators/python/run.sh
+++ b/e2e/facilitators/python/run.sh
@@ -1,3 +1,3 @@
#!/bin/bash
+uv sync --reinstall-package x402 --quiet
uv run python main.py
-
diff --git a/e2e/facilitators/python/test.config.json b/e2e/facilitators/python/test.config.json
index 11ced9d07d..ab0ea28eb9 100644
--- a/e2e/facilitators/python/test.config.json
+++ b/e2e/facilitators/python/test.config.json
@@ -11,10 +11,12 @@
2
],
"extensions": [
- "bazaar"
+ "bazaar",
+ "eip2612GasSponsoring",
+ "erc20ApprovalGasSponsoring"
],
"evm": {
- "transferMethods": ["eip3009"]
+ "transferMethods": ["eip3009", "permit2"]
},
"environment": {
"required": [
@@ -29,4 +31,3 @@
]
}
}
-
diff --git a/e2e/facilitators/python/uv.lock b/e2e/facilitators/python/uv.lock
index 569073a81f..e3a4299b04 100644
--- a/e2e/facilitators/python/uv.lock
+++ b/e2e/facilitators/python/uv.lock
@@ -2846,7 +2846,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.2.0"
+version = "2.5.0"
source = { editable = "../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
@@ -2877,7 +2877,7 @@ svm = [
[package.metadata]
requires-dist = [
{ name = "eth-abi", marker = "extra == 'evm'", specifier = ">=5.0.0" },
- { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.12.0" },
+ { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.13.0" },
{ name = "eth-keys", marker = "extra == 'evm'", specifier = ">=0.5.0" },
{ name = "eth-utils", marker = "extra == 'evm'", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], marker = "extra == 'fastapi'", specifier = ">=0.115.0" },
@@ -2904,7 +2904,7 @@ provides-extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp",
dev = [
{ name = "black", specifier = ">=23.0.0" },
{ name = "eth-abi", specifier = ">=5.0.0" },
- { name = "eth-account", specifier = ">=0.12.0" },
+ { name = "eth-account", specifier = ">=0.13.0" },
{ name = "eth-keys", specifier = ">=0.5.0" },
{ name = "eth-utils", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
diff --git a/e2e/facilitators/typescript/README.md b/e2e/facilitators/typescript/README.md
index c7064b9f3c..a87bbcade5 100644
--- a/e2e/facilitators/typescript/README.md
+++ b/e2e/facilitators/typescript/README.md
@@ -1,6 +1,6 @@
# E2E Test Facilitator: TypeScript
-This facilitator demonstrates and tests the TypeScript x402 facilitator implementation with both EVM and SVM payment verification and settlement.
+This facilitator demonstrates and tests the TypeScript x402 facilitator implementation with EVM, SVM, and optional Stellar payment verification and settlement.
## What It Tests
@@ -9,7 +9,7 @@ This facilitator demonstrates and tests the TypeScript x402 facilitator implemen
- ✅ **V1 Protocol** - Legacy x402 facilitator protocol
- ✅ **Payment Verification** - Validates payment payloads off-chain
- ✅ **Payment Settlement** - Executes transactions on-chain
-- ✅ **Multi-chain Support** - EVM and SVM mechanisms
+- ✅ **Multi-chain Support** - EVM, SVM, and (optional) Stellar mechanisms
- ✅ **HTTP API** - Express.js server exposing facilitator endpoints
### Facilitator Endpoints
@@ -27,6 +27,7 @@ This e2e facilitator showcases **production-ready lifecycle hook patterns**:
```typescript
const facilitator = new x402Facilitator()
.register("eip155:*", new ExactEvmFacilitator(evmSigner))
+ .register("stellar:*", new ExactStellarScheme([stellarSigner]))
.registerExtension(BAZAAR)
// Hook 1: Track verified payments + extract discovery info
.onAfterVerify(async (context) => {
@@ -75,6 +76,7 @@ import { ExactEvmFacilitator } from "@x402/evm";
import { ExactEvmFacilitatorV1, NETWORKS as EVM_NETWORKS } from "@x402/evm/v1";
import { ExactSvmFacilitator } from "@x402/svm";
import { ExactSvmFacilitatorV1, NETWORKS as SVM_NETWORKS } from "@x402/svm/v1";
+import { ExactStellarFacilitator } from "@x402/stellar";
// Create facilitator with bazaar extension
const facilitator = new x402Facilitator()
@@ -95,6 +97,8 @@ EVM_NETWORKS.forEach(network => {
});
// Register SVM schemes similarly...
+
+// Register Stellar (v2) schemes similarly...
```
### HTTP Server
@@ -118,7 +122,7 @@ app.listen(port, () => {
1. **Extension Registration** - Bazaar discovery
2. **Comprehensive Network Support** - All EVM V1 networks, all SVM V1 networks
-3. **Wildcard Schemes** - Efficient V2 registration with `eip155:*` and `solana:*`
+3. **Wildcard Schemes** - Efficient V2 registration with `eip155:*`, `solana:*`, and `stellar:*`
4. **HTTP Router Integration** - `@x402/server/facilitator` for Express
5. **Real Signers** - Actual blockchain transaction submission
6. **Multi-Protocol** - V1 and V2 side-by-side
@@ -128,12 +132,13 @@ app.listen(port, () => {
This facilitator is tested with:
- **Clients:** TypeScript Fetch, Go HTTP
- **Servers:** Express (TypeScript), Gin (Go)
-- **Networks:** Base Sepolia (EVM), Solana Devnet (SVM)
-- **Test Cases:**
+- **Networks:** Base Sepolia (EVM), Solana Devnet (SVM), Stellar Testnet (Stellar)
+- **Test Cases:**
- V1 EVM payments
- V2 EVM payments
- V1 SVM payments
- V2 SVM payments
+ - V2 Stellar payments (optional)
### Success Criteria
- ✅ Verification returns valid status
@@ -152,16 +157,24 @@ pnpm test --facilitator=typescript
cd e2e/facilitators/typescript
export EVM_PRIVATE_KEY="0x..."
export SVM_PRIVATE_KEY="..."
+export STELLAR_PRIVATE_KEY="S..." # optional
export PORT=4025
pnpm start
```
## Environment Variables
+### Required
- `PORT` - HTTP server port
- `EVM_PRIVATE_KEY` - Ethereum private key (hex with 0x prefix)
- `SVM_PRIVATE_KEY` - Solana private key (base58 encoded)
+### Optional
+- `STELLAR_PRIVATE_KEY` - Stellar private key (S... format) - enables Stellar support
+- `EVM_NETWORK` - EVM network (default: eip155:84532)
+- `SVM_NETWORK` - SVM network (default: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)
+- `STELLAR_NETWORK` - Stellar network (default: stellar:testnet)
+
## Package Dependencies
- `@x402/core` - Core facilitator
@@ -170,6 +183,7 @@ pnpm start
- `@x402/evm/v1` - EVM facilitator (V1) + NETWORKS
- `@x402/svm` - SVM facilitator (V2)
- `@x402/svm/v1` - SVM facilitator (V1) + NETWORKS
+- `@x402/stellar` - Stellar facilitator (V2, SEP-41)
- `express` - HTTP server
- `viem` - Ethereum transactions
- `@solana/web3.js` - Solana transactions
diff --git a/e2e/facilitators/typescript/bazaar.ts b/e2e/facilitators/typescript/bazaar.ts
index a09f29fcdb..c9b8cefaba 100644
--- a/e2e/facilitators/typescript/bazaar.ts
+++ b/e2e/facilitators/typescript/bazaar.ts
@@ -7,6 +7,7 @@ export interface DiscoveredResource {
x402Version: number;
accepts: PaymentRequirements[];
discoveryInfo?: DiscoveryInfo;
+ routeTemplate?: string;
lastUpdated: string;
metadata?: Record;
}
@@ -20,10 +21,14 @@ export class BazaarCatalog {
x402Version: number,
discoveryInfo: DiscoveryInfo,
paymentRequirements: PaymentRequirements,
+ routeTemplate?: string,
): void {
console.log(`📝 Discovered resource: ${resourceUrl}`);
console.log(` Method: ${method}`);
console.log(` x402 Version: ${x402Version}`);
+ if (routeTemplate) {
+ console.log(` Route template: ${routeTemplate}`);
+ }
this.discoveredResources.set(resourceUrl, {
resource: resourceUrl,
@@ -31,6 +36,7 @@ export class BazaarCatalog {
x402Version,
accepts: [paymentRequirements],
discoveryInfo,
+ routeTemplate,
lastUpdated: new Date().toISOString(),
metadata: {},
});
diff --git a/e2e/facilitators/typescript/index.ts b/e2e/facilitators/typescript/index.ts
index a8218f7fe8..ad3a406bfa 100644
--- a/e2e/facilitators/typescript/index.ts
+++ b/e2e/facilitators/typescript/index.ts
@@ -31,6 +31,7 @@ import {
} from "@x402/core/types";
import { toFacilitatorEvmSigner } from "@x402/evm";
import { ExactEvmScheme } from "@x402/evm/exact/facilitator";
+import { UptoEvmScheme } from "@x402/evm/upto/facilitator";
import { ExactEvmSchemeV1 } from "@x402/evm/exact/v1/facilitator";
import { NETWORKS as EVM_V1_NETWORKS } from "@x402/evm/v1";
import { BAZAAR, extractDiscoveryInfo } from "@x402/extensions/bazaar";
@@ -42,10 +43,12 @@ import { toFacilitatorSvmSigner } from "@x402/svm";
import { ExactSvmScheme } from "@x402/svm/exact/facilitator";
import { ExactSvmSchemeV1 } from "@x402/svm/exact/v1/facilitator";
import { NETWORKS as SVM_V1_NETWORKS } from "@x402/svm/v1";
+import { createEd25519Signer, type FacilitatorStellarSigner } from "@x402/stellar";
+import { ExactStellarScheme } from "@x402/stellar/exact/facilitator";
import crypto from "crypto";
import dotenv from "dotenv";
import express from "express";
-import { createWalletClient, http, publicActions, Chain } from "viem";
+import { createWalletClient, http, publicActions, Chain, parseTransaction, recoverTransactionAddress } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia, base } from "viem/chains";
import { BazaarCatalog } from "./bazaar.js";
@@ -58,9 +61,11 @@ const EVM_NETWORK = process.env.EVM_NETWORK || "eip155:84532";
const SVM_NETWORK =
process.env.SVM_NETWORK || "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
const APTOS_NETWORK = process.env.APTOS_NETWORK || "aptos:2";
+const STELLAR_NETWORK = process.env.STELLAR_NETWORK || "stellar:testnet";
const EVM_RPC_URL = process.env.EVM_RPC_URL;
const SVM_RPC_URL = process.env.SVM_RPC_URL;
const APTOS_RPC_URL = process.env.APTOS_RPC_URL;
+const STELLAR_RPC_URL = process.env.STELLAR_RPC_URL;
// Map CAIP-2 network IDs to viem chains
function getEvmChain(network: string): Chain {
@@ -76,9 +81,11 @@ function getEvmChain(network: string): Chain {
console.log(`🌐 EVM Network: ${EVM_NETWORK}`);
console.log(`🌐 SVM Network: ${SVM_NETWORK}`);
console.log(`🌐 Aptos Network: ${APTOS_NETWORK}`);
+console.log(`🌐 Stellar Network: ${STELLAR_NETWORK}`);
if (EVM_RPC_URL) console.log(`🌐 EVM RPC URL: ${EVM_RPC_URL}`);
if (SVM_RPC_URL) console.log(`🌐 SVM RPC URL: ${SVM_RPC_URL}`);
if (APTOS_RPC_URL) console.log(`🌐 Aptos RPC URL: ${APTOS_RPC_URL}`);
+if (STELLAR_RPC_URL) console.log(`🌐 Stellar RPC URL: ${STELLAR_RPC_URL}`);
// Validate required environment variables
if (!process.env.EVM_PRIVATE_KEY) {
@@ -117,6 +124,13 @@ if (process.env.APTOS_PRIVATE_KEY) {
);
}
+// Initialize the Stellar signer from private key (optional)
+let stellarSigner: FacilitatorStellarSigner | undefined;
+if (process.env.STELLAR_PRIVATE_KEY) {
+ stellarSigner = createEd25519Signer(process.env.STELLAR_PRIVATE_KEY as string, STELLAR_NETWORK as Network);
+ console.info(`Stellar Facilitator account: ${stellarSigner.address}`);
+}
+
// Create a Viem client with both wallet and public capabilities
const evmChain = getEvmChain(EVM_NETWORK);
const viemClient = createWalletClient({
@@ -152,10 +166,12 @@ const evmSigner = toFacilitatorEvmSigner({
abi: readonly unknown[];
functionName: string;
args: readonly unknown[];
+ gas?: bigint;
}) =>
viemClient.writeContract({
...args,
args: args.args || [],
+ gas: args.gas,
}),
sendTransaction: (args: { to: `0x${string}`; data: `0x${string}` }) =>
viemClient.sendTransaction(args),
@@ -195,6 +211,7 @@ const facilitator = new x402Facilitator();
// Register EVM, SVM, and Aptos schemes (v2 + v1)
facilitator
.register(EVM_NETWORK as Network, new ExactEvmScheme(evmSigner))
+ .register(EVM_NETWORK as Network, new UptoEvmScheme(evmSigner))
.registerV1(EVM_V1_NETWORKS as Network[], new ExactEvmSchemeV1(evmSigner))
.register(SVM_NETWORK as Network, new ExactSvmScheme(svmSigner))
.registerV1(SVM_V1_NETWORKS as Network[], new ExactSvmSchemeV1(svmSigner));
@@ -204,11 +221,74 @@ if (aptosSigner) {
new ExactAptosScheme(aptosSigner),
);
}
+if (stellarSigner) {
+ facilitator.register(STELLAR_NETWORK as Network, new ExactStellarScheme([stellarSigner]));
+}
+
+const PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3" as const;
+const erc20AllowanceAbi = [
+ {
+ inputs: [
+ { name: "owner", type: "address" },
+ { name: "spender", type: "address" },
+ ],
+ name: "allowance",
+ outputs: [{ name: "", type: "uint256" }],
+ stateMutability: "view",
+ type: "function",
+ },
+] as const;
+
+const erc20ApprovalSigner = {
+ ...evmSigner,
+ sendTransactions: async (
+ transactions: (`0x${string}` | { to: `0x${string}`; data: `0x${string}`; gas?: bigint })[],
+ ): Promise<`0x${string}`[]> => {
+ const hashes: `0x${string}`[] = [];
+ for (const tx of transactions) {
+ let hash: `0x${string}`;
+ if (typeof tx === "string") {
+ // Parse the raw tx to extract sender and gas params for potential gas funding
+ const parsed = parseTransaction(tx);
+ const payerAddress = await recoverTransactionAddress({ serializedTransaction: tx });
+ const gas = parsed.gas ?? 70_000n;
+ const maxFeePerGas = parsed.maxFeePerGas ?? 1_000_000_000n;
+ const gasCost = gas * maxFeePerGas;
+
+ // Check if the payer has enough ETH for gas
+ const payerBalance = await viemClient.getBalance({ address: payerAddress });
+ if (payerBalance < gasCost) {
+ const deficit = gasCost - payerBalance;
+ console.log(`⛽ Funding payer ${payerAddress} with ${deficit} wei for gas`);
+ const fundHash = await viemClient.sendTransaction({
+ to: payerAddress,
+ value: deficit,
+ });
+ const fundReceipt = await viemClient.waitForTransactionReceipt({ hash: fundHash });
+ if (fundReceipt.status !== "success") {
+ throw new Error(`gas_funding_failed: ${fundHash}`);
+ }
+ console.log(`⛽ Gas funding confirmed: ${fundHash}`);
+ }
+
+ hash = await viemClient.sendRawTransaction({ serializedTransaction: tx });
+ } else {
+ hash = await viemClient.sendTransaction(tx);
+ }
+ const receipt = await viemClient.waitForTransactionReceipt({ hash });
+ if (receipt.status !== "success") {
+ throw new Error(`transaction_failed: ${hash}`);
+ }
+ hashes.push(hash);
+ }
+ return hashes;
+ },
+};
facilitator
.registerExtension(BAZAAR)
.registerExtension(EIP2612_GAS_SPONSORING)
- .registerExtension(createErc20ApprovalGasSponsoringExtension(evmSigner, viemClient))
+ .registerExtension(createErc20ApprovalGasSponsoringExtension(erc20ApprovalSigner))
// Lifecycle hooks for payment tracking and discovery
.onAfterVerify(async (context) => {
// Hook 1: Track verified payment for verify→settle flow validation
@@ -228,6 +308,7 @@ facilitator
discovered.x402Version,
discovered.discoveryInfo,
context.requirements,
+ discovered.routeTemplate,
);
console.log(
`📦 Discovered resource: ${discovered.method} ${discovered.resourceUrl}`,
@@ -403,6 +484,7 @@ app.get("/health", (req, res) => {
evmNetwork: EVM_NETWORK,
svmNetwork: SVM_NETWORK,
aptosNetwork: aptosAccount ? APTOS_NETWORK : "(not configured)",
+ stellarNetwork: stellarSigner ? STELLAR_NETWORK : "(not configured)",
facilitator: "typescript",
version: "2.0.0",
extensions: [BAZAAR.key],
@@ -436,6 +518,7 @@ app.listen(parseInt(PORT), () => {
║ Aptos Network: ${APTOS_NETWORK} ║
║ EVM Address: ${evmAccount.address} ║
║ Aptos Address: ${aptosAccount ? aptosAccount.accountAddress.toStringLong().slice(0, 20) + "..." : "(not configured)"}
+║ Stellar Address: ${stellarSigner ? stellarSigner.address : "(not configured)"} ║
║ Extensions: bazaar ║
║ ║
║ Endpoints: ║
diff --git a/e2e/facilitators/typescript/package.json b/e2e/facilitators/typescript/package.json
index 6ce5e4d256..b3e6922b0a 100644
--- a/e2e/facilitators/typescript/package.json
+++ b/e2e/facilitators/typescript/package.json
@@ -19,6 +19,7 @@
"@x402/core": "workspace:*",
"@x402/evm": "workspace:*",
"@x402/extensions": "workspace:*",
+ "@x402/stellar": "workspace:*",
"@x402/svm": "workspace:*",
"dotenv": "^16.4.5",
"express": "^4.19.2",
diff --git a/e2e/facilitators/typescript/test.config.json b/e2e/facilitators/typescript/test.config.json
index 31f0a5d5ee..d4a6137e50 100644
--- a/e2e/facilitators/typescript/test.config.json
+++ b/e2e/facilitators/typescript/test.config.json
@@ -5,7 +5,8 @@
"protocolFamilies": [
"evm",
"svm",
- "aptos"
+ "aptos",
+ "stellar"
],
"x402Versions": [
1,
@@ -17,7 +18,7 @@
"erc20ApprovalGasSponsoring"
],
"evm": {
- "transferMethods": ["eip3009", "permit2"]
+ "transferMethods": ["eip3009", "permit2", "upto"]
},
"environment": {
"required": [
@@ -27,9 +28,11 @@
],
"optional": [
"APTOS_PRIVATE_KEY",
+ "STELLAR_PRIVATE_KEY",
"EVM_NETWORK",
"SVM_NETWORK",
- "APTOS_NETWORK"
+ "APTOS_NETWORK",
+ "STELLAR_NETWORK"
]
}
}
diff --git a/e2e/mock-facilitator/index.ts b/e2e/mock-facilitator/index.ts
new file mode 100644
index 0000000000..024cdd896f
--- /dev/null
+++ b/e2e/mock-facilitator/index.ts
@@ -0,0 +1,112 @@
+import http from "node:http";
+
+/**
+ * Mock facilitator that claims to support all schemes/networks but errors
+ * if verify or settle are actually called. Used as a fallback facilitator
+ * during e2e testing so that servers with routes unsupported by the real
+ * facilitator (e.g. "upto" on Go/Python facilitators) can still start.
+ *
+ * The real facilitator is always first in the client array and handles
+ * all actual operations. This mock only fills validation gaps at startup.
+ */
+
+const PORT = parseInt(process.env.PORT || "4099", 10);
+const EVM_NETWORK = process.env.EVM_NETWORK || "eip155:84532";
+const SVM_NETWORK = process.env.SVM_NETWORK || "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1";
+const APTOS_NETWORK = process.env.APTOS_NETWORK || "aptos:2";
+const STELLAR_NETWORK = process.env.STELLAR_NETWORK || "stellar:testnet";
+
+const DUMMY_EVM_SIGNER = "0x0000000000000000000000000000000000000001";
+const DUMMY_SVM_SIGNER = "11111111111111111111111111111111";
+const DUMMY_APTOS_SIGNER =
+ "0x0000000000000000000000000000000000000000000000000000000000000001";
+const DUMMY_STELLAR_SIGNER = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF";
+
+function buildSupportedResponse() {
+ const evmSchemes = ["exact", "upto"];
+ const otherSchemes = ["exact"];
+ const versions = [1, 2];
+
+ const kinds: Array<{
+ x402Version: number;
+ scheme: string;
+ network: string;
+ }> = [];
+
+ for (const version of versions) {
+ for (const scheme of evmSchemes) {
+ kinds.push({ x402Version: version, scheme, network: EVM_NETWORK });
+ }
+ for (const scheme of otherSchemes) {
+ kinds.push({ x402Version: version, scheme, network: SVM_NETWORK });
+ }
+ if (APTOS_NETWORK) {
+ for (const scheme of otherSchemes) {
+ kinds.push({ x402Version: version, scheme, network: APTOS_NETWORK });
+ }
+ }
+ if (STELLAR_NETWORK) {
+ for (const scheme of otherSchemes) {
+ kinds.push({ x402Version: version, scheme, network: STELLAR_NETWORK });
+ }
+ }
+ }
+
+ const signers: Record = {
+ "eip155:*": [DUMMY_EVM_SIGNER],
+ "solana:*": [DUMMY_SVM_SIGNER],
+ };
+ if (APTOS_NETWORK) {
+ signers["aptos:*"] = [DUMMY_APTOS_SIGNER];
+ }
+ if (STELLAR_NETWORK) {
+ signers["stellar:*"] = [DUMMY_STELLAR_SIGNER];
+ }
+
+ return { kinds, extensions: [], signers };
+}
+
+function sendJson(res: http.ServerResponse, statusCode: number, body: unknown) {
+ const json = JSON.stringify(body);
+ res.writeHead(statusCode, { "Content-Type": "application/json" });
+ res.end(json);
+}
+
+const supportedResponse = buildSupportedResponse();
+
+const server = http.createServer((req, res) => {
+ const url = new URL(req.url || "/", `http://localhost:${PORT}`);
+
+ if (req.method === "GET" && url.pathname === "/supported") {
+ sendJson(res, 200, supportedResponse);
+ return;
+ }
+
+ if (req.method === "GET" && url.pathname === "/health") {
+ sendJson(res, 200, { status: "ok" });
+ return;
+ }
+
+ if (req.method === "POST" && url.pathname === "/verify") {
+ sendJson(res, 500, {
+ error: "Mock facilitator: /verify should never be called. " +
+ "The real facilitator should handle all verification.",
+ });
+ return;
+ }
+
+ if (req.method === "POST" && url.pathname === "/settle") {
+ sendJson(res, 500, {
+ error: "Mock facilitator: /settle should never be called. " +
+ "The real facilitator should handle all settlement.",
+ });
+ return;
+ }
+
+ sendJson(res, 404, { error: "Not found" });
+});
+
+server.listen(PORT, () => {
+ console.log(`Mock facilitator listening on port ${PORT}`);
+ console.log("Facilitator listening");
+});
diff --git a/e2e/mock-facilitator/run.sh b/e2e/mock-facilitator/run.sh
new file mode 100755
index 0000000000..68d48fcdae
--- /dev/null
+++ b/e2e/mock-facilitator/run.sh
@@ -0,0 +1 @@
+npx tsx index.ts
diff --git a/e2e/pnpm-lock.yaml b/e2e/pnpm-lock.yaml
index 6e0ce33d8f..40353cd865 100644
--- a/e2e/pnpm-lock.yaml
+++ b/e2e/pnpm-lock.yaml
@@ -94,6 +94,9 @@ importers:
../typescript/packages/extensions:
dependencies:
+ '@noble/curves':
+ specifier: ^1.9.0
+ version: 1.9.7
'@scure/base':
specifier: ^1.2.6
version: 1.2.6
@@ -103,6 +106,9 @@ importers:
ajv:
specifier: ^8.17.1
version: 8.17.1
+ jose:
+ specifier: ^5.9.6
+ version: 5.10.0
siwe:
specifier: ^2.3.2
version: 2.3.2(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -222,12 +228,6 @@ importers:
../typescript/packages/http/express:
dependencies:
- '@coinbase/cdp-sdk':
- specifier: ^1.22.0
- version: 1.38.4(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
- '@solana/kit':
- specifier: ^6.1.0
- version: 6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)
'@x402/core':
specifier: workspace:~
version: link:../../core
@@ -235,7 +235,7 @@ importers:
specifier: workspace:~
version: link:../../extensions
'@x402/paywall':
- specifier: workspace:*
+ specifier: workspace:^
version: link:../paywall
viem:
specifier: ^2.39.3
@@ -296,6 +296,67 @@ importers:
specifier: ^3.0.5
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.1)
+ ../typescript/packages/http/fastify:
+ dependencies:
+ '@x402/core':
+ specifier: workspace:~
+ version: link:../../core
+ '@x402/extensions':
+ specifier: workspace:~
+ version: link:../../extensions
+ '@x402/paywall':
+ specifier: workspace:*
+ version: link:../paywall
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.38.0
+ '@types/node':
+ specifier: ^22.13.4
+ version: 22.16.0
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.38.0(jiti@1.21.7)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@1.21.7))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.38.0(jiti@1.21.7))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint@9.38.0(jiti@1.21.7))(prettier@3.5.2)
+ fastify:
+ specifier: ^5.0.0
+ version: 5.8.2
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsup:
+ specifier: ^8.4.0
+ version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.1)
+ tsx:
+ specifier: ^4.19.2
+ version: 4.20.3
+ typescript:
+ specifier: ^5.7.3
+ version: 5.8.3
+ vite:
+ specifier: ^6.2.6
+ version: 6.4.1(@types/node@22.16.0)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.1)
+ vite-tsconfig-paths:
+ specifier: ^5.1.4
+ version: 5.1.4(typescript@5.8.3)(vite@6.4.1(@types/node@22.16.0)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.1))
+ vitest:
+ specifier: ^3.0.5
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.1)
+
../typescript/packages/http/fetch:
dependencies:
'@x402/core':
@@ -363,7 +424,7 @@ importers:
specifier: workspace:~
version: link:../../extensions
'@x402/paywall':
- specifier: workspace:*
+ specifier: workspace:^
version: link:../paywall
zod:
specifier: ^3.24.2
@@ -420,9 +481,6 @@ importers:
../typescript/packages/http/next:
dependencies:
- '@coinbase/cdp-sdk':
- specifier: ^1.22.0
- version: 1.38.4(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@x402/core':
specifier: workspace:~
version: link:../../core
@@ -430,7 +488,7 @@ importers:
specifier: workspace:~
version: link:../../extensions
'@x402/paywall':
- specifier: workspace:*
+ specifier: workspace:^
version: link:../paywall
next:
specifier: ^16.0.10
@@ -504,7 +562,7 @@ importers:
version: 6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)
'@solana/transaction-confirmation':
specifier: ^2.1.1
- version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/wallet-standard-features':
specifier: ^1.3.0
version: 1.3.0
@@ -923,10 +981,10 @@ importers:
dependencies:
'@coinbase/cdp-sdk':
specifier: ^1.22.0
- version: 1.38.4(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ version: 1.38.4(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/kit':
specifier: ^5.0.0
- version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
hono:
specifier: ^4.7.1
version: 4.10.2
@@ -1180,9 +1238,6 @@ importers:
'@x402/core':
specifier: workspace:~
version: link:../../core
- '@x402/extensions':
- specifier: workspace:~
- version: link:../../extensions
viem:
specifier: ^2.39.3
version: 2.40.3(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)
@@ -1236,6 +1291,61 @@ importers:
specifier: ^3.0.5
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.1)
+ ../typescript/packages/mechanisms/stellar:
+ dependencies:
+ '@stellar/stellar-sdk':
+ specifier: ^14.6.1
+ version: 14.6.1
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../core
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.38.0
+ '@types/node':
+ specifier: ^22.13.4
+ version: 22.16.0
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.38.0(jiti@1.21.7)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@1.21.7))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.38.0(jiti@1.21.7))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint@9.38.0(jiti@1.21.7))(prettier@3.5.2)
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsup:
+ specifier: ^8.4.0
+ version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.1)
+ tsx:
+ specifier: ^4.19.2
+ version: 4.20.3
+ typescript:
+ specifier: ^5.7.3
+ version: 5.8.3
+ vite:
+ specifier: ^6.2.6
+ version: 6.4.1(@types/node@22.16.0)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.1)
+ vite-tsconfig-paths:
+ specifier: ^5.1.4
+ version: 5.1.4(typescript@5.8.3)(vite@6.4.1(@types/node@22.16.0)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.1))
+ vitest:
+ specifier: ^3.0.5
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@22.16.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.1)
+
../typescript/packages/mechanisms/svm:
dependencies:
'@solana-program/compute-budget':
@@ -1326,6 +1436,9 @@ importers:
'@x402/evm':
specifier: workspace:*
version: link:../../../typescript/packages/mechanisms/evm
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/stellar
'@x402/svm':
specifier: workspace:*
version: link:../../../typescript/packages/mechanisms/svm
@@ -1393,6 +1506,9 @@ importers:
'@x402/fetch':
specifier: workspace:*
version: link:../../../typescript/packages/http/fetch
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/stellar
'@x402/svm':
specifier: workspace:*
version: link:../../../typescript/packages/mechanisms/svm
@@ -1509,6 +1625,9 @@ importers:
'@x402/extensions':
specifier: workspace:*
version: link:../../../typescript/packages/extensions
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/stellar
'@x402/svm':
specifier: workspace:*
version: link:../../../typescript/packages/mechanisms/svm
@@ -1827,6 +1946,9 @@ importers:
'@x402/extensions':
specifier: workspace:*
version: link:../../../typescript/packages/extensions
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/stellar
'@x402/svm':
specifier: workspace:*
version: link:../../../typescript/packages/mechanisms/svm
@@ -1874,6 +1996,70 @@ importers:
specifier: ^5.3.0
version: 5.8.3
+ servers/fastify:
+ dependencies:
+ '@x402/aptos':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/aptos
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/core
+ '@x402/evm':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/evm
+ '@x402/extensions':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/extensions
+ '@x402/fastify':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/http/fastify
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/stellar
+ '@x402/svm':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/svm
+ dotenv:
+ specifier: ^16.6.1
+ version: 16.6.1
+ fastify:
+ specifier: ^5.3.3
+ version: 5.8.2
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.38.0
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.38.0(jiti@1.21.7)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.38.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@1.21.7))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.38.0(jiti@1.21.7))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint@9.38.0(jiti@1.21.7))(prettier@3.5.2)
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsup:
+ specifier: ^7.2.0
+ version: 7.3.0(postcss@8.5.6)(typescript@5.8.3)
+ tsx:
+ specifier: ^4.7.0
+ version: 4.20.3
+ typescript:
+ specifier: ^5.3.0
+ version: 5.8.3
+
servers/hono:
dependencies:
'@hono/node-server':
@@ -1894,6 +2080,9 @@ importers:
'@x402/hono':
specifier: workspace:*
version: link:../../../typescript/packages/http/hono
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/stellar
'@x402/svm':
specifier: workspace:*
version: link:../../../typescript/packages/mechanisms/svm
@@ -2019,6 +2208,9 @@ importers:
'@x402/next':
specifier: workspace:*
version: link:../../../typescript/packages/http/next
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../typescript/packages/mechanisms/stellar
'@x402/svm':
specifier: workspace:*
version: link:../../../typescript/packages/mechanisms/svm
@@ -3114,6 +3306,24 @@ packages:
peerDependencies:
typescript: 5.8.3
+ '@fastify/ajv-compiler@4.0.5':
+ resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
+
+ '@fastify/error@4.2.0':
+ resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
+
+ '@fastify/fast-json-stringify-compiler@5.0.3':
+ resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==}
+
+ '@fastify/forwarded@3.0.1':
+ resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==}
+
+ '@fastify/merge-json-schemas@0.2.1':
+ resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==}
+
+ '@fastify/proxy-addr@5.1.0':
+ resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
+
'@gemini-wallet/core@0.2.0':
resolution: {integrity: sha512-vv9aozWnKrrPWQ3vIFcWk7yta4hQW1Ie0fsNNPeXnjAxkbXr2hqMagEptLuMxpEP2W3mnRu05VDNKzcvAuuZDw==}
peerDependencies:
@@ -3584,6 +3794,9 @@ packages:
resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==}
deprecated: 'The package is now available as "qr": npm install qr'
+ '@pinojs/redact@0.4.0':
+ resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
+
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -5085,6 +5298,18 @@ packages:
'@stablelib/wipe@1.0.1':
resolution: {integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==}
+ '@stellar/js-xdr@3.1.2':
+ resolution: {integrity: sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==}
+
+ '@stellar/stellar-base@14.1.0':
+ resolution: {integrity: sha512-A8kFli6QGy22SRF45IjgPAJfUNGjnI+R7g4DF5NZYVsD1kGf7B4ITyc4OPclLV9tqNI4/lXxafGEw0JEUbHixw==}
+ engines: {node: '>=20.0.0'}
+
+ '@stellar/stellar-sdk@14.6.1':
+ resolution: {integrity: sha512-A1rQWDLdUasXkMXnYSuhgep+3ZZzyuXJKdt5/KAIc0gkmSp906HTvUpbT4pu+bVr41tu0+J4Ugz9J4BQAGGytg==}
+ engines: {node: '>=20.0.0'}
+ hasBin: true
+
'@svgr/babel-plugin-add-jsx-attribute@8.0.0':
resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==}
engines: {node: '>=14'}
@@ -5713,6 +5938,9 @@ packages:
zod:
optional: true
+ abstract-logging@2.0.1:
+ resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
+
accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@@ -5863,6 +6091,9 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
+ avvio@9.2.0:
+ resolution: {integrity: sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==}
+
axe-core@4.11.0:
resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==}
engines: {node: '>=4'}
@@ -5878,6 +6109,9 @@ packages:
axios@1.12.2:
resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==}
+ axios@1.13.4:
+ resolution: {integrity: sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==}
+
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
engines: {node: '>= 0.4'}
@@ -5906,6 +6140,10 @@ packages:
base-x@5.0.1:
resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==}
+ base32.js@0.1.0:
+ resolution: {integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==}
+ engines: {node: '>=0.12.0'}
+
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -5916,6 +6154,9 @@ packages:
big.js@6.2.2:
resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==}
+ bignumber.js@9.3.1:
+ resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==}
+
binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
@@ -6189,6 +6430,10 @@ packages:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
engines: {node: '>= 0.6'}
+ cookie@1.1.1:
+ resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
+ engines: {node: '>=18'}
+
core-js-compat@3.46.0:
resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==}
@@ -6386,6 +6631,10 @@ packages:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
derive-valtio@0.1.0:
resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==}
peerDependencies:
@@ -6771,6 +7020,10 @@ packages:
resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
engines: {node: '>=18.0.0'}
+ eventsource@2.0.2:
+ resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==}
+ engines: {node: '>=12.0.0'}
+
eventsource@3.0.7:
resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
engines: {node: '>=18.0.0'}
@@ -6808,6 +7061,9 @@ packages:
resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==}
engines: {node: '> 0.1.90'}
+ fast-decode-uri-component@1.0.1:
+ resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
+
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -6825,9 +7081,15 @@ packages:
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+ fast-json-stringify@6.3.0:
+ resolution: {integrity: sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==}
+
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+ fast-querystring@1.1.2:
+ resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
+
fast-redact@3.5.0:
resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==}
engines: {node: '>=6'}
@@ -6844,6 +7106,9 @@ packages:
fastestsmallesttextencoderdecoder@1.0.22:
resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==}
+ fastify@5.8.2:
+ resolution: {integrity: sha512-lZmt3navvZG915IE+f7/TIVamxIwmBd+OMB+O9WBzcpIwOo6F0LTh0sluoMFk5VkrKTvvrwIaoJPkir4Z+jtAg==}
+
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
@@ -6856,6 +7121,9 @@ packages:
picomatch:
optional: true
+ feaxios@0.0.23:
+ resolution: {integrity: sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==}
+
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
@@ -6876,6 +7144,10 @@ packages:
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
engines: {node: '>= 18.0.0'}
+ find-my-way@9.5.0:
+ resolution: {integrity: sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ==}
+ engines: {node: '>=20'}
+
find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
@@ -7168,6 +7440,10 @@ packages:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
+ ipaddr.js@2.3.0:
+ resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==}
+ engines: {node: '>= 10'}
+
iron-webcrypto@1.2.1:
resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
@@ -7270,6 +7546,10 @@ packages:
resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==}
engines: {node: '>=10'}
+ is-retry-allowed@3.0.0:
+ resolution: {integrity: sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==}
+ engines: {node: '>=12'}
+
is-set@2.0.3:
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
engines: {node: '>= 0.4'}
@@ -7400,6 +7680,9 @@ packages:
json-rpc-random-id@1.0.1:
resolution: {integrity: sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==}
+ json-schema-ref-resolver@3.0.0:
+ resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==}
+
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -7457,6 +7740,9 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
+ light-my-request@6.6.0:
+ resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==}
+
lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
@@ -7806,6 +8092,10 @@ packages:
on-exit-leak-free@0.2.0:
resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==}
+ on-exit-leak-free@2.1.2:
+ resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
+ engines: {node: '>=14.0.0'}
+
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
@@ -8001,9 +8291,19 @@ packages:
pino-abstract-transport@0.5.0:
resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==}
+ pino-abstract-transport@3.0.0:
+ resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==}
+
pino-std-serializers@4.0.0:
resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==}
+ pino-std-serializers@7.1.0:
+ resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==}
+
+ pino@10.3.1:
+ resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==}
+ hasBin: true
+
pino@7.11.0:
resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==}
hasBin: true
@@ -8142,6 +8442,12 @@ packages:
process-warning@1.0.0:
resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==}
+ process-warning@4.0.1:
+ resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==}
+
+ process-warning@5.0.0:
+ resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
+
prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
@@ -8274,6 +8580,10 @@ packages:
resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==}
engines: {node: '>= 12.13.0'}
+ real-require@0.2.0:
+ resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
+ engines: {node: '>= 12.13.0'}
+
reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
@@ -8337,10 +8647,17 @@ packages:
responselike@2.0.1:
resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
+ ret@0.5.0:
+ resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==}
+ engines: {node: '>=10'}
+
reusify@1.1.0:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
ripemd160@2.0.3:
resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==}
engines: {node: '>= 0.8'}
@@ -8381,6 +8698,10 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
+ safe-regex2@5.1.0:
+ resolution: {integrity: sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw==}
+ hasBin: true
+
safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
@@ -8395,6 +8716,9 @@ packages:
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+ secure-json-parse@4.1.0:
+ resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==}
+
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@@ -8423,6 +8747,9 @@ packages:
set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+ set-cookie-parser@2.7.2:
+ resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
+
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
@@ -8507,6 +8834,9 @@ packages:
sonic-boom@2.8.0:
resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==}
+ sonic-boom@4.2.1:
+ resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -8700,6 +9030,10 @@ packages:
thread-stream@0.15.2:
resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==}
+ thread-stream@4.0.0:
+ resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==}
+ engines: {node: '>=20'}
+
tinybench@2.9.0:
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
@@ -8737,10 +9071,17 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ toad-cache@3.7.0:
+ resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
+ engines: {node: '>=12'}
+
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
+ toml@3.0.0:
+ resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
+
tough-cookie@5.1.2:
resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
engines: {node: '>=16'}
@@ -8993,6 +9334,9 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ urijs@1.19.11:
+ resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==}
+
use-sync-external-store@1.2.0:
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
@@ -10254,6 +10598,29 @@ snapshots:
- utf-8-validate
- zod
+ '@coinbase/cdp-sdk@1.38.4(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana-program/system': 0.8.1(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+ '@solana-program/token': 0.6.0(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
+ '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ abitype: 1.0.6(typescript@5.8.3)(zod@3.25.71)
+ axios: 1.13.4
+ axios-retry: 4.5.0(axios@1.13.4)
+ jose: 6.1.3
+ md5: 2.3.0
+ uncrypto: 0.1.3
+ viem: 2.45.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71)
+ zod: 3.25.71
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - encoding
+ - fastestsmallesttextencoderdecoder
+ - typescript
+ - utf-8-validate
+ - ws
+
'@coinbase/cdp-sdk@1.38.4(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana-program/system': 0.8.1(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))
@@ -10261,8 +10628,8 @@ snapshots:
'@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)
abitype: 1.0.6(typescript@5.8.3)(zod@3.25.71)
- axios: 1.12.2
- axios-retry: 4.5.0(axios@1.12.2)
+ axios: 1.13.4
+ axios-retry: 4.5.0(axios@1.13.4)
jose: 6.1.3
md5: 2.3.0
uncrypto: 0.1.3
@@ -10704,6 +11071,29 @@ snapshots:
typescript: 5.8.3
zod: 3.25.71
+ '@fastify/ajv-compiler@4.0.5':
+ dependencies:
+ ajv: 8.17.1
+ ajv-formats: 3.0.1(ajv@8.17.1)
+ fast-uri: 3.1.0
+
+ '@fastify/error@4.2.0': {}
+
+ '@fastify/fast-json-stringify-compiler@5.0.3':
+ dependencies:
+ fast-json-stringify: 6.3.0
+
+ '@fastify/forwarded@3.0.1': {}
+
+ '@fastify/merge-json-schemas@0.2.1':
+ dependencies:
+ dequal: 2.0.3
+
+ '@fastify/proxy-addr@5.1.0':
+ dependencies:
+ '@fastify/forwarded': 3.0.1
+ ipaddr.js: 2.3.0
+
'@gemini-wallet/core@0.2.0(viem@2.31.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.71))':
dependencies:
'@metamask/rpc-errors': 7.0.2
@@ -11232,6 +11622,8 @@ snapshots:
'@paulmillr/qr@0.2.1': {}
+ '@pinojs/redact@0.4.0': {}
+
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -11867,6 +12259,10 @@ snapshots:
dependencies:
'@solana/kit': 6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@solana-program/system@0.8.1(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
+ dependencies:
+ '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+
'@solana-program/system@0.8.1(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
dependencies:
'@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -11890,6 +12286,10 @@ snapshots:
dependencies:
'@solana/kit': 6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)
+ '@solana-program/token@0.6.0(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
+ dependencies:
+ '@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+
'@solana-program/token@0.6.0(@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
dependencies:
'@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -12422,6 +12822,32 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
+ '@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/codecs': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/errors': 3.0.3(typescript@5.8.3)
+ '@solana/functional': 3.0.3(typescript@5.8.3)
+ '@solana/instruction-plans': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/instructions': 3.0.3(typescript@5.8.3)
+ '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/programs': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc-parsed-types': 3.0.3(typescript@5.8.3)
+ '@solana/rpc-spec-types': 3.0.3(typescript@5.8.3)
+ '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/signers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/sysvars': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/transaction-confirmation': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+ - ws
+
'@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
@@ -12448,6 +12874,32 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
+ '@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/accounts': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/codecs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/errors': 5.0.0(typescript@5.8.3)
+ '@solana/functional': 5.0.0(typescript@5.8.3)
+ '@solana/instruction-plans': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/instructions': 5.0.0(typescript@5.8.3)
+ '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/programs': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc-parsed-types': 5.0.0(typescript@5.8.3)
+ '@solana/rpc-spec-types': 5.0.0(typescript@5.8.3)
+ '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/signers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/sysvars': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/transaction-confirmation': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+ - ws
+
'@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/accounts': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
@@ -12945,14 +13397,23 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/errors': 2.3.0(typescript@5.8.3)
'@solana/functional': 2.3.0(typescript@5.8.3)
'@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3)
'@solana/subscribable': 2.3.0(typescript@5.8.3)
typescript: 5.8.3
- ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+
+ '@solana/rpc-subscriptions-channel-websocket@3.0.3(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/errors': 3.0.3(typescript@5.8.3)
+ '@solana/functional': 3.0.3(typescript@5.8.3)
+ '@solana/rpc-subscriptions-spec': 3.0.3(typescript@5.8.3)
+ '@solana/subscribable': 3.0.3(typescript@5.8.3)
+ typescript: 5.8.3
+ ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)
'@solana/rpc-subscriptions-channel-websocket@3.0.3(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
@@ -12963,6 +13424,15 @@ snapshots:
typescript: 5.8.3
ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ '@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/errors': 5.0.0(typescript@5.8.3)
+ '@solana/functional': 5.0.0(typescript@5.8.3)
+ '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.8.3)
+ '@solana/subscribable': 5.0.0(typescript@5.8.3)
+ typescript: 5.8.3
+ ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+
'@solana/rpc-subscriptions-channel-websocket@5.0.0(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/errors': 5.0.0(typescript@5.8.3)
@@ -13038,7 +13508,7 @@ snapshots:
optionalDependencies:
typescript: 5.8.3
- '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/errors': 2.3.0(typescript@5.8.3)
'@solana/fast-stable-stringify': 2.3.0(typescript@5.8.3)
@@ -13046,7 +13516,7 @@ snapshots:
'@solana/promises': 2.3.0(typescript@5.8.3)
'@solana/rpc-spec-types': 2.3.0(typescript@5.8.3)
'@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
- '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3)
'@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
'@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
@@ -13056,6 +13526,24 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
+ '@solana/rpc-subscriptions@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/errors': 3.0.3(typescript@5.8.3)
+ '@solana/fast-stable-stringify': 3.0.3(typescript@5.8.3)
+ '@solana/functional': 3.0.3(typescript@5.8.3)
+ '@solana/promises': 3.0.3(typescript@5.8.3)
+ '@solana/rpc-spec-types': 3.0.3(typescript@5.8.3)
+ '@solana/rpc-subscriptions-api': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc-subscriptions-channel-websocket': 3.0.3(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions-spec': 3.0.3(typescript@5.8.3)
+ '@solana/rpc-transformers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/subscribable': 3.0.3(typescript@5.8.3)
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+ - ws
+
'@solana/rpc-subscriptions@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/errors': 3.0.3(typescript@5.8.3)
@@ -13074,6 +13562,24 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
+ '@solana/rpc-subscriptions@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/errors': 5.0.0(typescript@5.8.3)
+ '@solana/fast-stable-stringify': 5.0.0(typescript@5.8.3)
+ '@solana/functional': 5.0.0(typescript@5.8.3)
+ '@solana/promises': 5.0.0(typescript@5.8.3)
+ '@solana/rpc-spec-types': 5.0.0(typescript@5.8.3)
+ '@solana/rpc-subscriptions-api': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc-subscriptions-channel-websocket': 5.0.0(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions-spec': 5.0.0(typescript@5.8.3)
+ '@solana/rpc-transformers': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/subscribable': 5.0.0(typescript@5.8.3)
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+ - ws
+
'@solana/rpc-subscriptions@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/errors': 5.0.0(typescript@5.8.3)
@@ -13201,7 +13707,7 @@ snapshots:
'@solana/rpc-spec': 3.0.3(typescript@5.8.3)
'@solana/rpc-spec-types': 3.0.3(typescript@5.8.3)
typescript: 5.8.3
- undici-types: 7.16.0
+ undici-types: 7.22.0
'@solana/rpc-transport-http@5.0.0(typescript@5.8.3)':
dependencies:
@@ -13491,7 +13997,7 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
'@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
@@ -13499,7 +14005,7 @@ snapshots:
'@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
'@solana/promises': 2.3.0(typescript@5.8.3)
'@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
- '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
'@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
'@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
@@ -13508,6 +14014,23 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
+ '@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/errors': 3.0.3(typescript@5.8.3)
+ '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/promises': 3.0.3(typescript@5.8.3)
+ '@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+ - ws
+
'@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
@@ -13525,6 +14048,23 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
+ '@solana/transaction-confirmation@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/codecs-strings': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/errors': 5.0.0(typescript@5.8.3)
+ '@solana/keys': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/promises': 5.0.0(typescript@5.8.3)
+ '@solana/rpc': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/rpc-subscriptions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-types': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/transaction-messages': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ '@solana/transactions': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
+ typescript: 5.8.3
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+ - ws
+
'@solana/transaction-confirmation@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/addresses': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)
@@ -13794,6 +14334,31 @@ snapshots:
'@stablelib/wipe@1.0.1': {}
+ '@stellar/js-xdr@3.1.2': {}
+
+ '@stellar/stellar-base@14.1.0':
+ dependencies:
+ '@noble/curves': 1.9.7
+ '@stellar/js-xdr': 3.1.2
+ base32.js: 0.1.0
+ bignumber.js: 9.3.1
+ buffer: 6.0.3
+ sha.js: 2.4.12
+
+ '@stellar/stellar-sdk@14.6.1':
+ dependencies:
+ '@stellar/stellar-base': 14.1.0
+ axios: 1.13.4
+ bignumber.js: 9.3.1
+ commander: 14.0.3
+ eventsource: 2.0.2
+ feaxios: 0.0.23
+ randombytes: 2.1.0
+ toml: 3.0.0
+ urijs: 1.19.11
+ transitivePeerDependencies:
+ - debug
+
'@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.4)':
dependencies:
'@babel/core': 7.28.4
@@ -15239,6 +15804,8 @@ snapshots:
typescript: 5.8.3
zod: 4.1.12
+ abstract-logging@2.0.1: {}
+
accepts@1.3.8:
dependencies:
mime-types: 2.1.35
@@ -15403,11 +15970,16 @@ snapshots:
dependencies:
possible-typed-array-names: 1.1.0
+ avvio@9.2.0:
+ dependencies:
+ '@fastify/error': 4.2.0
+ fastq: 1.19.1
+
axe-core@4.11.0: {}
- axios-retry@4.5.0(axios@1.12.2):
+ axios-retry@4.5.0(axios@1.13.4):
dependencies:
- axios: 1.12.2
+ axios: 1.13.4
is-retry-allowed: 2.2.0
axios@1.10.0:
@@ -15426,6 +15998,14 @@ snapshots:
transitivePeerDependencies:
- debug
+ axios@1.13.4:
+ dependencies:
+ follow-redirects: 1.15.9
+ form-data: 4.0.4
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
axobject-query@4.1.0: {}
babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4):
@@ -15460,12 +16040,16 @@ snapshots:
base-x@5.0.1: {}
+ base32.js@0.1.0: {}
+
base64-js@1.5.1: {}
baseline-browser-mapping@2.8.19: {}
big.js@6.2.2: {}
+ bignumber.js@9.3.1: {}
+
binary-extensions@2.3.0: {}
bn.js@4.12.2: {}
@@ -15759,6 +16343,8 @@ snapshots:
cookie@0.7.1: {}
+ cookie@1.1.1: {}
+
core-js-compat@3.46.0:
dependencies:
browserslist: 4.27.0
@@ -15960,6 +16546,8 @@ snapshots:
depd@2.0.0: {}
+ dequal@2.0.3: {}
+
derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.2.2)(react@19.2.0)):
dependencies:
valtio: 1.13.2(@types/react@19.2.2)(react@19.2.0)
@@ -16623,6 +17211,8 @@ snapshots:
eventsource-parser@3.0.6: {}
+ eventsource@2.0.2: {}
+
eventsource@3.0.7:
dependencies:
eventsource-parser: 3.0.6
@@ -16726,6 +17316,8 @@ snapshots:
eyes@0.1.8: {}
+ fast-decode-uri-component@1.0.1: {}
+
fast-deep-equal@3.1.3: {}
fast-diff@1.3.0: {}
@@ -16748,8 +17340,21 @@ snapshots:
fast-json-stable-stringify@2.1.0: {}
+ fast-json-stringify@6.3.0:
+ dependencies:
+ '@fastify/merge-json-schemas': 0.2.1
+ ajv: 8.17.1
+ ajv-formats: 3.0.1(ajv@8.17.1)
+ fast-uri: 3.1.0
+ json-schema-ref-resolver: 3.0.0
+ rfdc: 1.4.1
+
fast-levenshtein@2.0.6: {}
+ fast-querystring@1.1.2:
+ dependencies:
+ fast-decode-uri-component: 1.0.1
+
fast-redact@3.5.0: {}
fast-safe-stringify@2.1.1: {}
@@ -16760,6 +17365,24 @@ snapshots:
fastestsmallesttextencoderdecoder@1.0.22: {}
+ fastify@5.8.2:
+ dependencies:
+ '@fastify/ajv-compiler': 4.0.5
+ '@fastify/error': 4.2.0
+ '@fastify/fast-json-stringify-compiler': 5.0.3
+ '@fastify/proxy-addr': 5.1.0
+ abstract-logging: 2.0.1
+ avvio: 9.2.0
+ fast-json-stringify: 6.3.0
+ find-my-way: 9.5.0
+ light-my-request: 6.6.0
+ pino: 10.3.1
+ process-warning: 5.0.0
+ rfdc: 1.4.1
+ secure-json-parse: 4.1.0
+ semver: 7.7.3
+ toad-cache: 3.7.0
+
fastq@1.19.1:
dependencies:
reusify: 1.1.0
@@ -16768,6 +17391,10 @@ snapshots:
optionalDependencies:
picomatch: 4.0.3
+ feaxios@0.0.23:
+ dependencies:
+ is-retry-allowed: 3.0.0
+
file-entry-cache@8.0.0:
dependencies:
flat-cache: 4.0.1
@@ -16801,6 +17428,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ find-my-way@9.5.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-querystring: 1.1.2
+ safe-regex2: 5.1.0
+
find-up@4.1.0:
dependencies:
locate-path: 5.0.0
@@ -17129,6 +17762,8 @@ snapshots:
ipaddr.js@1.9.1: {}
+ ipaddr.js@2.3.0: {}
+
iron-webcrypto@1.2.1: {}
is-arguments@1.2.0:
@@ -17232,6 +17867,8 @@ snapshots:
is-retry-allowed@2.2.0: {}
+ is-retry-allowed@3.0.0: {}
+
is-set@2.0.3: {}
is-shared-array-buffer@1.0.4:
@@ -17381,6 +18018,10 @@ snapshots:
json-rpc-random-id@1.0.1: {}
+ json-schema-ref-resolver@3.0.0:
+ dependencies:
+ dequal: 2.0.3
+
json-schema-traverse@0.4.1: {}
json-schema-traverse@1.0.0: {}
@@ -17431,6 +18072,12 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
+ light-my-request@6.6.0:
+ dependencies:
+ cookie: 1.1.1
+ process-warning: 4.0.1
+ set-cookie-parser: 2.7.2
+
lilconfig@3.1.3: {}
lines-and-columns@1.2.4: {}
@@ -17744,6 +18391,8 @@ snapshots:
on-exit-leak-free@0.2.0: {}
+ on-exit-leak-free@2.1.2: {}
+
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
@@ -18024,8 +18673,28 @@ snapshots:
duplexify: 4.1.3
split2: 4.2.0
+ pino-abstract-transport@3.0.0:
+ dependencies:
+ split2: 4.2.0
+
pino-std-serializers@4.0.0: {}
+ pino-std-serializers@7.1.0: {}
+
+ pino@10.3.1:
+ dependencies:
+ '@pinojs/redact': 0.4.0
+ atomic-sleep: 1.0.0
+ on-exit-leak-free: 2.1.2
+ pino-abstract-transport: 3.0.0
+ pino-std-serializers: 7.1.0
+ process-warning: 5.0.0
+ quick-format-unescaped: 4.0.4
+ real-require: 0.2.0
+ safe-stable-stringify: 2.5.0
+ sonic-boom: 4.2.1
+ thread-stream: 4.0.0
+
pino@7.11.0:
dependencies:
atomic-sleep: 1.0.0
@@ -18186,6 +18855,10 @@ snapshots:
process-warning@1.0.0: {}
+ process-warning@4.0.1: {}
+
+ process-warning@5.0.0: {}
+
prompts@2.4.2:
dependencies:
kleur: 3.0.3
@@ -18334,6 +19007,8 @@ snapshots:
real-require@0.1.0: {}
+ real-require@0.2.0: {}
+
reflect.getprototypeof@1.0.10:
dependencies:
call-bind: 1.0.8
@@ -18405,8 +19080,12 @@ snapshots:
dependencies:
lowercase-keys: 2.0.0
+ ret@0.5.0: {}
+
reusify@1.1.0: {}
+ rfdc@1.4.1: {}
+
ripemd160@2.0.3:
dependencies:
hash-base: 3.1.2
@@ -18458,7 +19137,7 @@ snapshots:
buffer: 6.0.3
eventemitter3: 5.0.1
uuid: 8.3.2
- ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
optionalDependencies:
bufferutil: 4.0.9
utf-8-validate: 5.0.10
@@ -18492,6 +19171,10 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
+ safe-regex2@5.1.0:
+ dependencies:
+ ret: 0.5.0
+
safe-stable-stringify@2.5.0: {}
safer-buffer@2.1.2: {}
@@ -18502,6 +19185,8 @@ snapshots:
scheduler@0.27.0: {}
+ secure-json-parse@4.1.0: {}
+
semver@6.3.1: {}
semver@7.7.3: {}
@@ -18560,6 +19245,8 @@ snapshots:
set-blocking@2.0.0: {}
+ set-cookie-parser@2.7.2: {}
+
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
@@ -18699,6 +19386,10 @@ snapshots:
dependencies:
atomic-sleep: 1.0.0
+ sonic-boom@4.2.1:
+ dependencies:
+ atomic-sleep: 1.0.0
+
source-map-js@1.2.1: {}
source-map@0.8.0-beta.0:
@@ -18925,6 +19616,10 @@ snapshots:
dependencies:
real-require: 0.1.0
+ thread-stream@4.0.0:
+ dependencies:
+ real-require: 0.2.0
+
tinybench@2.9.0: {}
tinyexec@0.3.2: {}
@@ -18956,8 +19651,12 @@ snapshots:
dependencies:
is-number: 7.0.0
+ toad-cache@3.7.0: {}
+
toidentifier@1.0.1: {}
+ toml@3.0.0: {}
+
tough-cookie@5.1.2:
dependencies:
tldts: 6.1.86
@@ -19201,6 +19900,8 @@ snapshots:
dependencies:
punycode: 2.3.1
+ urijs@1.19.11: {}
+
use-sync-external-store@1.2.0(react@19.2.0):
dependencies:
react: 19.2.0
diff --git a/e2e/scripts/permit2-approval.ts b/e2e/scripts/permit2-approval.ts
index bf19b93d6b..9e3ef2e6f0 100644
--- a/e2e/scripts/permit2-approval.ts
+++ b/e2e/scripts/permit2-approval.ts
@@ -5,8 +5,10 @@
* It can grant unlimited approval or revoke existing approval.
*
* Usage:
- * pnpm tsx scripts/permit2-approval.ts approve # Check and approve if needed
- * pnpm tsx scripts/permit2-approval.ts revoke # Revoke Permit2 approval (set allowance to 0)
+ * pnpm tsx scripts/permit2-approval.ts approve [tokenAddress]
+ * pnpm tsx scripts/permit2-approval.ts revoke [tokenAddress]
+ *
+ * If tokenAddress is not provided, processes all known tokens.
*
* Environment variables required:
* CLIENT_EVM_PRIVATE_KEY - Private key of the client wallet
@@ -19,18 +21,44 @@ import {
http,
parseAbi,
formatUnits,
+ getAddress,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
-import { baseSepolia } from 'viem/chains';
+import { base, baseSepolia } from 'viem/chains';
config();
// Permit2 canonical address (same on all EVM chains)
const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3';
-// Base Sepolia USDC
-const USDC_ADDRESS = '0x036CbD53842c5426634e7929541eC2318f3dCF7e';
-const USDC_DECIMALS = 6;
+const evmNetwork = process.env.EVM_NETWORK || 'eip155:84532';
+const evmRpcUrl = process.env.EVM_RPC_URL;
+const evmChain = evmNetwork === 'eip155:8453' ? base : baseSepolia;
+const isMainnet = evmNetwork === 'eip155:8453';
+
+const TOKENS_BY_NETWORK: Record> = {
+ 'eip155:84532': {
+ USDC: {
+ address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
+ decimals: 6,
+ name: 'USDC',
+ },
+ MockERC20: {
+ address: '0xeED520980fC7C7B4eB379B96d61CEdea2423005a',
+ decimals: 6,
+ name: 'MockERC20',
+ },
+ },
+ 'eip155:8453': {
+ USDC: {
+ address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
+ decimals: 6,
+ name: 'USDC',
+ },
+ },
+};
+
+const TOKENS = TOKENS_BY_NETWORK[evmNetwork] || TOKENS_BY_NETWORK['eip155:84532'];
// Maximum uint256 for unlimited approval
const MAX_UINT256 = 2n ** 256n - 1n;
@@ -44,14 +72,18 @@ const erc20Abi = parseAbi([
async function main() {
const action = process.argv[2];
+ const tokenAddressArg = process.argv[3];
+ const filterAddress = tokenAddressArg ? (getAddress(tokenAddressArg) as `0x${string}`) : undefined;
if (!action || (action !== 'approve' && action !== 'revoke')) {
console.log(`
Permit2 Approval Script
Usage:
- pnpm tsx scripts/permit2-approval.ts approve # Check and approve Permit2 if needed
- pnpm tsx scripts/permit2-approval.ts revoke # Revoke Permit2 approval (set allowance to 0)
+ pnpm tsx scripts/permit2-approval.ts approve [tokenAddress]
+ pnpm tsx scripts/permit2-approval.ts revoke [tokenAddress]
+
+If tokenAddress is not provided, processes all known tokens (USDC and MockERC20).
Environment variables required:
CLIENT_EVM_PRIVATE_KEY - Private key of the client wallet
@@ -68,105 +100,104 @@ Environment variables required:
const account = privateKeyToAccount(privateKey as `0x${string}`);
const publicClient = createPublicClient({
- chain: baseSepolia,
- transport: http(),
+ chain: evmChain,
+ transport: http(evmRpcUrl),
});
const walletClient = createWalletClient({
account,
- chain: baseSepolia,
- transport: http(),
+ chain: evmChain,
+ transport: http(evmRpcUrl),
});
console.log(`\n🔑 Wallet: ${account.address}`);
- console.log(`📍 Network: Base Sepolia`);
- console.log(`💰 Token: USDC (${USDC_ADDRESS})`);
+ console.log(`📍 Network: ${evmChain.name} (${evmNetwork})`);
console.log(`🔐 Permit2: ${PERMIT2_ADDRESS}\n`);
- // Check current balance
- const balance = await publicClient.readContract({
- address: USDC_ADDRESS,
- abi: erc20Abi,
- functionName: 'balanceOf',
- args: [account.address],
- });
- console.log(`💵 USDC Balance: ${formatUnits(balance, USDC_DECIMALS)} USDC`);
-
- // Check current allowance
- const currentAllowance = await publicClient.readContract({
- address: USDC_ADDRESS,
- abi: erc20Abi,
- functionName: 'allowance',
- args: [account.address, PERMIT2_ADDRESS],
- });
-
- const formattedAllowance =
- currentAllowance === MAX_UINT256
- ? 'unlimited'
- : `${formatUnits(currentAllowance, USDC_DECIMALS)} USDC`;
- console.log(`📋 Current Permit2 Allowance: ${formattedAllowance}\n`);
-
- if (action === 'revoke') {
- // Revoke approval by setting allowance to 0
- if (currentAllowance === 0n) {
- console.log('✅ Permit2 approval is already revoked (allowance is 0)');
- process.exit(0);
- }
+ // Display balance and allowance for all known tokens
+ const tokenStates: { name: string; address: `0x${string}`; decimals: number; balance: bigint; allowance: bigint }[] = [];
- console.log('🔄 Revoking Permit2 approval (setting allowance to 0)...');
+ for (const token of Object.values(TOKENS)) {
+ const balance = await publicClient.readContract({
+ address: token.address,
+ abi: erc20Abi,
+ functionName: 'balanceOf',
+ args: [account.address],
+ });
- const hash = await walletClient.writeContract({
- address: USDC_ADDRESS,
+ const allowance = await publicClient.readContract({
+ address: token.address,
abi: erc20Abi,
- functionName: 'approve',
- args: [PERMIT2_ADDRESS, 0n],
+ functionName: 'allowance',
+ args: [account.address, PERMIT2_ADDRESS],
});
- console.log(`📝 Transaction submitted: ${hash}`);
- console.log('⏳ Waiting for confirmation...');
+ tokenStates.push({ ...token, balance, allowance });
- const receipt = await publicClient.waitForTransactionReceipt({ hash });
+ const formattedBalance = `${formatUnits(balance, token.decimals)} ${token.name}`;
+ const formattedAllowance =
+ allowance === MAX_UINT256
+ ? 'unlimited'
+ : `${formatUnits(allowance, token.decimals)} ${token.name}`;
- if (receipt.status === 'success') {
- console.log(`\n✅ Permit2 approval revoked successfully!`);
- console.log(` Block: ${receipt.blockNumber}`);
- console.log(` Gas used: ${receipt.gasUsed}`);
- } else {
- console.error(`\n❌ Revoke transaction failed`);
- process.exit(1);
- }
- return;
+ console.log(`💰 ${token.name} (${token.address})`);
+ console.log(` 💵 Balance: ${formattedBalance}`);
+ console.log(` 📋 Permit2 Allowance: ${formattedAllowance}`);
}
+ console.log();
- // action === 'approve'
- // Check if approval already exists
- if (currentAllowance === MAX_UINT256) {
- console.log('✅ Permit2 already has unlimited approval');
- process.exit(0);
+ const tokensToProcess = filterAddress
+ ? tokenStates.filter((t) => getAddress(t.address) === filterAddress)
+ : tokenStates;
+
+ if (tokensToProcess.length === 0) {
+ const addr = filterAddress ?? 'none';
+ console.error(`❌ No matching token found for address ${addr}`);
+ process.exit(1);
}
- // Grant unlimited approval
- console.log('🔄 Granting unlimited Permit2 approval...');
+ let nonce = await publicClient.getTransactionCount({ address: account.address });
- const hash = await walletClient.writeContract({
- address: USDC_ADDRESS,
- abi: erc20Abi,
- functionName: 'approve',
- args: [PERMIT2_ADDRESS, MAX_UINT256],
- });
+ if (action === 'revoke') {
+ for (const token of tokensToProcess) {
+ if (token.allowance === 0n) {
+ console.log(`✅ ${token.name}: Permit2 approval already revoked (allowance is 0)`);
+ continue;
+ }
+
+ console.log(`🔄 ${token.name}: Revoking Permit2 approval...`);
+
+ const hash = await walletClient.writeContract({
+ address: token.address,
+ abi: erc20Abi,
+ functionName: 'approve',
+ args: [PERMIT2_ADDRESS, 0n],
+ nonce: nonce++,
+ });
+
+ console.log(` ✅ Revoke submitted (tx: ${hash})`);
+ }
+ return;
+ }
- console.log(`📝 Transaction submitted: ${hash}`);
- console.log('⏳ Waiting for confirmation...');
+ // action === 'approve'
+ for (const token of tokensToProcess) {
+ if (token.allowance === MAX_UINT256) {
+ console.log(`✅ ${token.name}: Permit2 already has unlimited approval`);
+ continue;
+ }
- const receipt = await publicClient.waitForTransactionReceipt({ hash });
+ console.log(`🔄 ${token.name}: Granting unlimited Permit2 approval...`);
- if (receipt.status === 'success') {
- console.log(`\n✅ Permit2 approval granted successfully!`);
- console.log(` Block: ${receipt.blockNumber}`);
- console.log(` Gas used: ${receipt.gasUsed}`);
- } else {
- console.error(`\n❌ Transaction failed`);
- process.exit(1);
+ const hash = await walletClient.writeContract({
+ address: token.address,
+ abi: erc20Abi,
+ functionName: 'approve',
+ args: [PERMIT2_ADDRESS, MAX_UINT256],
+ nonce: nonce++,
+ });
+
+ console.log(` ✅ Approve submitted (tx: ${hash})`);
}
}
diff --git a/e2e/servers/echo/README.md b/e2e/servers/echo/README.md
new file mode 100644
index 0000000000..0a1fae0ee0
--- /dev/null
+++ b/e2e/servers/echo/README.md
@@ -0,0 +1,203 @@
+# E2E Test Server: Echo (Go)
+
+This server demonstrates and tests the x402 Echo middleware with both EVM and SVM payment protection.
+
+## What It Tests
+
+### Core Functionality
+- ✅ **V2 Protocol** - Modern x402 server middleware
+- ✅ **Payment Protection** - Middleware protecting specific routes
+- ✅ **Multi-chain Support** - EVM and SVM payment acceptance
+- ✅ **Facilitator Integration** - HTTP communication with facilitator
+- ✅ **Extension Support** - Bazaar discovery metadata
+- ✅ **Settlement Handling** - Payment verification and confirmation
+
+### Protected Endpoints
+- ✅ `GET /protected` - Requires EVM payment (USDC on Base Sepolia)
+- ✅ `GET /protected-svm` - Requires SVM payment (USDC on Solana Devnet)
+
+## What It Demonstrates
+
+### Server Setup
+
+```go
+import (
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ echomw "github.com/coinbase/x402/go/http/echo"
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+ "github.com/coinbase/x402/go/extensions/bazaar"
+ "github.com/labstack/echo/v4"
+)
+
+// Create Echo instance
+e := echo.New()
+
+// Define payment routes
+routes := x402http.RoutesConfig{
+ "GET /protected": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ Network: "eip155:84532",
+ PayTo: evmPayeeAddress,
+ Price: "$0.001",
+ },
+ },
+ Extensions: map[string]interface{}{
+ "bazaar": discoveryExtension,
+ },
+ },
+ "GET /protected-svm": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ PayTo: svmPayeeAddress,
+ Price: "$0.001",
+ },
+ },
+ Extensions: map[string]interface{}{
+ "bazaar": discoveryExtension,
+ },
+ },
+}
+
+// Apply payment middleware
+e.Use(echomw.X402Payment(echomw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []echomw.SchemeConfig{
+ {Network: "eip155:84532", Server: evm.NewExactEvmScheme()},
+ {Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", Server: svm.NewExactSvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+}))
+
+// Define protected endpoints
+e.GET("/protected", func(c echo.Context) error {
+ return c.JSON(200, map[string]string{"message": "EVM payment successful!"})
+})
+
+e.GET("/protected-svm", func(c echo.Context) error {
+ return c.JSON(200, map[string]string{"message": "SVM payment successful!"})
+})
+```
+
+### Key Concepts Shown
+
+1. **Route Configuration** - Map of route → payment requirements
+2. **Multi-Chain Services** - Different services for EVM vs SVM
+3. **Facilitator Client** - HTTP client for verification/settlement
+4. **Middleware Options** - Functional options pattern
+5. **Extension Integration** - Bazaar discovery declarations
+6. **Automatic Initialization** - Service initialization on startup
+
+## Test Scenarios
+
+This server is tested with:
+- **Clients:** TypeScript Fetch, Go HTTP
+- **Facilitators:** TypeScript, Go
+- **Payment Types:** EVM (Base Sepolia), SVM (Solana Devnet)
+- **Protocols:** V2 (primary), V1 (via client negotiation)
+
+### Request Flow
+1. Client makes initial request (no payment)
+2. Middleware returns 402 with `PAYMENT-REQUIRED` header
+3. Client creates payment payload
+4. Client retries with `PAYMENT-SIGNATURE` header
+5. Middleware forwards to facilitator for verification
+6. Middleware returns protected content + `PAYMENT-RESPONSE` header
+
+## Running
+
+```bash
+# Via e2e test suite
+cd e2e
+pnpm test --server=echo
+
+# Direct execution
+cd e2e/servers/echo
+export FACILITATOR_URL="http://localhost:4024"
+export EVM_PAYEE_ADDRESS="0x..."
+export SVM_PAYEE_ADDRESS="..."
+export PORT=4023
+./echo
+```
+
+## Environment Variables
+
+- `PORT` - HTTP server port (default: 4021)
+- `FACILITATOR_URL` - Facilitator endpoint URL
+- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments
+- `SVM_PAYEE_ADDRESS` - Solana address to receive payments
+- `EVM_NETWORK` - EVM network (default: eip155:84532)
+- `SVM_NETWORK` - SVM network (default: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)
+
+## Response Examples
+
+### 402 Payment Required
+
+```
+HTTP/1.1 402 Payment Required
+PAYMENT-REQUIRED:
+Content-Type: application/json
+
+{
+ "error": "Payment required",
+ "x402Version": 2,
+ "accepts": [...],
+ "resource": {...},
+ "extensions": {
+ "bazaar": {
+ "method": "GET",
+ "outputExample": {...}
+ }
+ }
+}
+```
+
+### 200 Success (After Payment)
+
+```
+HTTP/1.1 200 OK
+PAYMENT-RESPONSE:
+Content-Type: application/json
+
+{
+ "message": "Protected endpoint accessed successfully",
+ "timestamp": "2024-01-01T00:00:00Z"
+}
+```
+
+## Dependencies
+
+- `github.com/coinbase/x402/go` - Core x402
+- `github.com/coinbase/x402/go/http` - HTTP integration
+- `github.com/coinbase/x402/go/http/echo` - Echo middleware
+- `github.com/coinbase/x402/go/mechanisms/evm` - EVM server
+- `github.com/coinbase/x402/go/mechanisms/svm` - SVM server
+- `github.com/coinbase/x402/go/extensions/bazaar` - Discovery extension
+- `github.com/labstack/echo/v4` - HTTP framework
+
+## Implementation Highlights
+
+### Middleware Features
+- **Route Matching** - Pattern-based route configuration
+- **Payment Requirement Building** - Automatic 402 response generation
+- **Facilitator Communication** - HTTP client for verification
+- **Settlement Callbacks** - Optional handlers for payment events
+- **Extension Support** - Bazaar metadata in responses
+- **Timeout Handling** - Configurable facilitator timeouts
+
+### Service Integration
+- **EVM Server** - Base Sepolia USDC
+- **SVM Server** - Solana Devnet USDC
+- **Initialization** - Fetches supported kinds from facilitator
+- **Price Parsing** - Dollar strings → token amounts
+
+### Bazaar Extension
+- **Method Declaration** - GET with output schema
+- **Example Output** - Response structure preview
+- **Schema Definition** - JSON Schema for validation
diff --git a/e2e/servers/echo/build.sh b/e2e/servers/echo/build.sh
new file mode 100755
index 0000000000..dd569a0dd8
--- /dev/null
+++ b/e2e/servers/echo/build.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+
+echo "Building Echo server..."
+go build -o echo .
+echo "✅ Build completed: echo"
diff --git a/e2e/servers/echo/go.mod b/e2e/servers/echo/go.mod
new file mode 100644
index 0000000000..dc75cd9323
--- /dev/null
+++ b/e2e/servers/echo/go.mod
@@ -0,0 +1,73 @@
+module github.com/coinbase/x402/e2e/servers/echo
+
+go 1.24.0
+
+toolchain go1.24.1
+
+require (
+ github.com/coinbase/x402/go v0.0.0
+ github.com/joho/godotenv v1.5.1
+ github.com/labstack/echo/v4 v4.15.1
+)
+
+require (
+ filippo.io/edwards25519 v1.0.0-rc.1 // indirect
+ github.com/Microsoft/go-winio v0.6.2 // indirect
+ github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
+ github.com/StackExchange/wmi v1.2.1 // indirect
+ github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
+ github.com/bits-and-blooms/bitset v1.20.0 // indirect
+ github.com/blendle/zapdriver v1.3.1 // indirect
+ github.com/consensys/gnark-crypto v0.18.0 // indirect
+ github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
+ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/deckarep/golang-set/v2 v2.6.0 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
+ github.com/ethereum/go-ethereum v1.16.7 // indirect
+ github.com/ethereum/go-verkle v0.2.2 // indirect
+ github.com/fatih/color v1.16.0 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
+ github.com/gagliardetto/solana-go v1.14.0 // indirect
+ github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/go-ole/go-ole v1.3.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/gorilla/websocket v1.4.2 // indirect
+ github.com/holiman/uint256 v1.3.2 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.16.0 // indirect
+ github.com/labstack/gommon v0.4.2 // indirect
+ github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mitchellh/go-testing-interface v1.14.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect
+ github.com/mr-tron/base58 v1.2.0 // indirect
+ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
+ github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
+ github.com/tklauser/numcpus v0.6.1 // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/valyala/fasttemplate v1.2.2 // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+ github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+ go.mongodb.org/mongo-driver v1.12.2 // indirect
+ go.uber.org/atomic v1.7.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ go.uber.org/ratelimit v0.2.0 // indirect
+ go.uber.org/zap v1.21.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
+ golang.org/x/net v0.48.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/term v0.38.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
+ golang.org/x/time v0.14.0 // indirect
+)
+
+replace github.com/coinbase/x402/go => ../../../go
diff --git a/e2e/servers/echo/go.sum b/e2e/servers/echo/go.sum
new file mode 100644
index 0000000000..02788fc579
--- /dev/null
+++ b/e2e/servers/echo/go.sum
@@ -0,0 +1,342 @@
+filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
+filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
+github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=
+github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
+github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
+github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
+github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
+github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
+github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
+github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
+github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
+github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
+github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
+github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
+github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
+github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
+github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
+github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
+github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
+github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
+github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
+github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
+github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
+github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
+github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ=
+github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
+github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
+github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
+github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
+github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
+github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
+github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
+github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
+github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/solana-go v1.14.0 h1:3WfAi70jOOjAJ0deFMjdhFYlLXATF4tOQXsDNWJtOLw=
+github.com/gagliardetto/solana-go v1.14.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
+github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
+github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
+github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
+github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
+github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
+github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
+github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330=
+github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg=
+github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
+github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
+github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
+github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
+github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
+github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
+github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
+github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
+github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/labstack/echo/v4 v4.15.1 h1:S9keusg26gZpjMmPqB5hOEvNKnmd1lNmcHrbbH2lnFs=
+github.com/labstack/echo/v4 v4.15.1/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=
+github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
+github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
+github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
+github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
+github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
+github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
+github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
+github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
+github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
+github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk=
+github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE=
+github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
+github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
+github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
+github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
+github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
+github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
+github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
+github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
+github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
+github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
+github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
+github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
+github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
+github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
+github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
+github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
+github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
+github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
+github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.12.2 h1:gbWY1bJkkmUB9jjZzcdhOL8O85N9H+Vvsf2yFN0RDws=
+go.mongodb.org/mongo-driver v1.12.2/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
+go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
+go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
+golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
+google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/e2e/servers/echo/install.sh b/e2e/servers/echo/install.sh
new file mode 100755
index 0000000000..9928a06918
--- /dev/null
+++ b/e2e/servers/echo/install.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+
+echo "Installing Go dependencies for Echo server..."
+go mod tidy
+echo "✅ Dependencies installed"
diff --git a/e2e/servers/echo/main.go b/e2e/servers/echo/main.go
new file mode 100644
index 0000000000..0269f74880
--- /dev/null
+++ b/e2e/servers/echo/main.go
@@ -0,0 +1,443 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ "github.com/coinbase/x402/go/extensions/bazaar"
+ "github.com/coinbase/x402/go/extensions/eip2612gassponsor"
+ "github.com/coinbase/x402/go/extensions/erc20approvalgassponsor"
+ "github.com/coinbase/x402/go/extensions/types"
+ x402http "github.com/coinbase/x402/go/http"
+ echomw "github.com/coinbase/x402/go/http/echo"
+ exactevm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ uptoevm "github.com/coinbase/x402/go/mechanisms/evm/upto/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+ "github.com/joho/godotenv"
+ "github.com/labstack/echo/v4"
+)
+
+var shutdownRequested bool
+
+// Echo E2E Test Server with x402 v2 Payment Middleware
+//
+// This server demonstrates how to integrate x402 v2 payment middleware
+// with an Echo application for end-to-end testing.
+
+func main() {
+ // Load .env file if it exists
+ if err := godotenv.Load(); err != nil {
+ fmt.Println("Warning: .env file not found. Using environment variables.")
+ }
+
+ // Get configuration from environment
+ port := os.Getenv("PORT")
+ if port == "" {
+ port = "4021"
+ }
+
+ evmPayeeAddress := os.Getenv("EVM_PAYEE_ADDRESS")
+ if evmPayeeAddress == "" {
+ fmt.Println("❌ EVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ svmPayeeAddress := os.Getenv("SVM_PAYEE_ADDRESS")
+ if svmPayeeAddress == "" {
+ fmt.Println("❌ SVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ facilitatorURL := os.Getenv("FACILITATOR_URL")
+ if facilitatorURL == "" {
+ fmt.Println("❌ FACILITATOR_URL environment variable is required")
+ os.Exit(1)
+ }
+
+ // Network configurations (from env or defaults)
+ evmNetworkStr := os.Getenv("EVM_NETWORK")
+ if evmNetworkStr == "" {
+ evmNetworkStr = "eip155:84532" // Default: Base Sepolia
+ }
+ svmNetworkStr := os.Getenv("SVM_NETWORK")
+ if svmNetworkStr == "" {
+ svmNetworkStr = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1" // Default: Solana Devnet
+ }
+ evmNetwork := x402.Network(evmNetworkStr)
+ svmNetwork := x402.Network(svmNetworkStr)
+
+ evmPermit2Asset := os.Getenv("EVM_PERMIT2_ASSET")
+ if evmPermit2Asset == "" {
+ evmPermit2Asset = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
+ }
+
+ fmt.Printf("EVM Payee address: %s\n", evmPayeeAddress)
+ fmt.Printf("SVM Payee address: %s\n", svmPayeeAddress)
+ fmt.Printf("Using remote facilitator at: %s\n", facilitatorURL)
+
+ // Create Echo instance
+ e := echo.New()
+ e.HideBanner = true
+
+ // Create HTTP facilitator client
+ facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+ })
+
+ // Configure x402 payment middleware
+
+ // Declare bazaar discovery extension for GET endpoints
+ discoveryExtension, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET,
+ nil, // No query params
+ nil, // No input schema
+ "", // No body type (GET method)
+ &types.OutputConfig{
+ Example: map[string]interface{}{
+ "message": "Protected endpoint accessed successfully",
+ "timestamp": "2024-01-01T00:00:00Z",
+ },
+ Schema: types.JSONSchema{
+ "properties": map[string]interface{}{
+ "message": map[string]interface{}{"type": "string"},
+ "timestamp": map[string]interface{}{"type": "string"},
+ },
+ "required": []string{"message", "timestamp"},
+ },
+ },
+ )
+ if err != nil {
+ fmt.Printf("Warning: Failed to create bazaar extension: %v\n", err)
+ }
+
+ routes := x402http.RoutesConfig{
+ "GET /exact/evm/eip3009": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Price: "$0.001",
+ Network: evmNetwork,
+ },
+ },
+ Extensions: map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ },
+ },
+ "GET /exact/svm": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: svmPayeeAddress,
+ Price: "$0.001",
+ Network: svmNetwork,
+ },
+ },
+ Extensions: map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ },
+ },
+ // Permit2 direct endpoint - standard settle, no gas sponsoring (client must pre-approve Permit2)
+ "GET /exact/evm/permit2": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "1000",
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ "extra": map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ },
+ },
+ },
+ },
+ Extensions: map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ },
+ },
+ "GET /exact/evm/permit2-eip2612GasSponsoring": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "1000",
+ "asset": evmPermit2Asset,
+ "extra": func() map[string]interface{} {
+ name := "USD Coin"
+ if evmNetworkStr == "eip155:84532" {
+ name = "USDC"
+ }
+ return map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ "name": name,
+ "version": "2",
+ }
+ }(),
+ },
+ },
+ },
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
+ for k, v := range eip2612gassponsor.DeclareEip2612GasSponsoringExtension() {
+ ext[k] = v
+ }
+ return ext
+ }(),
+ },
+ "GET /upto/evm/permit2": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "upto",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "2000",
+ "asset": evmPermit2Asset,
+ "extra": map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ "name": "USDC",
+ "version": "2",
+ },
+ },
+ },
+ },
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
+ for k, v := range eip2612gassponsor.DeclareEip2612GasSponsoringExtension() {
+ ext[k] = v
+ }
+ return ext
+ }(),
+ },
+ "GET /exact/evm/permit2-erc20ApprovalGasSponsoring": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "1000",
+ "asset": evmPermit2Asset,
+ "extra": map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ },
+ },
+ },
+ },
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
+ for k, v := range erc20approvalgassponsor.DeclareExtension() {
+ ext[k] = v
+ }
+ return ext
+ }(),
+ },
+ }
+
+ // Apply payment middleware with detailed error logging
+ e.Use(echomw.X402Payment(echomw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []echomw.SchemeConfig{
+ {Network: evmNetwork, Server: exactevm.NewExactEvmScheme()},
+ {Network: evmNetwork, Server: uptoevm.NewUptoEvmScheme()},
+ {Network: svmNetwork, Server: svm.NewExactSvmScheme()},
+ },
+ SyncFacilitatorOnStart: true,
+ Timeout: 30 * time.Second,
+ ErrorHandler: func(c echo.Context, err error) {
+ // Log detailed error information for debugging
+ fmt.Printf("❌ [E2E SERVER ERROR] Payment error occurred\n")
+ fmt.Printf(" Path: %s\n", c.Request().URL.Path)
+ fmt.Printf(" Method: %s\n", c.Request().Method)
+ fmt.Printf(" Error: %v\n", err)
+ fmt.Printf(" Headers: %v\n", c.Request().Header)
+
+ // Default error response
+ c.JSON(http.StatusPaymentRequired, map[string]interface{}{
+ "error": err.Error(),
+ })
+ },
+ SettlementHandler: func(c echo.Context, settleResp *x402.SettleResponse) {
+ // Log successful settlement
+ fmt.Printf("✅ [E2E SERVER SUCCESS] Payment settled\n")
+ fmt.Printf(" Path: %s\n", c.Request().URL.Path)
+ fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
+ fmt.Printf(" Network: %s\n", settleResp.Network)
+ fmt.Printf(" Payer: %s\n", settleResp.Payer)
+ },
+ }))
+
+ // Protected endpoint - requires payment to access
+ e.GET("/exact/evm/eip3009", func(c echo.Context) error {
+ if shutdownRequested {
+ return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ }
+
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "message": "Protected endpoint accessed successfully (EVM)",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "network": "eip155:84532",
+ })
+ })
+
+ // Protected SVM endpoint - requires payment to access
+ e.GET("/exact/svm", func(c echo.Context) error {
+ if shutdownRequested {
+ return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ }
+
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "message": "Protected endpoint accessed successfully (SVM)",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ })
+ })
+
+ // Protected Permit2 direct endpoint - standard settle (no gas sponsoring)
+ e.GET("/exact/evm/permit2", func(c echo.Context) error {
+ if shutdownRequested {
+ return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ }
+
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "message": "Permit2 endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "permit2",
+ })
+ })
+
+ // Protected Permit2 EIP-2612 endpoint - Permit2 with gas sponsoring
+ e.GET("/exact/evm/permit2-eip2612GasSponsoring", func(c echo.Context) error {
+ if shutdownRequested {
+ return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ }
+
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "message": "Permit2 EIP-2612 endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "permit2-eip2612",
+ })
+ })
+
+ // Protected Permit2 ERC-20 approval endpoint
+ e.GET("/exact/evm/permit2-erc20ApprovalGasSponsoring", func(c echo.Context) error {
+ if shutdownRequested {
+ return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ }
+
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "message": "Permit2 ERC-20 approval endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "permit2-erc20-approval",
+ })
+ })
+
+ e.GET("/upto/evm/permit2", func(c echo.Context) error {
+ if shutdownRequested {
+ return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ }
+
+ echomw.SetSettlementOverrides(c, &x402.SettlementOverrides{Amount: "1000"})
+
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "message": "Upto Permit2 endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "upto-permit2",
+ })
+ })
+
+ // Health check endpoint - no payment required
+ e.GET("/health", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "status": "ok",
+ "version": "2.0.0",
+ "evm_network": string(evmNetwork),
+ "evm_payee": evmPayeeAddress,
+ "svm_network": string(svmNetwork),
+ "svm_payee": svmPayeeAddress,
+ })
+ })
+
+ // Shutdown endpoint - used by e2e tests
+ e.POST("/close", func(c echo.Context) error {
+ shutdownRequested = true
+
+ fmt.Println("Received shutdown request")
+
+ // Schedule server shutdown after response
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ os.Exit(0)
+ }()
+
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "message": "Server shutting down gracefully",
+ })
+ })
+
+ // Set up graceful shutdown
+ quit := make(chan os.Signal, 1)
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+
+ go func() {
+ <-quit
+ fmt.Println("Received shutdown signal, exiting...")
+ os.Exit(0)
+ }()
+
+ // Print startup banner
+ fmt.Printf(`
+╔════════════════════════════════════════════════════════╗
+║ x402 Echo E2E Test Server ║
+╠════════════════════════════════════════════════════════╣
+║ Server: http://localhost:%-29s ║
+║ EVM Network: %-40s ║
+║ EVM Payee: %-40s ║
+║ SVM Network: %-40s ║
+║ SVM Payee: %-40s ║
+║ ║
+║ Endpoints: ║
+║ • GET /exact/evm/eip3009 (EVM EIP-3009) ║
+║ • GET /exact/evm/permit2 (Permit2) ║
+║ • GET /exact/evm/permit2-eip2612GasSponsoring ║
+║ • GET /exact/evm/permit2-erc20ApprovalGasSponsoring ║
+║ • GET /exact/svm (SVM) ║
+║ • GET /upto/evm/permit2 (Upto Permit2) ║
+║ • GET /health (no payment required) ║
+║ • POST /close (shutdown server) ║
+╚════════════════════════════════════════════════════════╝
+`, port, evmNetwork, evmPayeeAddress, svmNetwork, svmPayeeAddress)
+
+ if err := e.Start(":" + port); err != nil && err != http.ErrServerClosed {
+ fmt.Printf("Error starting server: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/e2e/servers/echo/run.sh b/e2e/servers/echo/run.sh
new file mode 100755
index 0000000000..5f2f5f8501
--- /dev/null
+++ b/e2e/servers/echo/run.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+go run main.go
diff --git a/e2e/servers/echo/test.config.json b/e2e/servers/echo/test.config.json
new file mode 100644
index 0000000000..74682bc629
--- /dev/null
+++ b/e2e/servers/echo/test.config.json
@@ -0,0 +1,87 @@
+{
+ "name": "echo",
+ "type": "server",
+ "language": "go",
+ "x402Version": 2,
+ "extensions": [
+ "bazaar",
+ "eip2612GasSponsoring",
+ "erc20ApprovalGasSponsoring"
+ ],
+ "description": "Go Echo server with x402 v2 payment middleware",
+ "endpoints": [
+ {
+ "path": "/exact/evm/eip3009",
+ "method": "GET",
+ "description": "Protected endpoint requiring EIP-3009 payment",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "eip3009"
+ },
+ {
+ "path": "/exact/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment (standard settle, no gas sponsoring)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "permit2Direct": true
+ },
+ {
+ "path": "/exact/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "coldstart": true
+ },
+ {
+ "path": "/exact/evm/permit2-erc20ApprovalGasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
+ },
+ {
+ "path": "/upto/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring upto Permit2 payment (usage-based settlement)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "permit2Direct": true
+ },
+ {
+ "path": "/exact/svm",
+ "method": "GET",
+ "description": "Protected endpoint requiring payment (SVM)",
+ "requiresPayment": true,
+ "protocolFamily": "svm"
+ },
+ {
+ "path": "/health",
+ "method": "GET",
+ "description": "Health check endpoint",
+ "health": true
+ },
+ {
+ "path": "/close",
+ "method": "POST",
+ "description": "Graceful shutdown endpoint",
+ "close": true
+ }
+ ],
+ "environment": {
+ "required": [
+ "PORT",
+ "EVM_PAYEE_ADDRESS",
+ "SVM_PAYEE_ADDRESS",
+ "FACILITATOR_URL"
+ ],
+ "optional": []
+ }
+}
diff --git a/e2e/servers/express/README.md b/e2e/servers/express/README.md
index c28f19f801..0e0d0aaf44 100644
--- a/e2e/servers/express/README.md
+++ b/e2e/servers/express/README.md
@@ -1,13 +1,13 @@
# E2E Test Server: Express (TypeScript)
-This server demonstrates and tests the x402 Express.js middleware with both EVM and SVM payment protection.
+This server demonstrates and tests the x402 Express.js middleware with EVM, SVM, and optional Stellar payment protection.
## What It Tests
### Core Functionality
- ✅ **V2 Protocol** - Modern x402 server middleware
- ✅ **Payment Protection** - Middleware protecting specific routes
-- ✅ **Multi-chain Support** - EVM and SVM payment acceptance
+- ✅ **Multi-chain Support** - EVM, SVM, and (optional) Stellar payment acceptance
- ✅ **Facilitator Integration** - HTTP communication with facilitator
- ✅ **Extension Support** - Bazaar discovery metadata
- ✅ **Settlement Handling** - Payment verification and confirmation
@@ -15,6 +15,7 @@ This server demonstrates and tests the x402 Express.js middleware with both EVM
### Protected Endpoints
- ✅ `GET /protected` - Requires EVM payment (USDC on Base Sepolia)
- ✅ `GET /protected-svm` - Requires SVM payment (USDC on Solana Devnet)
+- ✅ `GET /protected-stellar` - Requires Stellar payment (USDC on Stellar Testnet)
## What It Demonstrates
@@ -24,7 +25,8 @@ This server demonstrates and tests the x402 Express.js middleware with both EVM
import express from "express";
import { x402Middleware } from "@x402/server/express";
import { ExactEvmServer } from "@x402/evm";
-import { ExactEvmServer } from "@x402/svm";
+import { ExactSvmServer } from "@x402/svm";
+import { ExactStellarServer } from "@x402/stellar";
const app = express();
@@ -47,16 +49,26 @@ const routes = {
extensions: {
bazaar: discoveryMetadata
}
+ },
+ "GET /protected-stellar": {
+ scheme: "exact",
+ network: "stellar:testnet",
+ payTo: "YourStellarAddress",
+ price: "$0.001",
+ extensions: {
+ bazaar: discoveryMetadata
+ }
}
};
-// Apply x402 middleware with EVM and SVM servers
+// Apply x402 middleware with EVM, SVM, and Stellar servers
app.use(x402Middleware({
routes,
facilitatorUrl: "http://localhost:4023",
servers: {
"eip155:84532": new ExactEvmServer(),
- "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": new ExactSvmServer()
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": new ExactSvmServer(),
+ "stellar:testnet": new ExactStellarServer()
}
}));
@@ -68,6 +80,10 @@ app.get("/protected", (req, res) => {
app.get("/protected-svm", (req, res) => {
res.json({ message: "SVM payment successful!" });
});
+
+app.get("/protected-stellar", (req, res) => {
+ res.json({ message: "Stellar payment successful!" });
+});
```
### Key Concepts Shown
@@ -84,7 +100,7 @@ app.get("/protected-svm", (req, res) => {
This server is tested with:
- **Clients:** TypeScript Fetch, Go HTTP
- **Facilitators:** TypeScript, Go
-- **Payment Types:** EVM (Base Sepolia), SVM (Solana Devnet)
+- **Payment Types:** EVM (Base Sepolia), SVM (Solana Devnet), Stellar (Stellar Testnet)
- **Protocols:** V2 (primary), V1 (via client negotiation)
### Request Flow
@@ -107,18 +123,24 @@ cd e2e/servers/express
export FACILITATOR_URL="http://localhost:4023"
export EVM_PAYEE_ADDRESS="0x..."
export SVM_PAYEE_ADDRESS="..."
+export STELLAR_PAYEE_ADDRESS="G..." # optional
export PORT=4022
pnpm start
```
## Environment Variables
+### Required
- `PORT` - HTTP server port (default: 4022)
- `FACILITATOR_URL` - Facilitator endpoint URL
- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments
- `SVM_PAYEE_ADDRESS` - Solana address to receive payments
+
+### Optional
+- `STELLAR_PAYEE_ADDRESS` - Stellar address to receive payments - enables Stellar endpoint
- `EVM_NETWORK` - EVM network (default: eip155:84532)
- `SVM_NETWORK` - SVM network (default: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)
+- `STELLAR_NETWORK` - Stellar network (default: stellar:testnet)
## Response Examples
@@ -151,6 +173,7 @@ PAYMENT-RESPONSE:
- `@x402/server` - Express middleware
- `@x402/evm` - EVM server
- `@x402/svm` - SVM server
+- `@x402/stellar` - Stellar server
- `@x402/extensions/bazaar` - Discovery extension
- `express` - HTTP server framework
@@ -166,5 +189,6 @@ PAYMENT-RESPONSE:
### Service Integration
- **EVM Server** - Handles Base Sepolia USDC payments
- **SVM Server** - Handles Solana Devnet USDC payments
+- **Stellar Server** - Handles Stellar Testnet USDC contract payments (SEP-41)
- **Price Conversion** - "$0.001" → token amounts with decimals
- **Asset Resolution** - Automatic USDC contract/mint lookup
diff --git a/e2e/servers/express/index.ts b/e2e/servers/express/index.ts
index 364a1ca7dd..90591c19c9 100644
--- a/e2e/servers/express/index.ts
+++ b/e2e/servers/express/index.ts
@@ -1,9 +1,11 @@
import express from "express";
-import { paymentMiddleware } from "@x402/express";
+import { paymentMiddleware, setSettlementOverrides } from "@x402/express";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { UptoEvmScheme } from "@x402/evm/upto/server";
import { ExactSvmScheme } from "@x402/svm/exact/server";
import { ExactAptosScheme } from "@x402/aptos/exact/server";
+import { ExactStellarScheme } from "@x402/stellar/exact/server";
import { bazaarResourceServerExtension, declareDiscoveryExtension } from "@x402/extensions/bazaar";
import {
declareEip2612GasSponsoringExtension,
@@ -22,11 +24,15 @@ dotenv.config();
const PORT = process.env.PORT || "4021";
const EVM_NETWORK = (process.env.EVM_NETWORK || "eip155:84532") as `${string}:${string}`;
-const SVM_NETWORK = (process.env.SVM_NETWORK || "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") as `${string}:${string}`;
+const SVM_NETWORK = (process.env.SVM_NETWORK ||
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") as `${string}:${string}`;
const APTOS_NETWORK = (process.env.APTOS_NETWORK || "aptos:2") as `${string}:${string}`;
+const STELLAR_NETWORK = (process.env.STELLAR_NETWORK || "stellar:testnet") as `${string}:${string}`;
const EVM_PAYEE_ADDRESS = process.env.EVM_PAYEE_ADDRESS as `0x${string}`;
const SVM_PAYEE_ADDRESS = process.env.SVM_PAYEE_ADDRESS as string;
+const EVM_PERMIT2_ASSET = process.env.EVM_PERMIT2_ASSET as `0x${string}`;
const APTOS_PAYEE_ADDRESS = process.env.APTOS_PAYEE_ADDRESS as string;
+const STELLAR_PAYEE_ADDRESS = process.env.STELLAR_PAYEE_ADDRESS as string | undefined;
const facilitatorUrl = process.env.FACILITATOR_URL;
if (!EVM_PAYEE_ADDRESS) {
@@ -39,7 +45,6 @@ if (!SVM_PAYEE_ADDRESS) {
process.exit(1);
}
-
if (!facilitatorUrl) {
console.error("❌ FACILITATOR_URL environment variable is required");
process.exit(1);
@@ -48,30 +53,40 @@ if (!facilitatorUrl) {
// Initialize Express app
const app = express();
-// Create HTTP facilitator client
-const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+// Create facilitator clients (mock facilitator as fallback for startup validation)
+const facilitatorClients = [new HTTPFacilitatorClient({ url: facilitatorUrl })];
+const mockFacilitatorUrl = process.env.MOCK_FACILITATOR_URL;
+if (mockFacilitatorUrl) {
+ facilitatorClients.push(new HTTPFacilitatorClient({ url: mockFacilitatorUrl }));
+}
// Create x402 resource server
-const server = new x402ResourceServer(facilitatorClient);
+const server = new x402ResourceServer(facilitatorClients);
// Register server schemes
server.register("eip155:*", new ExactEvmScheme());
+server.register("eip155:*", new UptoEvmScheme());
server.register("solana:*", new ExactSvmScheme());
if (APTOS_PAYEE_ADDRESS) {
server.register("aptos:*", new ExactAptosScheme());
}
+if (STELLAR_PAYEE_ADDRESS) {
+ server.register("stellar:*", new ExactStellarScheme());
+}
// Register Bazaar discovery extension
server.registerExtension(bazaarResourceServerExtension);
-console.log(`Facilitator account: ${process.env.EVM_PRIVATE_KEY ? process.env.EVM_PRIVATE_KEY.substring(0, 10) + '...' : 'not configured'}`);
+console.log(
+ `Facilitator account: ${process.env.EVM_PRIVATE_KEY ? process.env.EVM_PRIVATE_KEY.substring(0, 10) + "..." : "not configured"}`,
+);
console.log(`Using remote facilitator at: ${facilitatorUrl}`);
/**
* Pre-middleware guard for optional Aptos endpoint
* Returns 501 Not Implemented if Aptos is not configured
*/
-app.get("/protected-aptos", (req, res, next) => {
+app.get("/exact/aptos", (req, res, next) => {
if (!APTOS_PAYEE_ADDRESS) {
return res.status(501).json({
error: "Aptos payments not configured",
@@ -81,17 +96,31 @@ app.get("/protected-aptos", (req, res, next) => {
next();
});
+/**
+ * Pre-middleware guard for optional Stellar endpoint
+ * Returns 501 Not Implemented if Stellar is not configured
+ */
+app.use("/exact/stellar", (req, res, next) => {
+ if (!STELLAR_PAYEE_ADDRESS) {
+ return res.status(501).json({
+ error: "Stellar payments not configured",
+ message: "STELLAR_PAYEE_ADDRESS environment variable is not set",
+ });
+ }
+ next();
+});
+
/**
* Configure x402 payment middleware using builder pattern
*
* This middleware protects endpoints with $0.001 USDC payment requirements
- * on Base Sepolia, Solana Devnet, and Aptos Testnet with bazaar discovery extension.
+ * on Base Sepolia, Solana Devnet, Aptos Testnet, and Stellar Testnet with bazaar discovery extension.
*/
app.use(
paymentMiddleware(
{
// Route-specific payment configuration
- "GET /protected": {
+ "GET /exact/evm/eip3009": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
@@ -116,7 +145,7 @@ app.use(
}),
},
},
- "GET /protected-svm": {
+ "GET /exact/svm": {
accepts: {
payTo: SVM_PAYEE_ADDRESS,
scheme: "exact",
@@ -143,45 +172,44 @@ app.use(
},
...(APTOS_PAYEE_ADDRESS
? {
- "GET /protected-aptos": {
- accepts: {
- payTo: APTOS_PAYEE_ADDRESS,
- scheme: "exact",
- price: "$0.001",
- network: APTOS_NETWORK,
- },
- extensions: {
- ...declareDiscoveryExtension({
- output: {
- example: {
- message: "Protected endpoint accessed successfully",
- timestamp: "2024-01-01T00:00:00Z",
- },
- schema: {
- properties: {
- message: { type: "string" },
- timestamp: { type: "string" },
- },
- required: ["message", "timestamp"],
+ "GET /exact/aptos": {
+ accepts: {
+ payTo: APTOS_PAYEE_ADDRESS,
+ scheme: "exact",
+ price: "$0.001",
+ network: APTOS_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
},
+ required: ["message", "timestamp"],
},
- }),
- },
+ },
+ }),
},
- }
+ },
+ }
: {}),
- // Permit2 endpoint for generic ERC-20 tokens (no EIP-2612, uses raw approve tx)
- "GET /protected-permit2-erc20": {
+ // Permit2 endpoint for ERC-20 approval gas sponsoring (no EIP-2612)
+ "GET /exact/evm/permit2-erc20ApprovalGasSponsoring": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
network: EVM_NETWORK,
price: {
amount: "1000",
- asset: "0xeED520980fC7C7B4eB379B96d61CEdea2423005a", // Generic MockERC20 token (no EIP-2612)
+ asset: EVM_PERMIT2_ASSET,
extra: {
assetTransferMethod: "permit2",
- // No name/version - generic ERC-20 without EIP-2612
},
},
},
@@ -189,19 +217,18 @@ app.use(
...declareErc20ApprovalGasSponsoringExtension(),
},
},
- // Permit2 endpoint - explicitly requires Permit2 flow instead of EIP-3009
- "GET /protected-permit2": {
+ // Permit2 standard/direct endpoint - no gas sponsoring, client must pre-approve Permit2
+ "GET /exact/evm/permit2": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
network: EVM_NETWORK,
- // Use pre-parsed price with assetTransferMethod to force Permit2
price: {
- amount: "1000", // 0.001 USDC (6 decimals)
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // Base Sepolia USDC
+ amount: "1000",
+ asset: EVM_PERMIT2_ASSET,
extra: {
assetTransferMethod: "permit2",
- name: "USDC",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
version: "2",
},
},
@@ -224,9 +251,125 @@ app.use(
},
},
}),
+ },
+ },
+ // Permit2 endpoint with EIP-2612 gas sponsoring
+ "GET /exact/evm/permit2-eip2612GasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "exact",
+ network: EVM_NETWORK,
+ price: "$0.001",
+ extra: { assetTransferMethod: "permit2" },
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ method: "permit2-eip2612",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ method: { type: "string" },
+ },
+ required: ["message", "timestamp", "method"],
+ },
+ },
+ }),
...declareEip2612GasSponsoringExtension(),
},
},
+ // Upto Permit2 standard/direct endpoint - no gas sponsoring, client must pre-approve Permit2
+ // Authorizes up to 2000 atomic units, settles 1000 (partial settlement)
+ "GET /upto/evm/permit2": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ },
+ // Upto Permit2 endpoint with EIP-2612 gas sponsoring
+ // Authorizes up to 2000 atomic units, settles 1000 (partial settlement)
+ "GET /upto/evm/permit2-eip2612GasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ extensions: {
+ ...declareEip2612GasSponsoringExtension(),
+ },
+ },
+ // Upto Permit2 endpoint for ERC-20 approval gas sponsoring (no EIP-2612)
+ // Authorizes up to 2000 atomic units, settles 1000 (partial settlement)
+ "GET /upto/evm/permit2-erc20ApprovalGasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ },
+ },
+ },
+ extensions: {
+ ...declareErc20ApprovalGasSponsoringExtension(),
+ },
+ },
+ ...(STELLAR_PAYEE_ADDRESS
+ ? {
+ "GET /exact/stellar": {
+ accepts: {
+ payTo: STELLAR_PAYEE_ADDRESS!,
+ scheme: "exact",
+ price: "$0.001",
+ network: STELLAR_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected Stellar endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
+ },
+ },
+ }),
+ },
+ },
+ }
+ : {}),
},
server, // Pass pre-configured server instance
),
@@ -238,7 +381,7 @@ app.use(
* This endpoint demonstrates a resource protected by x402 payment middleware.
* Clients must provide a valid payment signature to access this endpoint.
*/
-app.get("/protected", (req, res) => {
+app.get("/exact/evm/eip3009", (req, res) => {
res.json({
message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -251,7 +394,7 @@ app.get("/protected", (req, res) => {
* This endpoint demonstrates a resource protected by x402 payment middleware for SVM.
* Clients must provide a valid payment signature to access this endpoint.
*/
-app.get("/protected-svm", (req, res) => {
+app.get("/exact/svm", (req, res) => {
res.json({
message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -265,7 +408,7 @@ app.get("/protected-svm", (req, res) => {
* Clients must provide a valid payment signature to access this endpoint.
* Note: 501 check is handled by pre-middleware guard above.
*/
-app.get("/protected-aptos", (req, res) => {
+app.get("/exact/aptos", (req, res) => {
res.json({
message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -279,7 +422,7 @@ app.get("/protected-aptos", (req, res) => {
* that do NOT implement EIP-2612. The facilitator broadcasts the pre-signed
* approve() transaction on the client's behalf before settling.
*/
-app.get("/protected-permit2-erc20", (req, res) => {
+app.get("/exact/evm/permit2-erc20ApprovalGasSponsoring", (req, res) => {
res.json({
message: "Permit2 ERC-20 approval endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -293,7 +436,7 @@ app.get("/protected-permit2-erc20", (req, res) => {
* This endpoint demonstrates the Permit2 payment flow.
* Clients must have approved Permit2 to spend their USDC before accessing.
*/
-app.get("/protected-permit2", (req, res) => {
+app.get("/exact/evm/permit2", (req, res) => {
res.json({
message: "Permit2 endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -301,6 +444,62 @@ app.get("/protected-permit2", (req, res) => {
});
});
+/**
+ * Protected Permit2 EIP-2612 endpoint - requires payment via Permit2 with gas sponsoring
+ *
+ * Uses EIP-2612 permit atomically in settleWithPermit. No pre-approval needed.
+ */
+app.get("/exact/evm/permit2-eip2612GasSponsoring", (req, res) => {
+ res.json({
+ message: "Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "permit2-eip2612",
+ });
+});
+
+app.get("/upto/evm/permit2", (req, res) => {
+ setSettlementOverrides(res, { amount: "1000" });
+ res.json({
+ message: "Upto Permit2 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2",
+ });
+});
+
+app.get("/upto/evm/permit2-eip2612GasSponsoring", (req, res) => {
+ setSettlementOverrides(res, { amount: "1000" });
+ res.json({
+ message: "Upto Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2-eip2612",
+ });
+});
+
+app.get("/upto/evm/permit2-erc20ApprovalGasSponsoring", (req, res) => {
+ setSettlementOverrides(res, { amount: "1000" });
+ res.json({
+ message: "Upto Permit2 ERC-20 approval endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2-erc20-approval",
+ });
+});
+
+/**
+ * Protected Stellar endpoint - requires payment to access
+ *
+ * This endpoint demonstrates a resource protected by x402 payment middleware for Stellar.
+ * Clients must provide a valid payment signature to access this endpoint.
+ * Note: 501 check is handled by pre-middleware guard above.
+ */
+if (STELLAR_PAYEE_ADDRESS) {
+ app.get("/exact/stellar", (req, res) => {
+ res.json({
+ message: "Protected Stellar endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ });
+ });
+}
+
/**
* Health check endpoint - no payment required
*
@@ -340,16 +539,20 @@ app.listen(parseInt(PORT), () => {
║ EVM Network: ${EVM_NETWORK} ║
║ SVM Network: ${SVM_NETWORK} ║
║ Aptos Network: ${APTOS_NETWORK} ║
+║ Stellar Network: ${STELLAR_NETWORK}║
║ EVM Payee: ${EVM_PAYEE_ADDRESS} ║
║ SVM Payee: ${SVM_PAYEE_ADDRESS} ║
║ Aptos Payee: ${APTOS_PAYEE_ADDRESS || "(not configured)"}
+║ Stellar Payee: ${STELLAR_PAYEE_ADDRESS || "(not configured)"}
║ ║
║ Endpoints: ║
-║ • GET /protected (EIP-3009 payment - EVM) ║
-║ • GET /protected-svm (SVM payment) ║
-║ • GET /protected-aptos (Aptos payment) ║
-║ • GET /protected-permit2 (Permit2 payment - EVM) ║
-║ • GET /protected-permit2-erc20 (Permit2 + ERC-20 approval) ║
+║ • GET /exact/evm/eip3009 (EVM EIP-3009) ║
+║ • GET /exact/evm/permit2 (Permit2) ║
+║ • GET /exact/evm/permit2-eip2612GasSponsoring ║
+║ • GET /exact/evm/permit2-erc20ApprovalGasSponsoring ║
+║ • GET /exact/svm (SVM) ║
+║ • GET /exact/aptos (Aptos) ║
+║ • GET /exact/stellar (Stellar) ║
║ • GET /health (no payment required) ║
║ • POST /close (shutdown server) ║
╚════════════════════════════════════════════════════════╝
diff --git a/e2e/servers/express/package.json b/e2e/servers/express/package.json
index eee39400e0..4756535ea6 100644
--- a/e2e/servers/express/package.json
+++ b/e2e/servers/express/package.json
@@ -15,6 +15,7 @@
"@x402/express": "workspace:*",
"@x402/evm": "workspace:*",
"@x402/extensions": "workspace:*",
+ "@x402/stellar": "workspace:*",
"@x402/svm": "workspace:*",
"dotenv": "^16.6.1",
"express": "^4.18.2"
@@ -33,4 +34,4 @@
"tsx": "^4.7.0",
"typescript": "^5.3.0"
}
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/express/test.config.json b/e2e/servers/express/test.config.json
index 52deba0120..188ab91bbf 100644
--- a/e2e/servers/express/test.config.json
+++ b/e2e/servers/express/test.config.json
@@ -3,14 +3,11 @@
"type": "server",
"language": "typescript",
"x402Version": 2,
- "extensions": [
- "bazaar",
- "eip2612GasSponsoring",
- "erc20ApprovalGasSponsoring"
- ],
+ "extensions": ["bazaar", "eip2612GasSponsoring", "erc20ApprovalGasSponsoring"],
+
"endpoints": [
{
- "path": "/protected",
+ "path": "/exact/evm/eip3009",
"method": "GET",
"description": "Protected endpoint requiring EIP-3009 payment",
"requiresPayment": true,
@@ -18,36 +15,82 @@
"transferMethod": "eip3009"
},
{
- "path": "/protected-permit2",
+ "path": "/exact/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment (standard settle, no gas sponsoring)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "permit2Direct": true
+ },
+ {
+ "path": "/exact/evm/permit2-eip2612GasSponsoring",
"method": "GET",
- "description": "Protected endpoint requiring Permit2 payment",
+ "description": "Protected endpoint requiring Permit2 payment with EIP-2612 gas sponsoring",
"requiresPayment": true,
"protocolFamily": "evm",
- "transferMethod": "permit2"
+ "transferMethod": "permit2",
+ "coldstart": true
},
{
- "path": "/protected-permit2-erc20",
+ "path": "/exact/evm/permit2-erc20ApprovalGasSponsoring",
"method": "GET",
"description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring",
"requiresPayment": true,
"protocolFamily": "evm",
"transferMethod": "permit2",
- "extensions": ["erc20ApprovalGasSponsoring"]
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
},
{
- "path": "/protected-svm",
+ "path": "/upto/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring Upto Permit2 payment (standard settle, no gas sponsoring)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "permit2Direct": true
+ },
+ {
+ "path": "/upto/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Upto Permit2 payment with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "coldstart": true
+ },
+ {
+ "path": "/upto/evm/permit2-erc20ApprovalGasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Upto Permit2 payment with ERC-20 approval gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
+ },
+ {
+ "path": "/exact/svm",
"method": "GET",
"description": "Protected endpoint requiring payment on SVM network",
"requiresPayment": true,
"protocolFamily": "svm"
},
{
- "path": "/protected-aptos",
+ "path": "/exact/aptos",
"method": "GET",
"description": "Protected endpoint requiring payment on Aptos network",
"requiresPayment": true,
"protocolFamily": "aptos"
},
+ {
+ "path": "/exact/stellar",
+ "method": "GET",
+ "description": "Protected endpoint requiring payment on Stellar network",
+ "requiresPayment": true,
+ "protocolFamily": "stellar"
+ },
{
"path": "/health",
"method": "GET",
@@ -62,14 +105,7 @@
}
],
"environment": {
- "required": [
- "PORT",
- "EVM_PAYEE_ADDRESS",
- "SVM_PAYEE_ADDRESS",
- "FACILITATOR_URL"
- ],
- "optional": [
- "APTOS_PAYEE_ADDRESS"
- ]
+ "required": ["PORT", "EVM_PAYEE_ADDRESS", "SVM_PAYEE_ADDRESS", "FACILITATOR_URL"],
+ "optional": ["APTOS_PAYEE_ADDRESS", "STELLAR_PAYEE_ADDRESS"]
}
}
diff --git a/e2e/servers/fastapi/build.sh b/e2e/servers/fastapi/build.sh
index f5fbe5e8f8..c1bb071bbe 100755
--- a/e2e/servers/fastapi/build.sh
+++ b/e2e/servers/fastapi/build.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Python doesn't require a build step
-# This file is intentionally empty
-exit 0
+set -e
+# Rebuild the local x402 editable dependency so the venv reflects source changes
+uv sync --reinstall-package x402
diff --git a/e2e/servers/fastapi/main.py b/e2e/servers/fastapi/main.py
index 1d582a8823..1cfce7bedf 100644
--- a/e2e/servers/fastapi/main.py
+++ b/e2e/servers/fastapi/main.py
@@ -22,6 +22,10 @@
declare_discovery_extension,
OutputConfig,
)
+from x402.extensions.eip2612_gas_sponsoring import declare_eip2612_gas_sponsoring_extension
+from x402.extensions.erc20_approval_gas_sponsoring import (
+ declare_erc20_approval_gas_sponsoring_extension,
+)
# Load environment variables
load_dotenv()
@@ -31,6 +35,9 @@
SVM_ADDRESS = os.getenv("SVM_PAYEE_ADDRESS")
PORT = int(os.getenv("PORT", "4021"))
FACILITATOR_URL = os.getenv("FACILITATOR_URL")
+EVM_PERMIT2_ASSET = os.getenv(
+ "EVM_PERMIT2_ASSET", "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
+)
if not EVM_ADDRESS:
print("Error: Missing required environment variable EVM_PAYEE_ADDRESS")
@@ -67,7 +74,7 @@
# Define routes with payment requirements
routes = {
- "GET /protected": {
+ "GET /exact/evm/eip3009": {
"accepts": {
"scheme": "exact",
"payTo": EVM_ADDRESS,
@@ -92,18 +99,18 @@
),
},
},
- "GET /protected-2": {
+ "GET /exact/svm": {
"accepts": {
"scheme": "exact",
- "payTo": EVM_ADDRESS,
- "price": "$0.001", # 0.001 USDC
- "network": EVM_NETWORK,
+ "payTo": SVM_ADDRESS,
+ "price": "$0.001",
+ "network": SVM_NETWORK,
},
"extensions": {
**declare_discovery_extension(
output=OutputConfig(
example={
- "message": "Access granted to protected resource #2",
+ "message": "Access granted to SVM protected resource",
"timestamp": "2024-01-01T00:00:00Z",
},
schema={
@@ -117,29 +124,55 @@
),
},
},
- "GET /protected-svm": {
+ "GET /exact/evm/permit2-eip2612GasSponsoring": {
"accepts": {
"scheme": "exact",
- "payTo": SVM_ADDRESS,
- "price": "$0.001",
- "network": SVM_NETWORK,
+ "payTo": EVM_ADDRESS,
+ "network": EVM_NETWORK,
+ "price": {
+ "amount": "1000",
+ "asset": EVM_PERMIT2_ASSET,
+ "extra": {
+ "assetTransferMethod": "permit2",
+ "name": "USDC",
+ "version": "2",
+ },
+ },
},
"extensions": {
**declare_discovery_extension(
output=OutputConfig(
example={
- "message": "Access granted to SVM protected resource",
+ "message": "Permit2 endpoint accessed successfully",
"timestamp": "2024-01-01T00:00:00Z",
+ "method": "permit2",
},
schema={
"properties": {
"message": {"type": "string"},
"timestamp": {"type": "string"},
+ "method": {"type": "string"},
},
"required": ["message", "timestamp"],
},
)
),
+ **declare_eip2612_gas_sponsoring_extension(),
+ },
+ },
+ "GET /exact/evm/permit2-erc20ApprovalGasSponsoring": {
+ "accepts": {
+ "scheme": "exact",
+ "payTo": EVM_ADDRESS,
+ "network": EVM_NETWORK,
+ "price": {
+ "amount": "1000",
+ "asset": EVM_PERMIT2_ASSET,
+ "extra": {"assetTransferMethod": "permit2"},
+ },
+ },
+ "extensions": {
+ **declare_erc20_approval_gas_sponsoring_extension(),
},
},
}
@@ -155,7 +188,7 @@ async def x402_payment_middleware(request, call_next):
shutdown_requested = False
-@app.get("/protected")
+@app.get("/exact/evm/eip3009")
async def protected_endpoint() -> Dict[str, Any]:
"""Protected endpoint that requires payment."""
if shutdown_requested:
@@ -167,27 +200,41 @@ async def protected_endpoint() -> Dict[str, Any]:
}
-@app.get("/protected-2")
-async def protected_endpoint_2() -> Dict[str, Any]:
- """Protected endpoint that requires ERC20 payment."""
+@app.get("/exact/svm")
+async def protected_svm_endpoint() -> Dict[str, Any]:
+ """Protected endpoint that requires SVM (Solana) payment."""
if shutdown_requested:
raise HTTPException(status_code=503, detail="Server shutting down")
return {
- "message": "Access granted to protected resource #2",
+ "message": "Access granted to SVM protected resource",
"timestamp": "2024-01-01T00:00:00Z",
}
-@app.get("/protected-svm")
-async def protected_svm_endpoint() -> Dict[str, Any]:
- """Protected endpoint that requires SVM (Solana) payment."""
+@app.get("/exact/evm/permit2-eip2612GasSponsoring")
+async def protected_permit2_endpoint() -> Dict[str, Any]:
+ """Protected endpoint that requires Permit2 payment."""
if shutdown_requested:
raise HTTPException(status_code=503, detail="Server shutting down")
return {
- "message": "Access granted to SVM protected resource",
+ "message": "Permit2 endpoint accessed successfully",
+ "timestamp": "2024-01-01T00:00:00Z",
+ "method": "permit2",
+ }
+
+
+@app.get("/exact/evm/permit2-erc20ApprovalGasSponsoring")
+async def protected_permit2_erc20_endpoint() -> Dict[str, Any]:
+ """Protected endpoint that requires Permit2 payment with ERC-20 approval sponsoring."""
+ if shutdown_requested:
+ raise HTTPException(status_code=503, detail="Server shutting down")
+
+ return {
+ "message": "Permit2+ERC20Approval endpoint accessed successfully",
"timestamp": "2024-01-01T00:00:00Z",
+ "method": "permit2+erc20approval",
}
diff --git a/e2e/servers/fastapi/run.sh b/e2e/servers/fastapi/run.sh
index 31c1f93486..c653d856a9 100644
--- a/e2e/servers/fastapi/run.sh
+++ b/e2e/servers/fastapi/run.sh
@@ -1,4 +1,3 @@
#!/bin/bash
-# Ensure dependencies are synced before running
-uv sync --quiet
+uv sync --reinstall-package x402 --quiet
uv run python main.py
diff --git a/e2e/servers/fastapi/test.config.json b/e2e/servers/fastapi/test.config.json
index 6a532a64f7..98be7db4af 100644
--- a/e2e/servers/fastapi/test.config.json
+++ b/e2e/servers/fastapi/test.config.json
@@ -4,32 +4,47 @@
"language": "python",
"x402Version": 2,
"extensions": [
- "bazaar"
+ "bazaar",
+ "eip2612GasSponsoring",
+ "erc20ApprovalGasSponsoring"
],
+ "evm": { "transferMethods": ["eip3009", "permit2"] },
"description": "Python FastAPI server with x402 v2 payment middleware",
"endpoints": [
{
- "path": "/protected",
+ "path": "/exact/evm/eip3009",
"method": "GET",
- "description": "Protected endpoint requiring payment",
+ "description": "Protected endpoint requiring EIP-3009 payment",
"requiresPayment": true,
"protocolFamily": "evm",
"transferMethod": "eip3009"
},
{
- "path": "/protected-2",
+ "path": "/exact/svm",
"method": "GET",
- "description": "Protected endpoint requiring ERC20 payment",
+ "description": "Protected endpoint requiring SVM (Solana) payment",
+ "requiresPayment": true,
+ "protocolFamily": "svm"
+ },
+ {
+ "path": "/exact/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with EIP-2612 gas sponsoring",
"requiresPayment": true,
"protocolFamily": "evm",
- "transferMethod": "eip3009"
+ "transferMethod": "permit2",
+ "extensions": ["eip2612GasSponsoring"],
+ "coldstart": true
},
{
- "path": "/protected-svm",
+ "path": "/exact/evm/permit2-erc20ApprovalGasSponsoring",
"method": "GET",
- "description": "Protected endpoint requiring SVM (Solana) payment",
+ "description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring",
"requiresPayment": true,
- "protocolFamily": "svm"
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
},
{
"path": "/health",
@@ -51,7 +66,8 @@
],
"optional": [
"PORT",
- "FACILITATOR_URL"
+ "FACILITATOR_URL",
+ "EVM_PERMIT2_ASSET"
]
}
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/fastapi/uv.lock b/e2e/servers/fastapi/uv.lock
index fc87dfbe5b..d3fe5a45b0 100644
--- a/e2e/servers/fastapi/uv.lock
+++ b/e2e/servers/fastapi/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -2841,7 +2841,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.2.0"
+version = "2.5.0"
source = { editable = "../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
@@ -2872,7 +2872,7 @@ svm = [
[package.metadata]
requires-dist = [
{ name = "eth-abi", marker = "extra == 'evm'", specifier = ">=5.0.0" },
- { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.12.0" },
+ { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.13.0" },
{ name = "eth-keys", marker = "extra == 'evm'", specifier = ">=0.5.0" },
{ name = "eth-utils", marker = "extra == 'evm'", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], marker = "extra == 'fastapi'", specifier = ">=0.115.0" },
@@ -2899,7 +2899,7 @@ provides-extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp",
dev = [
{ name = "black", specifier = ">=23.0.0" },
{ name = "eth-abi", specifier = ">=5.0.0" },
- { name = "eth-account", specifier = ">=0.12.0" },
+ { name = "eth-account", specifier = ">=0.13.0" },
{ name = "eth-keys", specifier = ">=0.5.0" },
{ name = "eth-utils", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
diff --git a/e2e/servers/fastify/.prettierignore b/e2e/servers/fastify/.prettierignore
new file mode 100644
index 0000000000..5bd240ba90
--- /dev/null
+++ b/e2e/servers/fastify/.prettierignore
@@ -0,0 +1,8 @@
+docs/
+dist/
+node_modules/
+coverage/
+.github/
+src/client
+**/**/*.json
+*.md
\ No newline at end of file
diff --git a/e2e/servers/fastify/.prettierrc b/e2e/servers/fastify/.prettierrc
new file mode 100644
index 0000000000..ffb416b74b
--- /dev/null
+++ b/e2e/servers/fastify/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": false,
+ "trailingComma": "all",
+ "bracketSpacing": true,
+ "arrowParens": "avoid",
+ "printWidth": 100,
+ "proseWrap": "never"
+}
diff --git a/e2e/servers/fastify/build.sh b/e2e/servers/fastify/build.sh
new file mode 100755
index 0000000000..b61bea4a5b
--- /dev/null
+++ b/e2e/servers/fastify/build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+# TypeScript build handled by pnpm at root level
+# This file is intentionally empty
+exit 0
diff --git a/e2e/servers/fastify/eslint.config.js b/e2e/servers/fastify/eslint.config.js
new file mode 100644
index 0000000000..e2fde7b3b8
--- /dev/null
+++ b/e2e/servers/fastify/eslint.config.js
@@ -0,0 +1,73 @@
+import js from "@eslint/js";
+import ts from "@typescript-eslint/eslint-plugin";
+import tsParser from "@typescript-eslint/parser";
+import prettier from "eslint-plugin-prettier";
+import jsdoc from "eslint-plugin-jsdoc";
+import importPlugin from "eslint-plugin-import";
+
+export default [
+ {
+ ignores: ["dist/**", "node_modules/**"],
+ },
+ {
+ files: ["**/*.ts"],
+ languageOptions: {
+ parser: tsParser,
+ sourceType: "module",
+ ecmaVersion: 2020,
+ globals: {
+ process: "readonly",
+ __dirname: "readonly",
+ module: "readonly",
+ require: "readonly",
+ Buffer: "readonly",
+ console: "readonly",
+ exports: "readonly",
+ setTimeout: "readonly",
+ clearTimeout: "readonly",
+ setInterval: "readonly",
+ clearInterval: "readonly",
+ },
+ },
+ plugins: {
+ "@typescript-eslint": ts,
+ prettier: prettier,
+ jsdoc: jsdoc,
+ import: importPlugin,
+ },
+ rules: {
+ ...ts.configs.recommended.rules,
+ "import/first": "error",
+ "prettier/prettier": "error",
+ "@typescript-eslint/member-ordering": "error",
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }],
+ "jsdoc/tag-lines": ["error", "any", { startLines: 1 }],
+ "jsdoc/check-alignment": "error",
+ "jsdoc/no-undefined-types": "off",
+ "jsdoc/check-param-names": "error",
+ "jsdoc/check-tag-names": "error",
+ "jsdoc/check-types": "error",
+ "jsdoc/implements-on-classes": "error",
+ "jsdoc/require-description": "error",
+ "jsdoc/require-jsdoc": [
+ "error",
+ {
+ require: {
+ FunctionDeclaration: true,
+ MethodDefinition: true,
+ ClassDeclaration: true,
+ ArrowFunctionExpression: false,
+ FunctionExpression: false,
+ },
+ },
+ ],
+ "jsdoc/require-param": "error",
+ "jsdoc/require-param-description": "error",
+ "jsdoc/require-param-type": "off",
+ "jsdoc/require-returns": "error",
+ "jsdoc/require-returns-description": "error",
+ "jsdoc/require-returns-type": "off",
+ "jsdoc/require-hyphen-before-param-description": ["error", "always"],
+ },
+ },
+];
diff --git a/e2e/servers/fastify/index.ts b/e2e/servers/fastify/index.ts
new file mode 100644
index 0000000000..4ba0718ed2
--- /dev/null
+++ b/e2e/servers/fastify/index.ts
@@ -0,0 +1,552 @@
+import Fastify from "fastify";
+import { paymentMiddleware, setSettlementOverrides } from "@x402/fastify";
+import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
+import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { UptoEvmScheme } from "@x402/evm/upto/server";
+import { ExactSvmScheme } from "@x402/svm/exact/server";
+import { ExactAptosScheme } from "@x402/aptos/exact/server";
+import { ExactStellarScheme } from "@x402/stellar/exact/server";
+import { bazaarResourceServerExtension, declareDiscoveryExtension } from "@x402/extensions/bazaar";
+import {
+ declareEip2612GasSponsoringExtension,
+ declareErc20ApprovalGasSponsoringExtension,
+} from "@x402/extensions";
+import dotenv from "dotenv";
+
+dotenv.config();
+
+/**
+ * Fastify E2E Test Server with x402 Payment Middleware
+ *
+ * This server demonstrates how to integrate x402 payment middleware
+ * with a Fastify application for end-to-end testing.
+ */
+
+const PORT = process.env.PORT || "4024";
+const EVM_NETWORK = (process.env.EVM_NETWORK || "eip155:84532") as `${string}:${string}`;
+const SVM_NETWORK = (process.env.SVM_NETWORK ||
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") as `${string}:${string}`;
+const APTOS_NETWORK = (process.env.APTOS_NETWORK || "aptos:2") as `${string}:${string}`;
+const STELLAR_NETWORK = (process.env.STELLAR_NETWORK || "stellar:testnet") as `${string}:${string}`;
+const EVM_PAYEE_ADDRESS = process.env.EVM_PAYEE_ADDRESS as `0x${string}`;
+const SVM_PAYEE_ADDRESS = process.env.SVM_PAYEE_ADDRESS as string;
+const EVM_PERMIT2_ASSET = process.env.EVM_PERMIT2_ASSET as `0x${string}`;
+const APTOS_PAYEE_ADDRESS = process.env.APTOS_PAYEE_ADDRESS as string;
+const STELLAR_PAYEE_ADDRESS = process.env.STELLAR_PAYEE_ADDRESS as string | undefined;
+const facilitatorUrl = process.env.FACILITATOR_URL;
+
+if (!EVM_PAYEE_ADDRESS) {
+ console.error("❌ EVM_PAYEE_ADDRESS environment variable is required");
+ process.exit(1);
+}
+
+if (!SVM_PAYEE_ADDRESS) {
+ console.error("❌ SVM_PAYEE_ADDRESS environment variable is required");
+ process.exit(1);
+}
+
+if (!facilitatorUrl) {
+ console.error("❌ FACILITATOR_URL environment variable is required");
+ process.exit(1);
+}
+
+// Initialize Fastify app
+const app = Fastify();
+
+// Create HTTP facilitator client
+const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+
+// Create x402 resource server
+const server = new x402ResourceServer(facilitatorClient);
+
+// Register server schemes
+server.register("eip155:*", new ExactEvmScheme());
+server.register("eip155:*", new UptoEvmScheme());
+server.register("solana:*", new ExactSvmScheme());
+if (APTOS_PAYEE_ADDRESS) {
+ server.register("aptos:*", new ExactAptosScheme());
+}
+if (STELLAR_PAYEE_ADDRESS) {
+ server.register("stellar:*", new ExactStellarScheme());
+}
+
+// Register Bazaar discovery extension
+server.registerExtension(bazaarResourceServerExtension);
+
+console.log(
+ `Facilitator account: ${process.env.EVM_PRIVATE_KEY ? process.env.EVM_PRIVATE_KEY.substring(0, 10) + "..." : "not configured"}`,
+);
+console.log(`Using remote facilitator at: ${facilitatorUrl}`);
+
+/**
+ * Pre-middleware guard for optional Aptos / Stellar endpoints
+ * Returns 501 Not Implemented if not configured
+ */
+app.addHook("onRequest", async (request, reply) => {
+ const path = request.url.split("?")[0];
+ if (path === "/exact/aptos" && !APTOS_PAYEE_ADDRESS) {
+ return reply.status(501).send({
+ error: "Aptos payments not configured",
+ message: "APTOS_PAYEE_ADDRESS environment variable is not set",
+ });
+ }
+ if (path.startsWith("/exact/stellar") && !STELLAR_PAYEE_ADDRESS) {
+ return reply.status(501).send({
+ error: "Stellar payments not configured",
+ message: "STELLAR_PAYEE_ADDRESS environment variable is not set",
+ });
+ }
+});
+
+/**
+ * Configure x402 payment middleware using builder pattern
+ *
+ * This middleware protects endpoints with $0.001 USDC payment requirements
+ * on Base Sepolia, Solana Devnet, Aptos Testnet, and Stellar Testnet with bazaar discovery extension.
+ */
+paymentMiddleware(
+ app,
+ {
+ // Route-specific payment configuration
+ "GET /exact/evm/eip3009": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "exact",
+ price: "$0.001",
+ network: EVM_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
+ },
+ },
+ }),
+ },
+ },
+ "GET /exact/svm": {
+ accepts: {
+ payTo: SVM_PAYEE_ADDRESS,
+ scheme: "exact",
+ price: "$0.001",
+ network: SVM_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
+ },
+ },
+ }),
+ },
+ },
+ ...(APTOS_PAYEE_ADDRESS
+ ? {
+ "GET /exact/aptos": {
+ accepts: {
+ payTo: APTOS_PAYEE_ADDRESS,
+ scheme: "exact",
+ price: "$0.001",
+ network: APTOS_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
+ },
+ },
+ }),
+ },
+ },
+ }
+ : {}),
+ // Permit2 standard/direct endpoint - no gas sponsoring, client must pre-approve Permit2
+ "GET /exact/evm/permit2": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "exact",
+ network: EVM_NETWORK,
+ price: {
+ amount: "1000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Permit2 endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ method: "permit2",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ method: { type: "string" },
+ },
+ required: ["message", "timestamp", "method"],
+ },
+ },
+ }),
+ },
+ },
+ // Permit2 endpoint with EIP-2612 gas sponsoring
+ "GET /exact/evm/permit2-eip2612GasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "exact",
+ network: EVM_NETWORK,
+ price: "$0.001",
+ extra: { assetTransferMethod: "permit2" },
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ method: "permit2-eip2612",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ method: { type: "string" },
+ },
+ required: ["message", "timestamp", "method"],
+ },
+ },
+ }),
+ ...declareEip2612GasSponsoringExtension(),
+ },
+ },
+ // Permit2 endpoint for ERC-20 approval gas sponsoring (no EIP-2612)
+ "GET /exact/evm/permit2-erc20ApprovalGasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "exact",
+ network: EVM_NETWORK,
+ price: {
+ amount: "1000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ },
+ },
+ },
+ extensions: {
+ ...declareErc20ApprovalGasSponsoringExtension(),
+ },
+ },
+ // Upto Permit2 direct endpoint - client must have Permit2 pre-approved
+ "GET /upto/evm/permit2": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ },
+ // Upto Permit2 endpoint with EIP-2612 gas sponsoring
+ "GET /upto/evm/permit2-eip2612GasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ extensions: {
+ ...declareEip2612GasSponsoringExtension(),
+ },
+ },
+ // Upto Permit2 endpoint for ERC-20 approval gas sponsoring
+ "GET /upto/evm/permit2-erc20ApprovalGasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ },
+ },
+ },
+ extensions: {
+ ...declareErc20ApprovalGasSponsoringExtension(),
+ },
+ },
+ ...(STELLAR_PAYEE_ADDRESS
+ ? {
+ "GET /exact/stellar": {
+ accepts: {
+ payTo: STELLAR_PAYEE_ADDRESS!,
+ scheme: "exact",
+ price: "$0.001",
+ network: STELLAR_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected Stellar endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
+ },
+ },
+ }),
+ },
+ },
+ }
+ : {}),
+ },
+ server, // Pass pre-configured server instance
+);
+
+/**
+ * Protected endpoint - requires payment to access
+ *
+ * This endpoint demonstrates a resource protected by x402 payment middleware.
+ * Clients must provide a valid payment signature to access this endpoint.
+ */
+app.get("/exact/evm/eip3009", async () => {
+ return {
+ message: "Protected endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ };
+});
+
+/**
+ * Protected SVM endpoint - requires payment to access
+ *
+ * This endpoint demonstrates a resource protected by x402 payment middleware for SVM.
+ * Clients must provide a valid payment signature to access this endpoint.
+ */
+app.get("/exact/svm", async () => {
+ return {
+ message: "Protected endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ };
+});
+
+/**
+ * Protected Aptos endpoint - requires payment to access
+ *
+ * This endpoint demonstrates a resource protected by x402 payment middleware for Aptos.
+ * Clients must provide a valid payment signature to access this endpoint.
+ * Note: 501 check is handled by pre-middleware guard above.
+ */
+app.get("/exact/aptos", async () => {
+ return {
+ message: "Protected endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ };
+});
+
+/**
+ * Protected Permit2 endpoint - standard settle (no gas sponsoring)
+ */
+app.get("/exact/evm/permit2", async () => {
+ return {
+ message: "Permit2 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "permit2",
+ };
+});
+
+/**
+ * Protected Permit2 EIP-2612 endpoint - requires Permit2 with gas sponsoring
+ */
+app.get("/exact/evm/permit2-eip2612GasSponsoring", async () => {
+ return {
+ message: "Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "permit2-eip2612",
+ };
+});
+
+/**
+ * Protected Permit2 ERC-20 endpoint - requires payment via Permit2 flow with ERC-20 approval
+ *
+ * This endpoint demonstrates the ERC-20 approval gas sponsoring flow for tokens
+ * that do NOT implement EIP-2612. The facilitator broadcasts the pre-signed
+ * approve() transaction on the client's behalf before settling.
+ */
+app.get("/exact/evm/permit2-erc20ApprovalGasSponsoring", async () => {
+ return {
+ message: "Permit2 ERC-20 approval endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "permit2-erc20-approval",
+ };
+});
+
+/**
+ * Upto Permit2 direct endpoint - upto scheme, client must pre-approve Permit2
+ */
+app.get("/upto/evm/permit2", async (_request, reply) => {
+ setSettlementOverrides(reply, { amount: "1000" });
+ return {
+ message: "Upto Permit2 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2",
+ };
+});
+
+/**
+ * Upto Permit2 EIP-2612 endpoint - upto scheme with gas sponsoring
+ */
+app.get("/upto/evm/permit2-eip2612GasSponsoring", async (_request, reply) => {
+ setSettlementOverrides(reply, { amount: "1000" });
+ return {
+ message: "Upto Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2-eip2612",
+ };
+});
+
+/**
+ * Upto Permit2 ERC-20 approval endpoint - upto scheme with ERC-20 gas sponsoring
+ */
+app.get("/upto/evm/permit2-erc20ApprovalGasSponsoring", async (_request, reply) => {
+ setSettlementOverrides(reply, { amount: "1000" });
+ return {
+ message: "Upto Permit2 ERC-20 approval endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2-erc20-approval",
+ };
+});
+
+/**
+ * Protected Stellar endpoint - requires payment to access
+ *
+ * This endpoint demonstrates a resource protected by x402 payment middleware for Stellar.
+ * Clients must provide a valid payment signature to access this endpoint.
+ * Note: 501 check is handled by pre-middleware guard above.
+ */
+if (STELLAR_PAYEE_ADDRESS) {
+ app.get("/exact/stellar", async () => {
+ return {
+ message: "Protected Stellar endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ };
+ });
+}
+
+/**
+ * Health check endpoint - no payment required
+ *
+ * Used to verify the server is running and responsive.
+ */
+app.get("/health", async () => {
+ return {
+ status: "ok",
+ network: EVM_NETWORK,
+ payee: EVM_PAYEE_ADDRESS,
+ version: "2.0.0",
+ };
+});
+
+/**
+ * Shutdown endpoint - used by e2e tests
+ *
+ * Allows graceful shutdown of the server during testing.
+ */
+app.post("/close", async (request, reply) => {
+ reply.send({ message: "Server shutting down gracefully" });
+ console.log("Received shutdown request");
+
+ // Give time for response to be sent
+ setTimeout(() => {
+ process.exit(0);
+ }, 100);
+});
+
+// Start the server
+app.listen({ port: parseInt(PORT) }, (err, address) => {
+ if (err) {
+ console.error(err);
+ process.exit(1);
+ }
+ console.log(`
+╔════════════════════════════════════════════════════════╗
+║ x402 Fastify E2E Test Server ║
+╠════════════════════════════════════════════════════════╣
+║ Server: ${address} ║
+║ EVM Network: ${EVM_NETWORK} ║
+║ SVM Network: ${SVM_NETWORK} ║
+║ Aptos Network: ${APTOS_NETWORK} ║
+║ Stellar Network: ${STELLAR_NETWORK}║
+║ EVM Payee: ${EVM_PAYEE_ADDRESS} ║
+║ SVM Payee: ${SVM_PAYEE_ADDRESS} ║
+║ Aptos Payee: ${APTOS_PAYEE_ADDRESS || "(not configured)"}
+║ Stellar Payee: ${STELLAR_PAYEE_ADDRESS || "(not configured)"}
+║ ║
+║ Endpoints: ║
+║ • GET /exact/evm/eip3009 (EVM EIP-3009) ║
+║ • GET /exact/evm/permit2 (Permit2) ║
+║ • GET /exact/evm/permit2-eip2612GasSponsoring ║
+║ • GET /exact/evm/permit2-erc20ApprovalGasSponsoring ║
+║ • GET /exact/svm (SVM) ║
+║ • GET /exact/aptos (Aptos) ║
+║ • GET /exact/stellar (Stellar) ║
+║ • GET /health (no payment required) ║
+║ • POST /close (shutdown server) ║
+╚════════════════════════════════════════════════════════╝
+ `);
+});
diff --git a/e2e/servers/fastify/install.sh b/e2e/servers/fastify/install.sh
new file mode 100755
index 0000000000..084d4490b3
--- /dev/null
+++ b/e2e/servers/fastify/install.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+# TypeScript dependencies handled by pnpm install at root level
+# This file is intentionally empty
+exit 0
diff --git a/e2e/servers/fastify/package.json b/e2e/servers/fastify/package.json
new file mode 100644
index 0000000000..690c1727e1
--- /dev/null
+++ b/e2e/servers/fastify/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "@x402/fastify-e2e",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "tsx index.ts",
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
+ "lint": "eslint . --ext .ts --fix",
+ "lint:check": "eslint . --ext .ts"
+ },
+ "dependencies": {
+ "@x402/aptos": "workspace:*",
+ "@x402/core": "workspace:*",
+ "@x402/fastify": "workspace:*",
+ "@x402/evm": "workspace:*",
+ "@x402/extensions": "workspace:*",
+ "@x402/stellar": "workspace:*",
+ "@x402/svm": "workspace:*",
+ "dotenv": "^16.6.1",
+ "fastify": "^5.3.3"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.24.0",
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
+ "@typescript-eslint/parser": "^8.29.1",
+ "eslint": "^9.24.0",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsdoc": "^50.6.9",
+ "eslint-plugin-prettier": "^5.2.6",
+ "prettier": "3.5.2",
+ "tsup": "^7.2.0",
+ "tsx": "^4.7.0",
+ "typescript": "^5.3.0"
+ }
+}
diff --git a/e2e/servers/fastify/run.sh b/e2e/servers/fastify/run.sh
new file mode 100755
index 0000000000..2cc84b562a
--- /dev/null
+++ b/e2e/servers/fastify/run.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+pnpm dev
diff --git a/e2e/servers/fastify/test.config.json b/e2e/servers/fastify/test.config.json
new file mode 100644
index 0000000000..5575e3681b
--- /dev/null
+++ b/e2e/servers/fastify/test.config.json
@@ -0,0 +1,109 @@
+{
+ "name": "fastify",
+ "type": "server",
+ "language": "typescript",
+ "x402Version": 2,
+ "extensions": ["bazaar", "eip2612GasSponsoring", "erc20ApprovalGasSponsoring"],
+
+ "endpoints": [
+ {
+ "path": "/exact/evm/eip3009",
+ "method": "GET",
+ "description": "Protected endpoint requiring EIP-3009 payment",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "eip3009"
+ },
+ {
+ "path": "/exact/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment (standard settle, no gas sponsoring)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "permit2Direct": true
+ },
+ {
+ "path": "/exact/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "coldstart": true
+ },
+ {
+ "path": "/exact/evm/permit2-erc20ApprovalGasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
+ },
+ {
+ "path": "/upto/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring upto Permit2 payment (direct, client must pre-approve)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "permit2Direct": true
+ },
+ {
+ "path": "/upto/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring upto Permit2 payment with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto"
+ },
+ {
+ "path": "/upto/evm/permit2-erc20ApprovalGasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring upto Permit2 payment with ERC-20 approval gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "extensions": ["erc20ApprovalGasSponsoring"]
+ },
+ {
+ "path": "/exact/svm",
+ "method": "GET",
+ "description": "Protected endpoint requiring payment on SVM network",
+ "requiresPayment": true,
+ "protocolFamily": "svm"
+ },
+ {
+ "path": "/exact/aptos",
+ "method": "GET",
+ "description": "Protected endpoint requiring payment on Aptos network",
+ "requiresPayment": true,
+ "protocolFamily": "aptos"
+ },
+ {
+ "path": "/exact/stellar",
+ "method": "GET",
+ "description": "Protected endpoint requiring payment on Stellar network",
+ "requiresPayment": true,
+ "protocolFamily": "stellar"
+ },
+ {
+ "path": "/health",
+ "method": "GET",
+ "description": "Health check endpoint",
+ "health": true
+ },
+ {
+ "path": "/close",
+ "method": "POST",
+ "description": "Graceful shutdown endpoint",
+ "close": true
+ }
+ ],
+ "environment": {
+ "required": ["PORT", "EVM_PAYEE_ADDRESS", "SVM_PAYEE_ADDRESS", "FACILITATOR_URL"],
+ "optional": ["APTOS_PAYEE_ADDRESS", "STELLAR_PAYEE_ADDRESS"]
+ }
+}
diff --git a/e2e/servers/fastify/tsconfig.json b/e2e/servers/fastify/tsconfig.json
new file mode 100644
index 0000000000..78f9479b1b
--- /dev/null
+++ b/e2e/servers/fastify/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ES2020",
+ "moduleResolution": "bundler",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "resolveJsonModule": true,
+ "baseUrl": ".",
+ "types": ["node"]
+ },
+ "include": ["index.ts"]
+}
diff --git a/e2e/servers/flask/build.sh b/e2e/servers/flask/build.sh
index f5fbe5e8f8..c1bb071bbe 100755
--- a/e2e/servers/flask/build.sh
+++ b/e2e/servers/flask/build.sh
@@ -1,5 +1,5 @@
#!/bin/bash
-# Python doesn't require a build step
-# This file is intentionally empty
-exit 0
+set -e
+# Rebuild the local x402 editable dependency so the venv reflects source changes
+uv sync --reinstall-package x402
diff --git a/e2e/servers/flask/main.py b/e2e/servers/flask/main.py
index 09b1b99dc7..6657dbe4ab 100644
--- a/e2e/servers/flask/main.py
+++ b/e2e/servers/flask/main.py
@@ -19,6 +19,10 @@
declare_discovery_extension,
OutputConfig,
)
+from x402.extensions.eip2612_gas_sponsoring import declare_eip2612_gas_sponsoring_extension
+from x402.extensions.erc20_approval_gas_sponsoring import (
+ declare_erc20_approval_gas_sponsoring_extension,
+)
# Configure logging to reduce verbosity
logging.getLogger("werkzeug").setLevel(logging.ERROR)
@@ -32,6 +36,9 @@
SVM_ADDRESS = os.getenv("SVM_PAYEE_ADDRESS")
PORT = int(os.getenv("PORT", "4021"))
FACILITATOR_URL = os.getenv("FACILITATOR_URL")
+EVM_PERMIT2_ASSET = os.getenv(
+ "EVM_PERMIT2_ASSET", "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
+)
if not EVM_ADDRESS:
print("Error: Missing required environment variable EVM_PAYEE_ADDRESS")
@@ -68,7 +75,7 @@
# Define routes with payment requirements
routes = {
- "GET /protected": {
+ "GET /exact/evm/eip3009": {
"accepts": {
"scheme": "exact",
"payTo": EVM_ADDRESS,
@@ -95,7 +102,7 @@
),
},
},
- "GET /protected-svm": {
+ "GET /exact/svm": {
"accepts": {
"scheme": "exact",
"payTo": SVM_ADDRESS,
@@ -120,6 +127,57 @@
),
},
},
+ "GET /exact/evm/permit2-eip2612GasSponsoring": {
+ "accepts": {
+ "scheme": "exact",
+ "payTo": EVM_ADDRESS,
+ "network": EVM_NETWORK,
+ "price": {
+ "amount": "1000",
+ "asset": EVM_PERMIT2_ASSET,
+ "extra": {
+ "assetTransferMethod": "permit2",
+ "name": "USDC",
+ "version": "2",
+ },
+ },
+ },
+ "extensions": {
+ **declare_discovery_extension(
+ output=OutputConfig(
+ example={
+ "message": "Permit2 endpoint accessed successfully",
+ "timestamp": "2024-01-01T00:00:00Z",
+ "method": "permit2",
+ },
+ schema={
+ "properties": {
+ "message": {"type": "string"},
+ "timestamp": {"type": "string"},
+ "method": {"type": "string"},
+ },
+ "required": ["message", "timestamp"],
+ },
+ )
+ ),
+ **declare_eip2612_gas_sponsoring_extension(),
+ },
+ },
+ "GET /exact/evm/permit2-erc20ApprovalGasSponsoring": {
+ "accepts": {
+ "scheme": "exact",
+ "payTo": EVM_ADDRESS,
+ "network": EVM_NETWORK,
+ "price": {
+ "amount": "1000",
+ "asset": EVM_PERMIT2_ASSET,
+ "extra": {"assetTransferMethod": "permit2"},
+ },
+ },
+ "extensions": {
+ **declare_erc20_approval_gas_sponsoring_extension(),
+ },
+ },
}
# Apply payment middleware
@@ -129,7 +187,7 @@
shutdown_requested = False
-@app.route("/protected")
+@app.route("/exact/evm/eip3009")
def protected_endpoint():
"""Protected endpoint that requires payment."""
if shutdown_requested:
@@ -144,7 +202,7 @@ def protected_endpoint():
)
-@app.route("/protected-svm")
+@app.route("/exact/svm")
def protected_svm_endpoint():
"""Protected endpoint that requires SVM (Solana) payment."""
if shutdown_requested:
@@ -158,6 +216,34 @@ def protected_svm_endpoint():
)
+@app.route("/exact/evm/permit2-eip2612GasSponsoring")
+def protected_permit2_endpoint():
+ """Protected endpoint that requires Permit2 payment."""
+ if shutdown_requested:
+ return jsonify({"error": "Server shutting down"}), 503
+ return jsonify(
+ {
+ "message": "Permit2 endpoint accessed successfully",
+ "timestamp": "2024-01-01T00:00:00Z",
+ "method": "permit2",
+ }
+ )
+
+
+@app.route("/exact/evm/permit2-erc20ApprovalGasSponsoring")
+def protected_permit2_erc20_endpoint():
+ """Protected endpoint that requires Permit2 payment with ERC-20 approval sponsoring."""
+ if shutdown_requested:
+ return jsonify({"error": "Server shutting down"}), 503
+ return jsonify(
+ {
+ "message": "Permit2+ERC20Approval endpoint accessed successfully",
+ "timestamp": "2024-01-01T00:00:00Z",
+ "method": "permit2+erc20approval",
+ }
+ )
+
+
@app.route("/health")
def health_check():
"""Health check endpoint."""
diff --git a/e2e/servers/flask/run.sh b/e2e/servers/flask/run.sh
index 31c1f93486..c653d856a9 100644
--- a/e2e/servers/flask/run.sh
+++ b/e2e/servers/flask/run.sh
@@ -1,4 +1,3 @@
#!/bin/bash
-# Ensure dependencies are synced before running
-uv sync --quiet
+uv sync --reinstall-package x402 --quiet
uv run python main.py
diff --git a/e2e/servers/flask/test.config.json b/e2e/servers/flask/test.config.json
index 4c73225cf1..3ba02fea37 100644
--- a/e2e/servers/flask/test.config.json
+++ b/e2e/servers/flask/test.config.json
@@ -4,25 +4,48 @@
"language": "python",
"x402Version": 2,
"extensions": [
- "bazaar"
+ "bazaar",
+ "eip2612GasSponsoring",
+ "erc20ApprovalGasSponsoring"
],
+ "evm": { "transferMethods": ["eip3009", "permit2"] },
"description": "Python Flask server with x402 v2 payment middleware",
"endpoints": [
{
- "path": "/protected",
+ "path": "/exact/evm/eip3009",
"method": "GET",
- "description": "Protected endpoint requiring payment",
+ "description": "Protected endpoint requiring EIP-3009 payment",
"requiresPayment": true,
"protocolFamily": "evm",
"transferMethod": "eip3009"
},
{
- "path": "/protected-svm",
+ "path": "/exact/svm",
"method": "GET",
"description": "Protected endpoint requiring SVM (Solana) payment",
"requiresPayment": true,
"protocolFamily": "svm"
},
+ {
+ "path": "/exact/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "extensions": ["eip2612GasSponsoring"],
+ "coldstart": true
+ },
+ {
+ "path": "/exact/evm/permit2-erc20ApprovalGasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
+ },
{
"path": "/health",
"method": "GET",
@@ -43,7 +66,8 @@
],
"optional": [
"PORT",
- "FACILITATOR_URL"
+ "FACILITATOR_URL",
+ "EVM_PERMIT2_ASSET"
]
}
}
diff --git a/e2e/servers/flask/uv.lock b/e2e/servers/flask/uv.lock
index 34f4b66b19..bec13acc9d 100644
--- a/e2e/servers/flask/uv.lock
+++ b/e2e/servers/flask/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -2072,7 +2072,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.2.0"
+version = "2.5.0"
source = { editable = "../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
@@ -2102,7 +2102,7 @@ svm = [
[package.metadata]
requires-dist = [
{ name = "eth-abi", marker = "extra == 'evm'", specifier = ">=5.0.0" },
- { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.12.0" },
+ { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.13.0" },
{ name = "eth-keys", marker = "extra == 'evm'", specifier = ">=0.5.0" },
{ name = "eth-utils", marker = "extra == 'evm'", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], marker = "extra == 'fastapi'", specifier = ">=0.115.0" },
@@ -2129,7 +2129,7 @@ provides-extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp",
dev = [
{ name = "black", specifier = ">=23.0.0" },
{ name = "eth-abi", specifier = ">=5.0.0" },
- { name = "eth-account", specifier = ">=0.12.0" },
+ { name = "eth-account", specifier = ">=0.13.0" },
{ name = "eth-keys", specifier = ">=0.5.0" },
{ name = "eth-utils", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
diff --git a/e2e/servers/gin/go.mod b/e2e/servers/gin/go.mod
index 74749dd523..393edfdb4f 100644
--- a/e2e/servers/gin/go.mod
+++ b/e2e/servers/gin/go.mod
@@ -12,7 +12,9 @@ require (
require (
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
+ github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
+ github.com/StackExchange/wmi v1.2.1 // indirect
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/blendle/zapdriver v1.3.1 // indirect
@@ -23,6 +25,7 @@ require (
github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
github.com/ethereum/go-ethereum v1.16.7 // indirect
@@ -33,19 +36,21 @@ require (
github.com/gagliardetto/solana-go v1.14.0 // indirect
github.com/gagliardetto/treeout v0.1.4 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
+ github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/google/uuid v1.6.0 // indirect
+ github.com/gorilla/websocket v1.4.2 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -55,8 +60,11 @@ require (
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.55.0 // indirect
+ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
+ github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
@@ -68,15 +76,15 @@ require (
go.uber.org/ratelimit v0.2.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/arch v0.20.0 // indirect
- golang.org/x/crypto v0.41.0 // indirect
- golang.org/x/mod v0.27.0 // indirect
- golang.org/x/net v0.43.0 // indirect
- golang.org/x/sync v0.16.0 // indirect
- golang.org/x/sys v0.36.0 // indirect
- golang.org/x/term v0.34.0 // indirect
- golang.org/x/text v0.28.0 // indirect
- golang.org/x/time v0.9.0 // indirect
- golang.org/x/tools v0.36.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
+ golang.org/x/mod v0.30.0 // indirect
+ golang.org/x/net v0.48.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/term v0.38.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
+ golang.org/x/time v0.14.0 // indirect
+ golang.org/x/tools v0.39.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
)
diff --git a/e2e/servers/gin/go.sum b/e2e/servers/gin/go.sum
index faaacc2034..071f4f88ab 100644
--- a/e2e/servers/gin/go.sum
+++ b/e2e/servers/gin/go.sum
@@ -2,14 +2,22 @@ filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmG
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=
github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
+github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
+github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
@@ -18,10 +26,26 @@ github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQ
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
+github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
+github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
+github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
+github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
+github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
+github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
+github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
@@ -29,6 +53,10 @@ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwz
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
+github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
+github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
+github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
@@ -37,6 +65,8 @@ github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
+github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
+github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ=
github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
@@ -55,10 +85,13 @@ github.com/gagliardetto/solana-go v1.14.0 h1:3WfAi70jOOjAJ0deFMjdhFYlLXATF4tOQXs
github.com/gagliardetto/solana-go v1.14.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
+github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
+github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -75,6 +108,10 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
+github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -86,8 +123,20 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
+github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
+github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330=
+github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg=
+github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
+github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
+github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
+github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
+github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
+github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -100,8 +149,12 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
@@ -110,9 +163,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
@@ -123,6 +175,8 @@ github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJ
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
+github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -139,17 +193,41 @@ github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
+github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
+github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
+github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
+github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
+github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
+github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
+github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
+github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
+github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
+github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
+github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
+github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
+github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
+github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
@@ -169,6 +247,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
@@ -179,6 +259,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
+github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
+github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
@@ -188,6 +270,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -216,13 +300,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
-golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
+golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -230,15 +316,16 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -246,31 +333,33 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
-golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
+golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -280,6 +369,8 @@ google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7I
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/e2e/servers/gin/main.go b/e2e/servers/gin/main.go
index 65ad0adeba..a52a7bc234 100644
--- a/e2e/servers/gin/main.go
+++ b/e2e/servers/gin/main.go
@@ -15,7 +15,8 @@ import (
"github.com/coinbase/x402/go/extensions/types"
x402http "github.com/coinbase/x402/go/http"
ginmw "github.com/coinbase/x402/go/http/gin"
- evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ exactevm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ uptoevm "github.com/coinbase/x402/go/mechanisms/evm/upto/server"
svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
ginfw "github.com/gin-gonic/gin"
"github.com/joho/godotenv"
@@ -23,12 +24,10 @@ import (
var shutdownRequested bool
-/**
- * Gin E2E Test Server with x402 v2 Payment Middleware
- *
- * This server demonstrates how to integrate x402 v2 payment middleware
- * with a Gin application for end-to-end testing.
- */
+// Gin E2E Test Server with x402 v2 Payment Middleware
+//
+// This server demonstrates how to integrate x402 v2 payment middleware
+// with a Gin application for end-to-end testing.
func main() {
// Load .env file if it exists
@@ -72,6 +71,11 @@ func main() {
evmNetwork := x402.Network(evmNetworkStr)
svmNetwork := x402.Network(svmNetworkStr)
+ evmPermit2Asset := os.Getenv("EVM_PERMIT2_ASSET")
+ if evmPermit2Asset == "" {
+ evmPermit2Asset = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
+ }
+
fmt.Printf("EVM Payee address: %s\n", evmPayeeAddress)
fmt.Printf("SVM Payee address: %s\n", svmPayeeAddress)
fmt.Printf("Using remote facilitator at: %s\n", facilitatorURL)
@@ -86,12 +90,6 @@ func main() {
URL: facilitatorURL,
})
- /**
- * Configure x402 payment middleware
- *
- * This middleware protects the /protected endpoint with a $0.001 USDC payment requirement
- * on the Base Sepolia testnet with bazaar discovery extension.
- */
// Declare bazaar discovery extension for GET endpoints
discoveryExtension, err := bazaar.DeclareDiscoveryExtension(
bazaar.MethodGET,
@@ -117,7 +115,7 @@ func main() {
}
routes := x402http.RoutesConfig{
- "GET /protected": {
+ "GET /exact/evm/eip3009": {
Accepts: x402http.PaymentOptions{
{
Scheme: "exact",
@@ -126,11 +124,11 @@ func main() {
Network: evmNetwork,
},
},
- Extensions: map[string]interface{}{
- types.BAZAAR.Key(): discoveryExtension,
+ Extensions: map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ },
},
- },
- "GET /protected-svm": {
+ "GET /exact/svm": {
Accepts: x402http.PaymentOptions{
{
Scheme: "exact",
@@ -139,31 +137,58 @@ func main() {
Network: svmNetwork,
},
},
- Extensions: map[string]interface{}{
- types.BAZAAR.Key(): discoveryExtension,
+ Extensions: map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ },
},
- },
- // Permit2 endpoint - explicitly requires Permit2 flow instead of EIP-3009
- "GET /protected-permit2": {
+ // Permit2 direct endpoint - standard settle, no gas sponsoring (client must pre-approve Permit2)
+ "GET /exact/evm/permit2": {
Accepts: x402http.PaymentOptions{
{
Scheme: "exact",
PayTo: evmPayeeAddress,
Network: evmNetwork,
- // Use pre-parsed price with assetTransferMethod to force Permit2
Price: map[string]interface{}{
- "amount": "1000", // 0.001 USDC (6 decimals)
- "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // Base Sepolia USDC
+ "amount": "1000",
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
"extra": map[string]interface{}{
"assetTransferMethod": "permit2",
},
},
},
},
- Extensions: func() map[string]interface{} {
- ext := map[string]interface{}{
+ Extensions: map[string]interface{}{
types.BAZAAR.Key(): discoveryExtension,
- }
+ },
+ },
+ // Permit2 endpoint - explicitly requires Permit2 flow instead of EIP-3009
+ "GET /exact/evm/permit2-eip2612GasSponsoring": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "1000",
+ "asset": evmPermit2Asset,
+ "extra": func() map[string]interface{} {
+ name := "USD Coin"
+ if evmNetworkStr == "eip155:84532" {
+ name = "USDC"
+ }
+ return map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ "name": name,
+ "version": "2",
+ }
+ }(),
+ },
+ },
+ },
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
// Add EIP-2612 gas sponsoring extension
for k, v := range eip2612gassponsor.DeclareEip2612GasSponsoringExtension() {
ext[k] = v
@@ -171,46 +196,73 @@ func main() {
return ext
}(),
},
- // Permit2 ERC-20 approval endpoint - requires Permit2 flow with a generic ERC-20 token (no EIP-2612)
- "GET /protected-permit2-erc20": {
- Accepts: x402http.PaymentOptions{
- {
- Scheme: "exact",
- PayTo: evmPayeeAddress,
- Network: evmNetwork,
- // Use MockGenericERC20 token that does NOT implement EIP-2612
- Price: map[string]interface{}{
- "amount": "1000", // smallest unit
- "asset": "0xeED520980fC7C7B4eB379B96d61CEdea2423005a", // MockGenericERC20 on Base Sepolia
- "extra": map[string]interface{}{
- "assetTransferMethod": "permit2",
+ "GET /upto/evm/permit2": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "upto",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "2000",
+ "asset": evmPermit2Asset,
+ "extra": map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ "name": "USDC",
+ "version": "2",
+ },
},
},
},
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
+ for k, v := range eip2612gassponsor.DeclareEip2612GasSponsoringExtension() {
+ ext[k] = v
+ }
+ return ext
+ }(),
},
- Extensions: func() map[string]interface{} {
- ext := map[string]interface{}{
- types.BAZAAR.Key(): discoveryExtension,
- }
- // Advertise ERC-20 approval gas sponsoring (for tokens without EIP-2612)
- for k, v := range erc20approvalgassponsor.DeclareExtension() {
- ext[k] = v
- }
- return ext
- }(),
- },
-}
+ // Permit2 ERC-20 approval endpoint - requires Permit2 flow with a generic ERC-20 token (no EIP-2612)
+ "GET /exact/evm/permit2-erc20ApprovalGasSponsoring": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "1000",
+ "asset": evmPermit2Asset,
+ "extra": map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ },
+ },
+ },
+ },
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
+ // Advertise ERC-20 approval gas sponsoring (for tokens without EIP-2612)
+ for k, v := range erc20approvalgassponsor.DeclareExtension() {
+ ext[k] = v
+ }
+ return ext
+ }(),
+ },
+ }
// Apply payment middleware with detailed error logging
r.Use(ginmw.X402Payment(ginmw.Config{
Routes: routes,
Facilitator: facilitatorClient,
Schemes: []ginmw.SchemeConfig{
- {Network: evmNetwork, Server: evm.NewExactEvmScheme()},
+ {Network: evmNetwork, Server: exactevm.NewExactEvmScheme()},
+ {Network: evmNetwork, Server: uptoevm.NewUptoEvmScheme()},
{Network: svmNetwork, Server: svm.NewExactSvmScheme()},
},
SyncFacilitatorOnStart: true,
- Timeout: 30 * time.Second,
+ Timeout: 30 * time.Second,
ErrorHandler: func(c *ginfw.Context, err error) {
// Log detailed error information for debugging
fmt.Printf("❌ [E2E SERVER ERROR] Payment error occurred\n")
@@ -234,13 +286,11 @@ func main() {
},
}))
- /**
- * Protected endpoint - requires payment to access
- *
- * This endpoint demonstrates a resource protected by x402 payment middleware.
- * Clients must provide a valid payment signature to access this endpoint.
- */
- r.GET("/protected", func(c *ginfw.Context) {
+ // Protected endpoint - requires payment to access
+ //
+ // This endpoint demonstrates a resource protected by x402 payment middleware.
+ // Clients must provide a valid payment signature to access this endpoint.
+ r.GET("/exact/evm/eip3009", func(c *ginfw.Context) {
if shutdownRequested {
c.JSON(http.StatusServiceUnavailable, ginfw.H{
"error": "Server shutting down",
@@ -255,13 +305,11 @@ func main() {
})
})
- /**
- * Protected SVM endpoint - requires payment to access
- *
- * This endpoint demonstrates a Solana payment protected resource.
- * Clients must provide a valid payment signature to access this endpoint.
- */
- r.GET("/protected-svm", func(c *ginfw.Context) {
+ // Protected SVM endpoint - requires payment to access
+ //
+ // This endpoint demonstrates a Solana payment protected resource.
+ // Clients must provide a valid payment signature to access this endpoint.
+ r.GET("/exact/svm", func(c *ginfw.Context) {
if shutdownRequested {
c.JSON(http.StatusServiceUnavailable, ginfw.H{
"error": "Server shutting down",
@@ -276,13 +324,8 @@ func main() {
})
})
- /**
- * Protected Permit2 endpoint - requires payment via Permit2 flow
- *
- * This endpoint demonstrates the Permit2 payment flow.
- * Clients must have approved Permit2 to spend their USDC before accessing.
- */
- r.GET("/protected-permit2", func(c *ginfw.Context) {
+ // Protected Permit2 direct endpoint - standard settle (no gas sponsoring)
+ r.GET("/exact/evm/permit2", func(c *ginfw.Context) {
if shutdownRequested {
c.JSON(http.StatusServiceUnavailable, ginfw.H{
"error": "Server shutting down",
@@ -297,12 +340,27 @@ func main() {
})
})
- /**
- * Protected Permit2 ERC-20 approval endpoint - requires payment via Permit2 flow
- * using a generic ERC-20 token that does NOT support EIP-2612.
- * The facilitator sponsors the approve(Permit2, MaxUint256) transaction.
- */
- r.GET("/protected-permit2-erc20", func(c *ginfw.Context) {
+ // Protected Permit2 EIP-2612 endpoint - requires payment via Permit2 with gas sponsoring.
+ // Uses EIP-2612 permit atomically in settleWithPermit. No pre-approval needed.
+ r.GET("/exact/evm/permit2-eip2612GasSponsoring", func(c *ginfw.Context) {
+ if shutdownRequested {
+ c.JSON(http.StatusServiceUnavailable, ginfw.H{
+ "error": "Server shutting down",
+ })
+ return
+ }
+
+ c.JSON(http.StatusOK, ginfw.H{
+ "message": "Permit2 EIP-2612 endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "permit2-eip2612",
+ })
+ })
+
+ // Protected Permit2 ERC-20 approval endpoint - requires payment via Permit2 flow
+ // using a generic ERC-20 token that does NOT support EIP-2612.
+ // The facilitator sponsors the approve(Permit2, MaxUint256) transaction.
+ r.GET("/exact/evm/permit2-erc20ApprovalGasSponsoring", func(c *ginfw.Context) {
if shutdownRequested {
c.JSON(http.StatusServiceUnavailable, ginfw.H{
"error": "Server shutting down",
@@ -317,11 +375,30 @@ func main() {
})
})
- /**
- * Health check endpoint - no payment required
- *
- * Used to verify the server is running and responsive.
- */
+ // Upto Permit2 endpoint - settles with partial amount
+ r.GET("/upto/evm/permit2", func(c *ginfw.Context) {
+ if shutdownRequested {
+ c.JSON(http.StatusServiceUnavailable, ginfw.H{
+ "error": "Server shutting down",
+ })
+ return
+ }
+
+ // Settle with partial amount (for e2e tests)
+ ginmw.SetSettlementOverrides(c, &x402.SettlementOverrides{
+ Amount: "1000",
+ })
+
+ c.JSON(http.StatusOK, ginfw.H{
+ "message": "Upto Permit2 endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "upto-permit2",
+ })
+ })
+
+ // Health check endpoint - no payment required
+ //
+ // Used to verify the server is running and responsive.
r.GET("/health", func(c *ginfw.Context) {
c.JSON(http.StatusOK, ginfw.H{
"status": "ok",
@@ -333,11 +410,9 @@ func main() {
})
})
- /**
- * Shutdown endpoint - used by e2e tests
- *
- * Allows graceful shutdown of the server during testing.
- */
+ // Shutdown endpoint - used by e2e tests
+ //
+ // Allows graceful shutdown of the server during testing.
r.POST("/close", func(c *ginfw.Context) {
shutdownRequested = true
@@ -375,10 +450,12 @@ func main() {
║ SVM Payee: %-40s ║
║ ║
║ Endpoints: ║
-║ • GET /protected (EIP-3009 payment) ║
-║ • GET /protected-svm (SVM payment) ║
-║ • GET /protected-permit2 (Permit2 payment) ║
-║ • GET /protected-permit2-erc20 (Permit2 ERC-20) ║
+║ • GET /exact/evm/eip3009 (EVM EIP-3009) ║
+║ • GET /exact/evm/permit2 (Permit2) ║
+║ • GET /exact/evm/permit2-eip2612GasSponsoring ║
+║ • GET /exact/evm/permit2-erc20ApprovalGasSponsoring ║
+║ • GET /upto/evm/permit2 (Upto Permit2) ║
+║ • GET /exact/svm (SVM) ║
║ • GET /health (no payment required) ║
║ • POST /close (shutdown server) ║
╚════════════════════════════════════════════════════════╝
diff --git a/e2e/servers/gin/test.config.json b/e2e/servers/gin/test.config.json
index 055a3b04bc..5e434fbfc3 100644
--- a/e2e/servers/gin/test.config.json
+++ b/e2e/servers/gin/test.config.json
@@ -11,7 +11,7 @@
"description": "Go Gin server with x402 v2 payment middleware",
"endpoints": [
{
- "path": "/protected",
+ "path": "/exact/evm/eip3009",
"method": "GET",
"description": "Protected endpoint requiring EIP-3009 payment",
"requiresPayment": true,
@@ -19,24 +19,44 @@
"transferMethod": "eip3009"
},
{
- "path": "/protected-permit2",
+ "path": "/exact/evm/permit2",
"method": "GET",
- "description": "Protected endpoint requiring Permit2 payment",
+ "description": "Protected endpoint requiring Permit2 payment (standard settle, no gas sponsoring)",
"requiresPayment": true,
"protocolFamily": "evm",
- "transferMethod": "permit2"
+ "transferMethod": "permit2",
+ "permit2Direct": true
+ },
+ {
+ "path": "/exact/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "coldstart": true
},
{
- "path": "/protected-permit2-erc20",
+ "path": "/exact/evm/permit2-erc20ApprovalGasSponsoring",
"method": "GET",
"description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring",
"requiresPayment": true,
"protocolFamily": "evm",
"transferMethod": "permit2",
- "extension": "erc20ApprovalGasSponsoring"
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
+ },
+ {
+ "path": "/upto/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring upto Permit2 payment (usage-based settlement)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "permit2Direct": true
},
{
- "path": "/protected-svm",
+ "path": "/exact/svm",
"method": "GET",
"description": "Protected endpoint requiring payment (SVM)",
"requiresPayment": true,
@@ -64,4 +84,4 @@
],
"optional": []
}
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/hono/README.md b/e2e/servers/hono/README.md
index 708dd989df..2f0483f54b 100644
--- a/e2e/servers/hono/README.md
+++ b/e2e/servers/hono/README.md
@@ -1,13 +1,13 @@
-# E2E Test Server: Express (TypeScript)
+# E2E Test Server: Hono (TypeScript)
-This server demonstrates and tests the x402 Express.js middleware with both EVM and SVM payment protection.
+This server demonstrates and tests the x402 Hono middleware with EVM, SVM, and optional Stellar payment protection.
## What It Tests
### Core Functionality
- ✅ **V2 Protocol** - Modern x402 server middleware
- ✅ **Payment Protection** - Middleware protecting specific routes
-- ✅ **Multi-chain Support** - EVM and SVM payment acceptance
+- ✅ **Multi-chain Support** - EVM, SVM, and optional Stellar payment acceptance
- ✅ **Facilitator Integration** - HTTP communication with facilitator
- ✅ **Extension Support** - Bazaar discovery metadata
- ✅ **Settlement Handling** - Payment verification and confirmation
@@ -15,6 +15,7 @@ This server demonstrates and tests the x402 Express.js middleware with both EVM
### Protected Endpoints
- ✅ `GET /protected` - Requires EVM payment (USDC on Base Sepolia)
- ✅ `GET /protected-svm` - Requires SVM payment (USDC on Solana Devnet)
+- ✅ `GET /protected-stellar` - Requires Stellar payment (USDC on Stellar Testnet, optional)
## What It Demonstrates
@@ -25,6 +26,7 @@ import express from "express";
import { x402Middleware } from "@x402/server/express";
import { ExactEvmServer } from "@x402/evm";
import { ExactEvmServer } from "@x402/svm";
+import { ExactStellarServer } from "@x402/stellar";
const app = express();
@@ -47,6 +49,15 @@ const routes = {
extensions: {
bazaar: discoveryMetadata
}
+ },
+ "GET /protected-stellar": {
+ scheme: "exact",
+ network: "stellar:testnet",
+ payTo: "YourStellarAddress",
+ price: "$0.001",
+ extensions: {
+ bazaar: discoveryMetadata
+ }
}
};
@@ -56,7 +67,8 @@ app.use(x402Middleware({
facilitatorUrl: "http://localhost:4023",
servers: {
"eip155:84532": new ExactEvmServer(),
- "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": new ExactSvmServer()
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1": new ExactSvmServer(),
+ "stellar:testnet": new ExactStellarServer()
}
}));
@@ -68,6 +80,10 @@ app.get("/protected", (req, res) => {
app.get("/protected-svm", (req, res) => {
res.json({ message: "SVM payment successful!" });
});
+
+app.get("/protected-stellar", (req, res) => {
+ res.json({ message: "Stellar payment successful!" });
+});
```
### Key Concepts Shown
@@ -84,7 +100,7 @@ app.get("/protected-svm", (req, res) => {
This server is tested with:
- **Clients:** TypeScript Fetch, Go HTTP
- **Facilitators:** TypeScript, Go
-- **Payment Types:** EVM (Base Sepolia), SVM (Solana Devnet)
+- **Payment Types:** EVM (Base Sepolia), SVM (Solana Devnet), Stellar (Stellar Testnet)
- **Protocols:** V2 (primary), V1 (via client negotiation)
### Request Flow
@@ -100,25 +116,31 @@ This server is tested with:
```bash
# Via e2e test suite
cd e2e
-pnpm test --server=express
+pnpm test --server=hono
# Direct execution
-cd e2e/servers/express
+cd e2e/servers/hono
export FACILITATOR_URL="http://localhost:4023"
export EVM_PAYEE_ADDRESS="0x..."
export SVM_PAYEE_ADDRESS="..."
+export STELLAR_PAYEE_ADDRESS="G..." # optional
export PORT=4022
pnpm start
```
## Environment Variables
+### Required
- `PORT` - HTTP server port (default: 4022)
- `FACILITATOR_URL` - Facilitator endpoint URL
- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments
- `SVM_PAYEE_ADDRESS` - Solana address to receive payments
+
+### Optional
+- `STELLAR_PAYEE_ADDRESS` - Stellar address to receive payments - enables Stellar endpoint
- `EVM_NETWORK` - EVM network (default: eip155:84532)
- `SVM_NETWORK` - SVM network (default: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)
+- `STELLAR_NETWORK` - Stellar network (default: stellar:testnet)
## Response Examples
@@ -151,8 +173,9 @@ PAYMENT-RESPONSE:
- `@x402/server` - Express middleware
- `@x402/evm` - EVM service
- `@x402/svm` - SVM service
+- `@x402/stellar` - Stellar server (optional)
- `@x402/extensions/bazaar` - Discovery extension
-- `express` - HTTP server framework
+- `hono` - HTTP server framework
## Implementation Highlights
@@ -166,5 +189,6 @@ PAYMENT-RESPONSE:
### Service Integration
- **EVM Service** - Handles Base Sepolia USDC payments
- **SVM Service** - Handles Solana Devnet USDC payments
+- **Stellar Service** - Handles Stellar Testnet USDC contract payments
- **Price Conversion** - "$0.001" → token amounts with decimals
- **Asset Resolution** - Automatic USDC contract/mint lookup
diff --git a/e2e/servers/hono/index.ts b/e2e/servers/hono/index.ts
index 0eb94b4744..9536d6b30f 100644
--- a/e2e/servers/hono/index.ts
+++ b/e2e/servers/hono/index.ts
@@ -1,10 +1,12 @@
import { serve } from "@hono/node-server";
import { Hono } from "hono";
-import { paymentMiddleware } from "@x402/hono";
+import { paymentMiddleware, setSettlementOverrides } from "@x402/hono";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { UptoEvmScheme } from "@x402/evm/upto/server";
import { ExactSvmScheme } from "@x402/svm/exact/server";
import { ExactAptosScheme } from "@x402/aptos/exact/server";
+import { ExactStellarScheme } from "@x402/stellar/exact/server";
import { bazaarResourceServerExtension, declareDiscoveryExtension } from "@x402/extensions/bazaar";
import {
declareEip2612GasSponsoringExtension,
@@ -23,11 +25,15 @@ dotenv.config();
const PORT = process.env.PORT || "4023";
const EVM_NETWORK = (process.env.EVM_NETWORK || "eip155:84532") as `${string}:${string}`;
-const SVM_NETWORK = (process.env.SVM_NETWORK || "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") as `${string}:${string}`;
+const SVM_NETWORK = (process.env.SVM_NETWORK ||
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") as `${string}:${string}`;
const APTOS_NETWORK = (process.env.APTOS_NETWORK || "aptos:2") as `${string}:${string}`;
+const STELLAR_NETWORK = (process.env.STELLAR_NETWORK || "stellar:testnet") as `${string}:${string}`;
const EVM_PAYEE_ADDRESS = process.env.EVM_PAYEE_ADDRESS as `0x${string}`;
const SVM_PAYEE_ADDRESS = process.env.SVM_PAYEE_ADDRESS as string;
const APTOS_PAYEE_ADDRESS = process.env.APTOS_PAYEE_ADDRESS as string;
+const STELLAR_PAYEE_ADDRESS = process.env.STELLAR_PAYEE_ADDRESS as string | undefined;
+const EVM_PERMIT2_ASSET = process.env.EVM_PERMIT2_ASSET as `0x${string}`;
const facilitatorUrl = process.env.FACILITATOR_URL;
if (!EVM_PAYEE_ADDRESS) {
@@ -40,7 +46,6 @@ if (!SVM_PAYEE_ADDRESS) {
process.exit(1);
}
-
if (!facilitatorUrl) {
console.error("❌ FACILITATOR_URL environment variable is required");
process.exit(1);
@@ -49,18 +54,26 @@ if (!facilitatorUrl) {
// Initialize Hono app
const app = new Hono();
-// Create HTTP facilitator client
-const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+// Create facilitator clients (mock facilitator as fallback for startup validation)
+const facilitatorClients = [new HTTPFacilitatorClient({ url: facilitatorUrl })];
+const mockFacilitatorUrl = process.env.MOCK_FACILITATOR_URL;
+if (mockFacilitatorUrl) {
+ facilitatorClients.push(new HTTPFacilitatorClient({ url: mockFacilitatorUrl }));
+}
// Create x402 resource server with builder pattern (cleaner!)
-const x402Server = new x402ResourceServer(facilitatorClient);
+const x402Server = new x402ResourceServer(facilitatorClients);
// Register server schemes
x402Server.register("eip155:*", new ExactEvmScheme());
+x402Server.register("eip155:*", new UptoEvmScheme());
x402Server.register("solana:*", new ExactSvmScheme());
if (APTOS_PAYEE_ADDRESS) {
x402Server.register("aptos:*", new ExactAptosScheme());
}
+if (STELLAR_PAYEE_ADDRESS) {
+ x402Server.register("stellar:*", new ExactStellarScheme());
+}
// Register Bazaar discovery extension
x402Server.registerExtension(bazaarResourceServerExtension);
@@ -74,11 +87,28 @@ console.log(`Using remote facilitator at: ${facilitatorUrl}`);
* Pre-middleware guard for optional Aptos endpoint
* Returns 501 Not Implemented if Aptos is not configured
*/
-app.use("/protected-aptos", async (c, next) => {
+app.use("/exact/aptos", async (c, next) => {
if (!APTOS_PAYEE_ADDRESS) {
+ return c.json(
+ {
+ error: "Aptos payments not configured",
+ message: "APTOS_PAYEE_ADDRESS environment variable is not set",
+ },
+ 501,
+ );
+ }
+ await next();
+});
+
+/**
+ * Pre-middleware guard for optional Stellar endpoint
+ * Returns 501 Not Implemented if Stellar is not configured
+ */
+app.use("/exact/stellar", async (c, next) => {
+ if (!STELLAR_PAYEE_ADDRESS) {
return c.json({
- error: "Aptos payments not configured",
- message: "APTOS_PAYEE_ADDRESS environment variable is not set",
+ error: "Stellar payments not configured",
+ message: "STELLAR_PAYEE_ADDRESS environment variable is not set",
}, 501);
}
await next();
@@ -95,7 +125,7 @@ app.use(
paymentMiddleware(
{
// Route-specific payment configuration
- "GET /protected": {
+ "GET /exact/evm/eip3009": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
@@ -120,7 +150,7 @@ app.use(
}),
},
},
- "GET /protected-svm": {
+ "GET /exact/svm": {
accepts: {
payTo: SVM_PAYEE_ADDRESS,
scheme: "exact",
@@ -147,44 +177,44 @@ app.use(
},
...(APTOS_PAYEE_ADDRESS
? {
- "GET /protected-aptos": {
- accepts: {
- payTo: APTOS_PAYEE_ADDRESS,
- scheme: "exact",
- price: "$0.001",
- network: APTOS_NETWORK,
- },
- extensions: {
- ...declareDiscoveryExtension({
- output: {
- example: {
- message: "Protected endpoint accessed successfully",
- timestamp: "2024-01-01T00:00:00Z",
- },
- schema: {
- properties: {
- message: { type: "string" },
- timestamp: { type: "string" },
- },
- required: ["message", "timestamp"],
+ "GET /exact/aptos": {
+ accepts: {
+ payTo: APTOS_PAYEE_ADDRESS,
+ scheme: "exact",
+ price: "$0.001",
+ network: APTOS_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
},
+ required: ["message", "timestamp"],
},
- }),
- },
+ },
+ }),
},
- }
+ },
+ }
: {}),
- "GET /protected-permit2": {
+ "GET /exact/evm/permit2": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
network: EVM_NETWORK,
price: {
amount: "1000",
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ asset: EVM_PERMIT2_ASSET,
extra: {
assetTransferMethod: "permit2",
- name: "USDC",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
version: "2",
},
},
@@ -207,17 +237,46 @@ app.use(
},
},
}),
+ },
+ },
+ "GET /exact/evm/permit2-eip2612GasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "exact",
+ network: EVM_NETWORK,
+ price: "$0.001",
+ // Use pre-parsed price with assetTransferMethod to force Permit2
+ extra: { assetTransferMethod: "permit2" },
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ method: "permit2-eip2612",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ method: { type: "string" },
+ },
+ required: ["message", "timestamp", "method"],
+ },
+ },
+ }),
...declareEip2612GasSponsoringExtension(),
},
},
- "GET /protected-permit2-erc20": {
+ "GET /exact/evm/permit2-erc20ApprovalGasSponsoring": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
network: EVM_NETWORK,
price: {
amount: "1000",
- asset: "0xeED520980fC7C7B4eB379B96d61CEdea2423005a",
+ asset: EVM_PERMIT2_ASSET,
extra: {
assetTransferMethod: "permit2",
},
@@ -227,6 +286,93 @@ app.use(
...declareErc20ApprovalGasSponsoringExtension(),
},
},
+ // Upto Permit2 direct endpoint - client must have Permit2 pre-approved
+ // Authorizes up to 2000 atomic units, settles 1000 (partial settlement)
+ "GET /upto/evm/permit2": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ },
+ // Upto Permit2 endpoint with EIP-2612 gas sponsoring
+ // Authorizes up to 2000 atomic units, settles 1000 (partial settlement)
+ "GET /upto/evm/permit2-eip2612GasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ extensions: {
+ ...declareEip2612GasSponsoringExtension(),
+ },
+ },
+ // Upto Permit2 endpoint for ERC-20 approval gas sponsoring (no EIP-2612)
+ // Authorizes up to 2000 atomic units, settles 1000 (partial settlement)
+ "GET /upto/evm/permit2-erc20ApprovalGasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ },
+ },
+ },
+ extensions: {
+ ...declareErc20ApprovalGasSponsoringExtension(),
+ },
+ },
+ ...(STELLAR_PAYEE_ADDRESS
+ ? {
+ "GET /exact/stellar": {
+ accepts: {
+ payTo: STELLAR_PAYEE_ADDRESS!,
+ scheme: "exact",
+ price: "$0.001",
+ network: STELLAR_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected Stellar endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
+ },
+ },
+ }),
+ },
+ },
+ }
+ : {}),
},
x402Server, // Pass pre-configured server instance
),
@@ -238,7 +384,7 @@ app.use(
* This endpoint demonstrates a resource protected by x402 payment middleware.
* Clients must provide a valid payment signature to access this endpoint.
*/
-app.get("/protected", (c) => {
+app.get("/exact/evm/eip3009", c => {
return c.json({
message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -251,7 +397,7 @@ app.get("/protected", (c) => {
* This endpoint demonstrates a resource protected by x402 payment middleware for SVM.
* Clients must provide a valid payment signature to access this endpoint.
*/
-app.get("/protected-svm", (c) => {
+app.get("/exact/svm", c => {
return c.json({
message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -265,7 +411,7 @@ app.get("/protected-svm", (c) => {
* Clients must provide a valid payment signature to access this endpoint.
* Note: 501 check is handled by pre-middleware guard above.
*/
-app.get("/protected-aptos", (c) => {
+app.get("/exact/aptos", c => {
return c.json({
message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -273,9 +419,9 @@ app.get("/protected-aptos", (c) => {
});
/**
- * Protected Permit2 endpoint - requires Permit2 payment with EIP-2612 gas sponsoring
+ * Protected Permit2 endpoint - standard settle (no gas sponsoring)
*/
-app.get("/protected-permit2", (c) => {
+app.get("/exact/evm/permit2", c => {
return c.json({
message: "Permit2 endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -283,10 +429,21 @@ app.get("/protected-permit2", (c) => {
});
});
+/**
+ * Protected Permit2 EIP-2612 endpoint - requires Permit2 with gas sponsoring
+ */
+app.get("/exact/evm/permit2-eip2612GasSponsoring", c => {
+ return c.json({
+ message: "Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "permit2-eip2612",
+ });
+});
+
/**
* Protected Permit2 ERC-20 endpoint - requires Permit2 payment with ERC-20 approval gas sponsoring
*/
-app.get("/protected-permit2-erc20", (c) => {
+app.get("/exact/evm/permit2-erc20ApprovalGasSponsoring", c => {
return c.json({
message: "Permit2 ERC-20 approval endpoint accessed successfully",
timestamp: new Date().toISOString(),
@@ -294,12 +451,67 @@ app.get("/protected-permit2-erc20", (c) => {
});
});
+/**
+ * Upto Permit2 direct endpoint - upto scheme, client must have Permit2 pre-approved
+ * Authorizes 2000, settles 1000 (partial settlement)
+ */
+app.get("/upto/evm/permit2", c => {
+ setSettlementOverrides(c, { amount: "1000" });
+ return c.json({
+ message: "Upto Permit2 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2",
+ });
+});
+
+/**
+ * Upto Permit2 EIP-2612 endpoint - upto scheme with gas sponsoring
+ * Authorizes 2000, settles 1000 (partial settlement)
+ */
+app.get("/upto/evm/permit2-eip2612GasSponsoring", c => {
+ setSettlementOverrides(c, { amount: "1000" });
+ return c.json({
+ message: "Upto Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2-eip2612",
+ });
+});
+
+/**
+ * Upto Permit2 ERC-20 endpoint - upto scheme with ERC-20 approval gas sponsoring
+ * Authorizes 2000, settles 1000 (partial settlement)
+ */
+app.get("/upto/evm/permit2-erc20ApprovalGasSponsoring", c => {
+ setSettlementOverrides(c, { amount: "1000" });
+ return c.json({
+ message: "Upto Permit2 ERC-20 approval endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2-erc20-approval",
+ });
+});
+
+/**
+ * Protected Stellar endpoint - requires payment to access
+ *
+ * This endpoint demonstrates a resource protected by x402 payment middleware for Stellar.
+ * Clients must provide a valid payment signature to access this endpoint.
+ * Note: 501 check is handled by pre-middleware guard above.
+ */
+if (STELLAR_PAYEE_ADDRESS) {
+ app.get("/exact/stellar", c => {
+ return c.json({
+ message: "Protected Stellar endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ });
+ });
+}
+
/**
* Health check endpoint - no payment required
*
* Used to verify the server is running and responsive.
*/
-app.get("/health", (c) => {
+app.get("/health", c => {
return c.json({
status: "ok",
network: EVM_NETWORK,
@@ -313,7 +525,7 @@ app.get("/health", (c) => {
*
* Allows graceful shutdown of the server during testing.
*/
-app.post("/close", (c) => {
+app.post("/close", c => {
console.log("Received shutdown request");
// Give time for response to be sent
@@ -338,16 +550,20 @@ console.log(`
║ EVM Network: ${EVM_NETWORK} ║
║ SVM Network: ${SVM_NETWORK} ║
║ Aptos Network: ${APTOS_NETWORK} ║
+║ Stellar Network: ${STELLAR_NETWORK} ║
║ EVM Payee: ${EVM_PAYEE_ADDRESS} ║
║ SVM Payee: ${SVM_PAYEE_ADDRESS} ║
║ Aptos Payee: ${APTOS_PAYEE_ADDRESS || "(not configured)"}
+║ Stellar Payee: ${STELLAR_PAYEE_ADDRESS || "(not configured)"}
║ ║
║ Endpoints: ║
-║ • GET /protected (EIP-3009 payment) ║
-║ • GET /protected-permit2 (Permit2 + EIP-2612) ║
-║ • GET /protected-permit2-erc20 (Permit2 + ERC-20 approval)║
-║ • GET /protected-svm (SVM payment) ║
-║ • GET /protected-aptos (Aptos payment) ║
+║ • GET /exact/evm/eip3009 (EVM EIP-3009) ║
+║ • GET /exact/evm/permit2 (Permit2) ║
+║ • GET /exact/evm/permit2-eip2612GasSponsoring ║
+║ • GET /exact/evm/permit2-erc20ApprovalGasSponsoring ║
+║ • GET /exact/svm (SVM) ║
+║ • GET /exact/aptos (Aptos) ║
+║ • GET /exact/stellar (Stellar) ║
║ • GET /health (no payment required) ║
║ • POST /close (shutdown server) ║
╚════════════════════════════════════════════════════════╝
diff --git a/e2e/servers/hono/package.json b/e2e/servers/hono/package.json
index c4924a9b02..ba14bf7e2d 100644
--- a/e2e/servers/hono/package.json
+++ b/e2e/servers/hono/package.json
@@ -16,6 +16,7 @@
"@x402/evm": "workspace:*",
"@x402/extensions": "workspace:*",
"@x402/hono": "workspace:*",
+ "@x402/stellar": "workspace:*",
"@x402/svm": "workspace:*",
"dotenv": "^16.6.1",
"hono": "^4.7.1"
@@ -34,4 +35,4 @@
"tsx": "^4.7.0",
"typescript": "^5.3.0"
}
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/hono/test.config.json b/e2e/servers/hono/test.config.json
index 2030b88cf0..c014288c77 100644
--- a/e2e/servers/hono/test.config.json
+++ b/e2e/servers/hono/test.config.json
@@ -3,14 +3,11 @@
"type": "server",
"language": "typescript",
"x402Version": 2,
- "extensions": [
- "bazaar",
- "eip2612GasSponsoring",
- "erc20ApprovalGasSponsoring"
- ],
+ "extensions": ["bazaar", "eip2612GasSponsoring", "erc20ApprovalGasSponsoring"],
+
"endpoints": [
{
- "path": "/protected",
+ "path": "/exact/evm/eip3009",
"method": "GET",
"description": "Protected endpoint requiring EIP-3009 payment",
"requiresPayment": true,
@@ -18,36 +15,82 @@
"transferMethod": "eip3009"
},
{
- "path": "/protected-permit2",
+ "path": "/exact/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment (standard settle, no gas sponsoring)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "permit2Direct": true
+ },
+ {
+ "path": "/exact/evm/permit2-eip2612GasSponsoring",
"method": "GET",
- "description": "Protected endpoint requiring Permit2 payment",
+ "description": "Protected endpoint requiring Permit2 payment with EIP-2612 gas sponsoring",
"requiresPayment": true,
"protocolFamily": "evm",
- "permit2": true
+ "transferMethod": "permit2",
+ "coldstart": true
},
{
- "path": "/protected-permit2-erc20",
+ "path": "/exact/evm/permit2-erc20ApprovalGasSponsoring",
"method": "GET",
"description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring",
"requiresPayment": true,
"protocolFamily": "evm",
"transferMethod": "permit2",
- "extensions": ["erc20ApprovalGasSponsoring"]
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
},
{
- "path": "/protected-svm",
+ "path": "/upto/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring upto Permit2 payment (direct, client must pre-approve)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "permit2Direct": true
+ },
+ {
+ "path": "/upto/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Upto Permit2 payment with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "coldstart": true
+ },
+ {
+ "path": "/upto/evm/permit2-erc20ApprovalGasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Upto Permit2 payment with ERC-20 approval gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
+ },
+ {
+ "path": "/exact/svm",
"method": "GET",
"description": "Protected endpoint requiring payment on SVM network",
"requiresPayment": true,
"protocolFamily": "svm"
},
{
- "path": "/protected-aptos",
+ "path": "/exact/aptos",
"method": "GET",
"description": "Protected endpoint requiring payment on Aptos network",
"requiresPayment": true,
"protocolFamily": "aptos"
},
+ {
+ "path": "/exact/stellar",
+ "method": "GET",
+ "description": "Protected endpoint requiring payment on Stellar network",
+ "requiresPayment": true,
+ "protocolFamily": "stellar"
+ },
{
"path": "/health",
"method": "GET",
@@ -62,14 +105,7 @@
}
],
"environment": {
- "required": [
- "PORT",
- "EVM_PAYEE_ADDRESS",
- "SVM_PAYEE_ADDRESS",
- "FACILITATOR_URL"
- ],
- "optional": [
- "APTOS_PAYEE_ADDRESS"
- ]
+ "required": ["PORT", "EVM_PAYEE_ADDRESS", "SVM_PAYEE_ADDRESS", "FACILITATOR_URL"],
+ "optional": ["APTOS_PAYEE_ADDRESS", "STELLAR_PAYEE_ADDRESS"]
}
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/mcp-go/go.mod b/e2e/servers/mcp-go/go.mod
index ef853a52a3..9fc72ccfb4 100644
--- a/e2e/servers/mcp-go/go.mod
+++ b/e2e/servers/mcp-go/go.mod
@@ -23,10 +23,10 @@ require (
github.com/holiman/uint256 v1.3.2 // indirect
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
- golang.org/x/crypto v0.41.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
- golang.org/x/sync v0.16.0 // indirect
- golang.org/x/sys v0.36.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
)
replace github.com/coinbase/x402/go => ../../../go
diff --git a/e2e/servers/mcp-go/go.sum b/e2e/servers/mcp-go/go.sum
index f897f19fa1..44cd6d3944 100644
--- a/e2e/servers/mcp-go/go.sum
+++ b/e2e/servers/mcp-go/go.sum
@@ -40,6 +40,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
@@ -72,18 +74,24 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
-golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/e2e/servers/mcp-python/uv.lock b/e2e/servers/mcp-python/uv.lock
index 31abc9061e..77e4c907b6 100644
--- a/e2e/servers/mcp-python/uv.lock
+++ b/e2e/servers/mcp-python/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -2137,7 +2137,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.1.0"
+version = "2.5.0"
source = { editable = "../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
@@ -2160,7 +2160,7 @@ mcp = [
[package.metadata]
requires-dist = [
{ name = "eth-abi", marker = "extra == 'evm'", specifier = ">=5.0.0" },
- { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.12.0" },
+ { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.13.0" },
{ name = "eth-keys", marker = "extra == 'evm'", specifier = ">=0.5.0" },
{ name = "eth-utils", marker = "extra == 'evm'", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], marker = "extra == 'fastapi'", specifier = ">=0.115.0" },
@@ -2187,7 +2187,7 @@ provides-extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp",
dev = [
{ name = "black", specifier = ">=23.0.0" },
{ name = "eth-abi", specifier = ">=5.0.0" },
- { name = "eth-account", specifier = ">=0.12.0" },
+ { name = "eth-account", specifier = ">=0.13.0" },
{ name = "eth-keys", specifier = ">=0.5.0" },
{ name = "eth-utils", specifier = ">=4.0.0" },
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
diff --git a/e2e/servers/nethttp/README.md b/e2e/servers/nethttp/README.md
new file mode 100644
index 0000000000..a5b9e3667f
--- /dev/null
+++ b/e2e/servers/nethttp/README.md
@@ -0,0 +1,204 @@
+# E2E Test Server: net/http (Go)
+
+This server demonstrates and tests the x402 net/http middleware with both EVM and SVM payment protection.
+
+## What It Tests
+
+### Core Functionality
+- ✅ **V2 Protocol** - Modern x402 server middleware
+- ✅ **Payment Protection** - Middleware protecting specific routes
+- ✅ **Multi-chain Support** - EVM and SVM payment acceptance
+- ✅ **Facilitator Integration** - HTTP communication with facilitator
+- ✅ **Extension Support** - Bazaar discovery metadata
+- ✅ **Settlement Handling** - Payment verification and confirmation
+
+### Protected Endpoints
+- ✅ `GET /protected` - Requires EVM payment (USDC on Base Sepolia)
+- ✅ `GET /protected-svm` - Requires SVM payment (USDC on Solana Devnet)
+
+## What It Demonstrates
+
+### Server Setup
+
+```go
+import (
+ "net/http"
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ nethttpmw "github.com/coinbase/x402/go/http/nethttp"
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+ "github.com/coinbase/x402/go/extensions/bazaar"
+)
+
+// Create ServeMux
+mux := http.NewServeMux()
+
+// Define payment routes
+routes := x402http.RoutesConfig{
+ "GET /protected": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ Network: "eip155:84532",
+ PayTo: evmPayeeAddress,
+ Price: "$0.001",
+ },
+ },
+ Extensions: map[string]interface{}{
+ "bazaar": discoveryExtension,
+ },
+ },
+ "GET /protected-svm": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ PayTo: svmPayeeAddress,
+ Price: "$0.001",
+ },
+ },
+ Extensions: map[string]interface{}{
+ "bazaar": discoveryExtension,
+ },
+ },
+}
+
+// Define protected endpoints
+mux.HandleFunc("GET /protected", func(w http.ResponseWriter, r *http.Request) {
+ json.NewEncoder(w).Encode(map[string]string{"message": "EVM payment successful!"})
+})
+
+mux.HandleFunc("GET /protected-svm", func(w http.ResponseWriter, r *http.Request) {
+ json.NewEncoder(w).Encode(map[string]string{"message": "SVM payment successful!"})
+})
+
+// Apply payment middleware
+handler := nethttpmw.X402Payment(nethttpmw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []nethttpmw.SchemeConfig{
+ {Network: "eip155:84532", Server: evm.NewExactEvmScheme()},
+ {Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", Server: svm.NewExactSvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+})(mux)
+
+http.ListenAndServe(":4021", handler)
+```
+
+### Key Concepts Shown
+
+1. **Route Configuration** - Map of route → payment requirements
+2. **Multi-Chain Services** - Different services for EVM vs SVM
+3. **Facilitator Client** - HTTP client for verification/settlement
+4. **Middleware Options** - Functional options pattern
+5. **Extension Integration** - Bazaar discovery declarations
+6. **Automatic Initialization** - Service initialization on startup
+
+## Test Scenarios
+
+This server is tested with:
+- **Clients:** TypeScript Fetch, Go HTTP
+- **Facilitators:** TypeScript, Go
+- **Payment Types:** EVM (Base Sepolia), SVM (Solana Devnet)
+- **Protocols:** V2 (primary), V1 (via client negotiation)
+
+### Request Flow
+1. Client makes initial request (no payment)
+2. Middleware returns 402 with `PAYMENT-REQUIRED` header
+3. Client creates payment payload
+4. Client retries with `PAYMENT-SIGNATURE` header
+5. Middleware forwards to facilitator for verification
+6. Middleware returns protected content + `PAYMENT-RESPONSE` header
+
+## Running
+
+```bash
+# Via e2e test suite
+cd e2e
+pnpm test --server=nethttp
+
+# Direct execution
+cd e2e/servers/nethttp
+export FACILITATOR_URL="http://localhost:4024"
+export EVM_PAYEE_ADDRESS="0x..."
+export SVM_PAYEE_ADDRESS="..."
+export PORT=4023
+./nethttp
+```
+
+## Environment Variables
+
+- `PORT` - HTTP server port (default: 4021)
+- `FACILITATOR_URL` - Facilitator endpoint URL
+- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments
+- `SVM_PAYEE_ADDRESS` - Solana address to receive payments
+- `EVM_NETWORK` - EVM network (default: eip155:84532)
+- `SVM_NETWORK` - SVM network (default: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)
+
+## Response Examples
+
+### 402 Payment Required
+
+```
+HTTP/1.1 402 Payment Required
+PAYMENT-REQUIRED:
+Content-Type: application/json
+
+{
+ "error": "Payment required",
+ "x402Version": 2,
+ "accepts": [...],
+ "resource": {...},
+ "extensions": {
+ "bazaar": {
+ "method": "GET",
+ "outputExample": {...}
+ }
+ }
+}
+```
+
+### 200 Success (After Payment)
+
+```
+HTTP/1.1 200 OK
+PAYMENT-RESPONSE:
+Content-Type: application/json
+
+{
+ "message": "Protected endpoint accessed successfully",
+ "timestamp": "2024-01-01T00:00:00Z"
+}
+```
+
+## Dependencies
+
+- `github.com/coinbase/x402/go` - Core x402
+- `github.com/coinbase/x402/go/http` - HTTP integration
+- `github.com/coinbase/x402/go/http/nethttp` - net/http middleware
+- `github.com/coinbase/x402/go/mechanisms/evm` - EVM server
+- `github.com/coinbase/x402/go/mechanisms/svm` - SVM server
+- `github.com/coinbase/x402/go/extensions/bazaar` - Discovery extension
+
+## Implementation Highlights
+
+### Middleware Features
+- **Route Matching** - Pattern-based route configuration
+- **Payment Requirement Building** - Automatic 402 response generation
+- **Facilitator Communication** - HTTP client for verification
+- **Settlement Callbacks** - Optional handlers for payment events
+- **Extension Support** - Bazaar metadata in responses
+- **Timeout Handling** - Configurable facilitator timeouts
+
+### Service Integration
+- **EVM Server** - Base Sepolia USDC
+- **SVM Server** - Solana Devnet USDC
+- **Initialization** - Fetches supported kinds from facilitator
+- **Price Parsing** - Dollar strings → token amounts
+
+### Bazaar Extension
+- **Method Declaration** - GET with output schema
+- **Example Output** - Response structure preview
+- **Schema Definition** - JSON Schema for validation
diff --git a/e2e/servers/nethttp/build.sh b/e2e/servers/nethttp/build.sh
new file mode 100755
index 0000000000..6ce0831b72
--- /dev/null
+++ b/e2e/servers/nethttp/build.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+
+echo "Building net/http server..."
+go build -o nethttp .
+echo "✅ Build completed: nethttp"
diff --git a/e2e/servers/nethttp/go.mod b/e2e/servers/nethttp/go.mod
new file mode 100644
index 0000000000..acc801d29a
--- /dev/null
+++ b/e2e/servers/nethttp/go.mod
@@ -0,0 +1,67 @@
+module github.com/coinbase/x402/e2e/servers/nethttp
+
+go 1.24.0
+
+toolchain go1.24.1
+
+require (
+ github.com/coinbase/x402/go v0.0.0
+ github.com/joho/godotenv v1.5.1
+)
+
+require (
+ filippo.io/edwards25519 v1.0.0-rc.1 // indirect
+ github.com/Microsoft/go-winio v0.6.2 // indirect
+ github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
+ github.com/StackExchange/wmi v1.2.1 // indirect
+ github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
+ github.com/bits-and-blooms/bitset v1.20.0 // indirect
+ github.com/blendle/zapdriver v1.3.1 // indirect
+ github.com/consensys/gnark-crypto v0.18.0 // indirect
+ github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
+ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/deckarep/golang-set/v2 v2.6.0 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
+ github.com/ethereum/go-ethereum v1.16.7 // indirect
+ github.com/ethereum/go-verkle v0.2.2 // indirect
+ github.com/fatih/color v1.16.0 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
+ github.com/gagliardetto/solana-go v1.14.0 // indirect
+ github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/go-ole/go-ole v1.3.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/gorilla/websocket v1.4.2 // indirect
+ github.com/holiman/uint256 v1.3.2 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.16.0 // indirect
+ github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mitchellh/go-testing-interface v1.14.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect
+ github.com/mr-tron/base58 v1.2.0 // indirect
+ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
+ github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
+ github.com/tklauser/numcpus v0.6.1 // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+ github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+ go.mongodb.org/mongo-driver v1.12.2 // indirect
+ go.uber.org/atomic v1.7.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ go.uber.org/ratelimit v0.2.0 // indirect
+ go.uber.org/zap v1.21.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/term v0.38.0 // indirect
+ golang.org/x/time v0.14.0 // indirect
+)
+
+replace github.com/coinbase/x402/go => ../../../go
diff --git a/e2e/servers/nethttp/go.sum b/e2e/servers/nethttp/go.sum
new file mode 100644
index 0000000000..bac78a1035
--- /dev/null
+++ b/e2e/servers/nethttp/go.sum
@@ -0,0 +1,334 @@
+filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
+filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
+github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=
+github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=
+github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
+github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
+github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
+github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
+github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
+github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
+github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
+github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
+github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
+github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
+github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
+github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
+github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
+github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
+github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
+github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
+github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
+github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
+github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
+github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
+github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
+github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
+github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
+github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
+github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
+github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ=
+github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
+github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
+github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
+github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
+github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
+github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
+github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
+github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
+github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/solana-go v1.14.0 h1:3WfAi70jOOjAJ0deFMjdhFYlLXATF4tOQXsDNWJtOLw=
+github.com/gagliardetto/solana-go v1.14.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
+github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
+github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
+github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
+github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
+github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
+github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
+github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330=
+github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg=
+github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
+github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
+github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
+github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
+github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
+github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
+github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
+github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
+github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
+github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
+github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
+github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
+github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
+github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
+github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
+github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk=
+github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE=
+github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
+github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
+github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
+github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
+github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
+github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
+github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
+github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
+github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
+github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM=
+github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
+github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
+github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
+github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
+github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
+github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
+github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
+github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
+github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
+github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
+github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
+github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
+github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.12.2 h1:gbWY1bJkkmUB9jjZzcdhOL8O85N9H+Vvsf2yFN0RDws=
+go.mongodb.org/mongo-driver v1.12.2/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
+go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
+go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
+golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
+golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
+google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/e2e/servers/nethttp/install.sh b/e2e/servers/nethttp/install.sh
new file mode 100755
index 0000000000..5043a56808
--- /dev/null
+++ b/e2e/servers/nethttp/install.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -e
+
+echo "Installing Go dependencies for net/http server..."
+go mod tidy
+echo "✅ Dependencies installed"
diff --git a/e2e/servers/nethttp/main.go b/e2e/servers/nethttp/main.go
new file mode 100644
index 0000000000..f3d13206e4
--- /dev/null
+++ b/e2e/servers/nethttp/main.go
@@ -0,0 +1,460 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ "github.com/coinbase/x402/go/extensions/bazaar"
+ "github.com/coinbase/x402/go/extensions/eip2612gassponsor"
+ "github.com/coinbase/x402/go/extensions/erc20approvalgassponsor"
+ "github.com/coinbase/x402/go/extensions/types"
+ x402http "github.com/coinbase/x402/go/http"
+ nethttpmw "github.com/coinbase/x402/go/http/nethttp"
+ exactevm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ uptoevm "github.com/coinbase/x402/go/mechanisms/evm/upto/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+ "github.com/joho/godotenv"
+)
+
+var shutdownRequested bool
+
+// net/http E2E Test Server with x402 v2 Payment Middleware
+//
+// This server demonstrates how to integrate x402 v2 payment middleware
+// with a standard net/http application for end-to-end testing.
+
+func main() {
+ // Load .env file if it exists
+ if err := godotenv.Load(); err != nil {
+ fmt.Println("Warning: .env file not found. Using environment variables.")
+ }
+
+ // Get configuration from environment
+ port := os.Getenv("PORT")
+ if port == "" {
+ port = "4021"
+ }
+
+ evmPayeeAddress := os.Getenv("EVM_PAYEE_ADDRESS")
+ if evmPayeeAddress == "" {
+ fmt.Println("❌ EVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ svmPayeeAddress := os.Getenv("SVM_PAYEE_ADDRESS")
+ if svmPayeeAddress == "" {
+ fmt.Println("❌ SVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ facilitatorURL := os.Getenv("FACILITATOR_URL")
+ if facilitatorURL == "" {
+ fmt.Println("❌ FACILITATOR_URL environment variable is required")
+ os.Exit(1)
+ }
+
+ // Network configurations (from env or defaults)
+ evmNetworkStr := os.Getenv("EVM_NETWORK")
+ if evmNetworkStr == "" {
+ evmNetworkStr = "eip155:84532" // Default: Base Sepolia
+ }
+ svmNetworkStr := os.Getenv("SVM_NETWORK")
+ if svmNetworkStr == "" {
+ svmNetworkStr = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1" // Default: Solana Devnet
+ }
+ evmNetwork := x402.Network(evmNetworkStr)
+ svmNetwork := x402.Network(svmNetworkStr)
+
+ evmPermit2Asset := os.Getenv("EVM_PERMIT2_ASSET")
+ if evmPermit2Asset == "" {
+ evmPermit2Asset = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
+ }
+
+ fmt.Printf("EVM Payee address: %s\n", evmPayeeAddress)
+ fmt.Printf("SVM Payee address: %s\n", svmPayeeAddress)
+ fmt.Printf("Using remote facilitator at: %s\n", facilitatorURL)
+
+ // Create HTTP facilitator client
+ facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+ })
+
+ // Configure x402 payment middleware
+ //
+ // This middleware protects /exact/* payment routes with USDC payment requirements
+ // on the Base Sepolia testnet with bazaar discovery extension.
+
+ // Declare bazaar discovery extension for GET endpoints
+ discoveryExtension, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET,
+ nil, // No query params
+ nil, // No input schema
+ "", // No body type (GET method)
+ &types.OutputConfig{
+ Example: map[string]interface{}{
+ "message": "Protected endpoint accessed successfully",
+ "timestamp": "2024-01-01T00:00:00Z",
+ },
+ Schema: types.JSONSchema{
+ "properties": map[string]interface{}{
+ "message": map[string]interface{}{"type": "string"},
+ "timestamp": map[string]interface{}{"type": "string"},
+ },
+ "required": []string{"message", "timestamp"},
+ },
+ },
+ )
+ if err != nil {
+ fmt.Printf("Warning: Failed to create bazaar extension: %v\n", err)
+ }
+
+ routes := x402http.RoutesConfig{
+ "GET /exact/evm/eip3009": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Price: "$0.001",
+ Network: evmNetwork,
+ },
+ },
+ Extensions: map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ },
+ },
+ "GET /exact/svm": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: svmPayeeAddress,
+ Price: "$0.001",
+ Network: svmNetwork,
+ },
+ },
+ Extensions: map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ },
+ },
+ "GET /exact/evm/permit2": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "1000",
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ "extra": map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ },
+ },
+ },
+ },
+ Extensions: map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ },
+ },
+ "GET /exact/evm/permit2-eip2612GasSponsoring": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "1000",
+ "asset": evmPermit2Asset,
+ "extra": func() map[string]interface{} {
+ name := "USD Coin"
+ if evmNetworkStr == "eip155:84532" {
+ name = "USDC"
+ }
+ return map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ "name": name,
+ "version": "2",
+ }
+ }(),
+ },
+ },
+ },
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
+ for k, v := range eip2612gassponsor.DeclareEip2612GasSponsoringExtension() {
+ ext[k] = v
+ }
+ return ext
+ }(),
+ },
+ "GET /upto/evm/permit2": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "upto",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "2000",
+ "asset": evmPermit2Asset,
+ "extra": map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ "name": "USDC",
+ "version": "2",
+ },
+ },
+ },
+ },
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
+ for k, v := range eip2612gassponsor.DeclareEip2612GasSponsoringExtension() {
+ ext[k] = v
+ }
+ return ext
+ }(),
+ },
+ "GET /exact/evm/permit2-erc20ApprovalGasSponsoring": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Network: evmNetwork,
+ Price: map[string]interface{}{
+ "amount": "1000",
+ "asset": evmPermit2Asset,
+ "extra": map[string]interface{}{
+ "assetTransferMethod": "permit2",
+ },
+ },
+ },
+ },
+ Extensions: func() map[string]interface{} {
+ ext := map[string]interface{}{
+ types.BAZAAR.Key(): discoveryExtension,
+ }
+ for k, v := range erc20approvalgassponsor.DeclareExtension() {
+ ext[k] = v
+ }
+ return ext
+ }(),
+ },
+ }
+
+ // Create ServeMux and register handlers
+ mux := http.NewServeMux()
+
+ // Protected endpoint - requires payment to access
+ mux.HandleFunc("GET /exact/evm/eip3009", func(w http.ResponseWriter, r *http.Request) {
+ if shutdownRequested {
+ writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ return
+ }
+
+ writeJSON(w, http.StatusOK, map[string]interface{}{
+ "message": "Protected endpoint accessed successfully (EVM)",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "network": "eip155:84532",
+ })
+ })
+
+ // Protected SVM endpoint - requires payment to access
+ mux.HandleFunc("GET /exact/svm", func(w http.ResponseWriter, r *http.Request) {
+ if shutdownRequested {
+ writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ return
+ }
+
+ writeJSON(w, http.StatusOK, map[string]interface{}{
+ "message": "Protected endpoint accessed successfully (SVM)",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ })
+ })
+
+ // Protected Permit2 direct endpoint - standard settle (no gas sponsoring)
+ mux.HandleFunc("GET /exact/evm/permit2", func(w http.ResponseWriter, r *http.Request) {
+ if shutdownRequested {
+ writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ return
+ }
+
+ writeJSON(w, http.StatusOK, map[string]interface{}{
+ "message": "Permit2 endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "permit2",
+ })
+ })
+
+ // Protected Permit2 EIP-2612 endpoint - Permit2 with gas sponsoring
+ mux.HandleFunc("GET /exact/evm/permit2-eip2612GasSponsoring", func(w http.ResponseWriter, r *http.Request) {
+ if shutdownRequested {
+ writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ return
+ }
+
+ writeJSON(w, http.StatusOK, map[string]interface{}{
+ "message": "Permit2 EIP-2612 endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "permit2-eip2612",
+ })
+ })
+
+ // Protected Permit2 ERC-20 approval endpoint
+ mux.HandleFunc("GET /exact/evm/permit2-erc20ApprovalGasSponsoring", func(w http.ResponseWriter, r *http.Request) {
+ if shutdownRequested {
+ writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ return
+ }
+
+ writeJSON(w, http.StatusOK, map[string]interface{}{
+ "message": "Permit2 ERC-20 approval endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "permit2-erc20-approval",
+ })
+ })
+
+ mux.HandleFunc("GET /upto/evm/permit2", func(w http.ResponseWriter, r *http.Request) {
+ if shutdownRequested {
+ writeJSON(w, http.StatusServiceUnavailable, map[string]interface{}{
+ "error": "Server shutting down",
+ })
+ return
+ }
+
+ nethttpmw.SetSettlementOverrides(w, &x402.SettlementOverrides{Amount: "1000"})
+
+ writeJSON(w, http.StatusOK, map[string]interface{}{
+ "message": "Upto Permit2 endpoint accessed successfully",
+ "timestamp": time.Now().Format(time.RFC3339),
+ "method": "upto-permit2",
+ })
+ })
+
+ // Health check endpoint - no payment required
+ mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
+ writeJSON(w, http.StatusOK, map[string]interface{}{
+ "status": "ok",
+ "version": "2.0.0",
+ "evm_network": string(evmNetwork),
+ "evm_payee": evmPayeeAddress,
+ "svm_network": string(svmNetwork),
+ "svm_payee": svmPayeeAddress,
+ })
+ })
+
+ // Shutdown endpoint - used by e2e tests
+ mux.HandleFunc("POST /close", func(w http.ResponseWriter, r *http.Request) {
+ shutdownRequested = true
+
+ writeJSON(w, http.StatusOK, map[string]interface{}{
+ "message": "Server shutting down gracefully",
+ })
+ fmt.Println("Received shutdown request")
+
+ // Schedule server shutdown after response
+ go func() {
+ time.Sleep(100 * time.Millisecond)
+ os.Exit(0)
+ }()
+ })
+
+ // Apply payment middleware with detailed error logging
+ handler := nethttpmw.X402Payment(nethttpmw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []nethttpmw.SchemeConfig{
+ {Network: evmNetwork, Server: exactevm.NewExactEvmScheme()},
+ {Network: evmNetwork, Server: uptoevm.NewUptoEvmScheme()},
+ {Network: svmNetwork, Server: svm.NewExactSvmScheme()},
+ },
+ SyncFacilitatorOnStart: true,
+ Timeout: 30 * time.Second,
+ ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
+ // Log detailed error information for debugging
+ fmt.Printf("❌ [E2E SERVER ERROR] Payment error occurred\n")
+ fmt.Printf(" Path: %s\n", r.URL.Path)
+ fmt.Printf(" Method: %s\n", r.Method)
+ fmt.Printf(" Error: %v\n", err)
+ fmt.Printf(" Headers: %v\n", r.Header)
+
+ // Default error response
+ writeJSON(w, http.StatusPaymentRequired, map[string]interface{}{
+ "error": err.Error(),
+ })
+ },
+ SettlementHandler: func(w http.ResponseWriter, r *http.Request, settleResp *x402.SettleResponse) {
+ // Log successful settlement
+ fmt.Printf("✅ [E2E SERVER SUCCESS] Payment settled\n")
+ fmt.Printf(" Path: %s\n", r.URL.Path)
+ fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
+ fmt.Printf(" Network: %s\n", settleResp.Network)
+ fmt.Printf(" Payer: %s\n", settleResp.Payer)
+ },
+ })(mux)
+
+ // Set up graceful shutdown
+ quit := make(chan os.Signal, 1)
+ signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
+
+ go func() {
+ <-quit
+ fmt.Println("Received shutdown signal, exiting...")
+ os.Exit(0)
+ }()
+
+ // Print startup banner
+ fmt.Printf(`
+╔════════════════════════════════════════════════════════╗
+║ x402 net/http E2E Test Server ║
+╠════════════════════════════════════════════════════════╣
+║ Server: http://localhost:%-29s ║
+║ EVM Network: %-40s ║
+║ EVM Payee: %-40s ║
+║ SVM Network: %-40s ║
+║ SVM Payee: %-40s ║
+║ ║
+║ Endpoints: ║
+║ • GET /exact/evm/eip3009 (EVM EIP-3009) ║
+║ • GET /exact/evm/permit2 (Permit2) ║
+║ • GET /exact/evm/permit2-eip2612GasSponsoring ║
+║ • GET /exact/evm/permit2-erc20ApprovalGasSponsoring ║
+║ • GET /exact/svm (SVM) ║
+║ • GET /health (no payment required) ║
+║ • POST /close (shutdown server) ║
+╚════════════════════════════════════════════════════════╝
+`, port, evmNetwork, evmPayeeAddress, svmNetwork, svmPayeeAddress)
+
+ server := &http.Server{
+ Addr: ":" + port,
+ Handler: handler,
+ }
+
+ if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+ fmt.Printf("Error starting server: %v\n", err)
+ os.Exit(1)
+ }
+}
+
+// writeJSON is a helper to write JSON responses.
+func writeJSON(w http.ResponseWriter, status int, data interface{}) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(status)
+ _ = json.NewEncoder(w).Encode(data)
+}
diff --git a/e2e/servers/nethttp/run.sh b/e2e/servers/nethttp/run.sh
new file mode 100755
index 0000000000..5f2f5f8501
--- /dev/null
+++ b/e2e/servers/nethttp/run.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+go run main.go
diff --git a/e2e/servers/nethttp/test.config.json b/e2e/servers/nethttp/test.config.json
new file mode 100644
index 0000000000..a77eb5bbb6
--- /dev/null
+++ b/e2e/servers/nethttp/test.config.json
@@ -0,0 +1,88 @@
+{
+ "name": "nethttp",
+ "type": "server",
+ "language": "go",
+ "x402Version": 2,
+ "extensions": [
+ "bazaar",
+ "eip2612GasSponsoring",
+ "erc20ApprovalGasSponsoring"
+ ],
+ "evm": { "transferMethods": ["eip3009", "permit2"] },
+ "description": "Go net/http server with x402 v2 payment middleware",
+ "endpoints": [
+ {
+ "path": "/exact/evm/eip3009",
+ "method": "GET",
+ "description": "Protected endpoint requiring EIP-3009 payment",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "eip3009"
+ },
+ {
+ "path": "/exact/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment (standard settle, no gas sponsoring)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "permit2Direct": true
+ },
+ {
+ "path": "/exact/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "coldstart": true
+ },
+ {
+ "path": "/exact/evm/permit2-erc20ApprovalGasSponsoring",
+ "method": "GET",
+ "description": "Protected endpoint requiring Permit2 payment with ERC-20 approval gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
+ },
+ {
+ "path": "/upto/evm/permit2",
+ "method": "GET",
+ "description": "Protected endpoint requiring upto Permit2 payment (usage-based settlement)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "permit2Direct": true
+ },
+ {
+ "path": "/exact/svm",
+ "method": "GET",
+ "description": "Protected endpoint requiring payment (SVM)",
+ "requiresPayment": true,
+ "protocolFamily": "svm"
+ },
+ {
+ "path": "/health",
+ "method": "GET",
+ "description": "Health check endpoint",
+ "health": true
+ },
+ {
+ "path": "/close",
+ "method": "POST",
+ "description": "Graceful shutdown endpoint",
+ "close": true
+ }
+ ],
+ "environment": {
+ "required": [
+ "PORT",
+ "EVM_PAYEE_ADDRESS",
+ "SVM_PAYEE_ADDRESS",
+ "FACILITATOR_URL"
+ ],
+ "optional": []
+ }
+}
diff --git a/e2e/servers/next/.env-local b/e2e/servers/next/.env-local
index 922c516362..4fb43f9906 100644
--- a/e2e/servers/next/.env-local
+++ b/e2e/servers/next/.env-local
@@ -1,3 +1,5 @@
-NEXT_PUBLIC_FACILITATOR_URL=https://x402.org/facilitator
-NETWORK=base-sepolia
-RESOURCE_WALLET_ADDRESS=
+PORT=3000
+EVM_PAYEE_ADDRESS=
+SVM_PAYEE_ADDRESS=
+STELLAR_PAYEE_ADDRESS=
+FACILITATOR_URL=http://localhost:4000
diff --git a/e2e/servers/next/README.md b/e2e/servers/next/README.md
index 87fb5e128a..5992b03e2d 100644
--- a/e2e/servers/next/README.md
+++ b/e2e/servers/next/README.md
@@ -1,16 +1,18 @@
# x402-next Example App
-This is a Next.js application that demonstrates how to use the `x402-next` middleware to implement paywall functionality in your Next.js routes.
+This is a Next.js application that demonstrates how to use the `x402-next` middleware to implement paywall functionality in your Next.js routes with EVM, SVM, and optional Stellar payment support.
## Prerequisites
- Node.js v20+ (install via [nvm](https://github.com/nvm-sh/nvm))
- pnpm v10 (install via [pnpm.io/installation](https://pnpm.io/installation))
- A valid Ethereum address for receiving payments
+- A valid Solana address for receiving payments
+- (Optional) A valid Stellar address for receiving payments
## Setup
-1. Copy `.env.local` to `.env` and add your Ethereum address to receive payments:
+1. Copy `.env.local` to `.env` and add your addresses to receive payments:
```bash
cp .env.local .env
@@ -125,3 +127,17 @@ export const config = {
matcher: ["/protected/:path*", "/api/premium/:path*"],
};
```
+
+## Environment Variables
+
+### Required
+- `PORT` - HTTP server port
+- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments
+- `SVM_PAYEE_ADDRESS` - Solana address to receive payments
+- `FACILITATOR_URL` - Facilitator endpoint URL
+
+### Optional
+- `STELLAR_PAYEE_ADDRESS` - Stellar address to receive payments - enables Stellar endpoints
+- `EVM_NETWORK` - EVM network (default: eip155:84532)
+- `SVM_NETWORK` - SVM network (default: solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)
+- `STELLAR_NETWORK` - Stellar network (default: stellar:testnet)
diff --git a/e2e/servers/next/app/api/close/route.ts b/e2e/servers/next/app/api/close/route.ts
index d2132c17c3..94835fd216 100644
--- a/e2e/servers/next/app/api/close/route.ts
+++ b/e2e/servers/next/app/api/close/route.ts
@@ -20,4 +20,4 @@ export async function POST() {
return NextResponse.json({
message: "Shutting down gracefully",
});
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/next/app/api/protected-proxy/route.ts b/e2e/servers/next/app/api/exact/aptos/route.ts
similarity index 66%
rename from e2e/servers/next/app/api/protected-proxy/route.ts
rename to e2e/servers/next/app/api/exact/aptos/route.ts
index 739c214d86..95caf09527 100644
--- a/e2e/servers/next/app/api/protected-proxy/route.ts
+++ b/e2e/servers/next/app/api/exact/aptos/route.ts
@@ -1,17 +1,13 @@
import { NextResponse } from "next/server";
/**
- * Protected endpoint requiring payment (proxy middleware)
+ * Aptos endpoint requiring payment (proxy middleware)
*/
export const runtime = "nodejs";
-/**
- * Protected endpoint requiring payment (proxy middleware)
- */
export async function GET() {
return NextResponse.json({
message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
});
}
-
diff --git a/e2e/servers/next/app/api/protected-svm-proxy/route.ts b/e2e/servers/next/app/api/exact/evm/eip3009/proxy/route.ts
similarity index 65%
rename from e2e/servers/next/app/api/protected-svm-proxy/route.ts
rename to e2e/servers/next/app/api/exact/evm/eip3009/proxy/route.ts
index f908c8c635..7912dfc6b4 100644
--- a/e2e/servers/next/app/api/protected-svm-proxy/route.ts
+++ b/e2e/servers/next/app/api/exact/evm/eip3009/proxy/route.ts
@@ -1,17 +1,13 @@
import { NextResponse } from "next/server";
/**
- * Protected SVM endpoint requiring payment (proxy middleware)
+ * EVM EIP-3009 endpoint requiring payment (proxy middleware)
*/
export const runtime = "nodejs";
-/**
- * Protected SVM endpoint requiring payment (proxy middleware)
- */
export async function GET() {
return NextResponse.json({
message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
});
}
-
diff --git a/e2e/servers/next/app/api/protected-withx402/route.ts b/e2e/servers/next/app/api/exact/evm/eip3009/withx402/route.ts
similarity index 99%
rename from e2e/servers/next/app/api/protected-withx402/route.ts
rename to e2e/servers/next/app/api/exact/evm/eip3009/withx402/route.ts
index a7bbbb2f1c..5edb0c1e1d 100644
--- a/e2e/servers/next/app/api/protected-withx402/route.ts
+++ b/e2e/servers/next/app/api/exact/evm/eip3009/withx402/route.ts
@@ -46,4 +46,3 @@ export const GET = withX402(
},
server,
);
-
diff --git a/e2e/servers/next/app/api/protected-permit2-proxy/route.ts b/e2e/servers/next/app/api/exact/evm/permit2-eip2612GasSponsoring/proxy/route.ts
similarity index 73%
rename from e2e/servers/next/app/api/protected-permit2-proxy/route.ts
rename to e2e/servers/next/app/api/exact/evm/permit2-eip2612GasSponsoring/proxy/route.ts
index aec9f5a500..dc3e3b0e1a 100644
--- a/e2e/servers/next/app/api/protected-permit2-proxy/route.ts
+++ b/e2e/servers/next/app/api/exact/evm/permit2-eip2612GasSponsoring/proxy/route.ts
@@ -1,14 +1,13 @@
import { NextResponse } from "next/server";
/**
- * Protected Permit2 endpoint requiring payment (proxy middleware)
+ * EVM Permit2 EIP-2612 gas sponsoring endpoint requiring payment (proxy middleware)
*/
export const runtime = "nodejs";
export async function GET() {
return NextResponse.json({
- message: "Permit2 endpoint accessed successfully",
+ message: "Protected endpoint accessed successfully",
timestamp: new Date().toISOString(),
- method: "permit2",
});
}
diff --git a/e2e/servers/next/app/api/exact/evm/permit2-erc20ApprovalGasSponsoring/proxy/route.ts b/e2e/servers/next/app/api/exact/evm/permit2-erc20ApprovalGasSponsoring/proxy/route.ts
new file mode 100644
index 0000000000..5867755d06
--- /dev/null
+++ b/e2e/servers/next/app/api/exact/evm/permit2-erc20ApprovalGasSponsoring/proxy/route.ts
@@ -0,0 +1,13 @@
+import { NextResponse } from "next/server";
+
+/**
+ * EVM Permit2 ERC-20 approval gas sponsoring endpoint requiring payment (proxy middleware)
+ */
+export const runtime = "nodejs";
+
+export async function GET() {
+ return NextResponse.json({
+ message: "Protected endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ });
+}
diff --git a/e2e/servers/next/app/api/exact/evm/permit2/proxy/route.ts b/e2e/servers/next/app/api/exact/evm/permit2/proxy/route.ts
new file mode 100644
index 0000000000..09b5d2e9d2
--- /dev/null
+++ b/e2e/servers/next/app/api/exact/evm/permit2/proxy/route.ts
@@ -0,0 +1,13 @@
+import { NextResponse } from "next/server";
+
+/**
+ * EVM Permit2 direct endpoint requiring payment (proxy middleware)
+ */
+export const runtime = "nodejs";
+
+export async function GET() {
+ return NextResponse.json({
+ message: "Protected endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ });
+}
diff --git a/e2e/servers/next/app/api/exact/stellar/route.ts b/e2e/servers/next/app/api/exact/stellar/route.ts
new file mode 100644
index 0000000000..38b773d52a
--- /dev/null
+++ b/e2e/servers/next/app/api/exact/stellar/route.ts
@@ -0,0 +1,13 @@
+import { NextResponse } from "next/server";
+
+/**
+ * Stellar endpoint requiring payment (proxy middleware)
+ */
+export const runtime = "nodejs";
+
+export async function GET() {
+ return NextResponse.json({
+ message: "Protected endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ });
+}
diff --git a/e2e/servers/next/app/api/exact/stellar/withx402/route.ts b/e2e/servers/next/app/api/exact/stellar/withx402/route.ts
new file mode 100644
index 0000000000..7082891b44
--- /dev/null
+++ b/e2e/servers/next/app/api/exact/stellar/withx402/route.ts
@@ -0,0 +1,55 @@
+import { NextRequest, NextResponse } from "next/server";
+import { withX402 } from "@x402/next";
+import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
+import { server, STELLAR_PAYEE_ADDRESS, STELLAR_NETWORK } from "../../../proxy";
+
+/**
+ * Handler for the protected endpoint
+ */
+const handler = async (_: NextRequest) => {
+ return NextResponse.json({
+ message: "Protected Stellar endpoint accessed successfully (withX402)",
+ timestamp: new Date().toISOString(),
+ });
+};
+
+/**
+ * Protected Stellar endpoint using withX402 wrapper
+ * Only exported if STELLAR_PAYEE_ADDRESS is configured
+ */
+export const GET = STELLAR_PAYEE_ADDRESS
+ ? withX402(
+ handler,
+ {
+ accepts: {
+ payTo: STELLAR_PAYEE_ADDRESS,
+ scheme: "exact",
+ price: "$0.001",
+ network: STELLAR_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected Stellar endpoint accessed successfully (withX402)",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
+ },
+ },
+ }),
+ },
+ },
+ server,
+ )
+ : async () => {
+ return NextResponse.json(
+ { error: "Stellar not configured" },
+ { status: 503 },
+ );
+ };
diff --git a/e2e/servers/next/app/api/exact/svm/route.ts b/e2e/servers/next/app/api/exact/svm/route.ts
new file mode 100644
index 0000000000..001d600906
--- /dev/null
+++ b/e2e/servers/next/app/api/exact/svm/route.ts
@@ -0,0 +1,13 @@
+import { NextResponse } from "next/server";
+
+/**
+ * SVM endpoint requiring payment (proxy middleware)
+ */
+export const runtime = "nodejs";
+
+export async function GET() {
+ return NextResponse.json({
+ message: "Protected endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ });
+}
diff --git a/e2e/servers/next/app/api/protected-svm-withx402/route.ts b/e2e/servers/next/app/api/exact/svm/withx402/route.ts
similarity index 99%
rename from e2e/servers/next/app/api/protected-svm-withx402/route.ts
rename to e2e/servers/next/app/api/exact/svm/withx402/route.ts
index 57b3736191..ea63bc43c2 100644
--- a/e2e/servers/next/app/api/protected-svm-withx402/route.ts
+++ b/e2e/servers/next/app/api/exact/svm/withx402/route.ts
@@ -45,4 +45,3 @@ export const GET = withX402(
},
server,
);
-
diff --git a/e2e/servers/next/app/api/health/route.ts b/e2e/servers/next/app/api/health/route.ts
index 8a57257b35..1c3a28cfc3 100644
--- a/e2e/servers/next/app/api/health/route.ts
+++ b/e2e/servers/next/app/api/health/route.ts
@@ -12,4 +12,4 @@ export async function GET() {
return NextResponse.json({
status: "healthy",
});
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/next/app/api/protected-aptos-proxy/route.ts b/e2e/servers/next/app/api/protected-aptos-proxy/route.ts
deleted file mode 100644
index 2b3a78839d..0000000000
--- a/e2e/servers/next/app/api/protected-aptos-proxy/route.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { NextResponse } from "next/server";
-import { APTOS_PAYEE_ADDRESS } from "../../../proxy";
-
-/**
- * Protected Aptos endpoint requiring payment (proxy middleware)
- */
-export const runtime = "nodejs";
-
-/**
- * Protected Aptos endpoint requiring payment (proxy middleware)
- */
-export async function GET() {
- if (!APTOS_PAYEE_ADDRESS) {
- return NextResponse.json({
- error: "Aptos payments not configured",
- message: "APTOS_PAYEE_ADDRESS environment variable is not set",
- }, { status: 501 });
- }
- return NextResponse.json({
- message: "Protected endpoint accessed successfully",
- timestamp: new Date().toISOString(),
- });
-}
diff --git a/e2e/servers/next/app/api/protected-permit2-erc20-proxy/route.ts b/e2e/servers/next/app/api/protected-permit2-erc20-proxy/route.ts
deleted file mode 100644
index 66b4c383c4..0000000000
--- a/e2e/servers/next/app/api/protected-permit2-erc20-proxy/route.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { NextResponse } from "next/server";
-
-/**
- * Protected Permit2 ERC-20 endpoint requiring payment with ERC-20 approval gas sponsoring (proxy middleware)
- */
-export const runtime = "nodejs";
-
-export async function GET() {
- return NextResponse.json({
- message: "Permit2 ERC-20 approval endpoint accessed successfully",
- timestamp: new Date().toISOString(),
- method: "permit2-erc20-approval",
- });
-}
diff --git a/e2e/servers/next/app/api/upto/evm/permit2-eip2612GasSponsoring/route.ts b/e2e/servers/next/app/api/upto/evm/permit2-eip2612GasSponsoring/route.ts
new file mode 100644
index 0000000000..f7ff3bbe24
--- /dev/null
+++ b/e2e/servers/next/app/api/upto/evm/permit2-eip2612GasSponsoring/route.ts
@@ -0,0 +1,14 @@
+import { NextResponse } from "next/server";
+
+/**
+ * Upto Permit2 EIP-2612 endpoint requiring payment (proxy middleware)
+ */
+export const runtime = "nodejs";
+
+export async function GET() {
+ return NextResponse.json({
+ message: "Upto Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2-eip2612",
+ });
+}
diff --git a/e2e/servers/next/app/api/upto/evm/permit2-erc20ApprovalGasSponsoring/route.ts b/e2e/servers/next/app/api/upto/evm/permit2-erc20ApprovalGasSponsoring/route.ts
new file mode 100644
index 0000000000..526b5f786b
--- /dev/null
+++ b/e2e/servers/next/app/api/upto/evm/permit2-erc20ApprovalGasSponsoring/route.ts
@@ -0,0 +1,14 @@
+import { NextResponse } from "next/server";
+
+/**
+ * Upto Permit2 ERC-20 approval endpoint requiring payment (proxy middleware)
+ */
+export const runtime = "nodejs";
+
+export async function GET() {
+ return NextResponse.json({
+ message: "Upto Permit2 ERC-20 approval endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2-erc20-approval",
+ });
+}
diff --git a/e2e/servers/next/app/api/upto/evm/permit2/route.ts b/e2e/servers/next/app/api/upto/evm/permit2/route.ts
new file mode 100644
index 0000000000..79743431b9
--- /dev/null
+++ b/e2e/servers/next/app/api/upto/evm/permit2/route.ts
@@ -0,0 +1,18 @@
+import { NextResponse } from "next/server";
+import { SETTLEMENT_OVERRIDES_HEADER } from "@x402/core/server";
+
+/**
+ * Upto Permit2 direct endpoint requiring payment (proxy middleware)
+ * Client must have Permit2 pre-approved. Settles partial amount (1000 of 2000 authorized).
+ */
+export const runtime = "nodejs";
+
+export async function GET() {
+ const response = NextResponse.json({
+ message: "Upto Permit2 endpoint accessed successfully",
+ timestamp: new Date().toISOString(),
+ method: "upto-permit2",
+ });
+ response.headers.set(SETTLEMENT_OVERRIDES_HEADER, JSON.stringify({ amount: "1000" }));
+ return response;
+}
diff --git a/e2e/servers/next/next.config.ts b/e2e/servers/next/next.config.ts
index db0a372753..cb651cdc00 100644
--- a/e2e/servers/next/next.config.ts
+++ b/e2e/servers/next/next.config.ts
@@ -1,6 +1,5 @@
import type { NextConfig } from "next";
-const nextConfig: NextConfig = {
-};
+const nextConfig: NextConfig = {};
export default nextConfig;
diff --git a/e2e/servers/next/package.json b/e2e/servers/next/package.json
index 917e7c47ad..e709f385b2 100644
--- a/e2e/servers/next/package.json
+++ b/e2e/servers/next/package.json
@@ -18,6 +18,7 @@
"@x402/evm": "workspace:*",
"@x402/extensions": "workspace:*",
"@x402/next": "workspace:*",
+ "@x402/stellar": "workspace:*",
"@x402/svm": "workspace:*",
"@heroicons/react": "^2.2.0",
"next": "^16.0.10",
@@ -45,4 +46,4 @@
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/next/proxy.ts b/e2e/servers/next/proxy.ts
index 5225b8204c..8f5f877dd9 100644
--- a/e2e/servers/next/proxy.ts
+++ b/e2e/servers/next/proxy.ts
@@ -1,8 +1,10 @@
import { paymentProxy } from "@x402/next";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { UptoEvmScheme } from "@x402/evm/upto/server";
import { ExactSvmScheme } from "@x402/svm/exact/server";
import { ExactAptosScheme } from "@x402/aptos/exact/server";
+import { ExactStellarScheme } from "@x402/stellar/exact/server";
import { bazaarResourceServerExtension, declareDiscoveryExtension } from "@x402/extensions/bazaar";
import {
declareEip2612GasSponsoringExtension,
@@ -12,9 +14,14 @@ import {
export const EVM_PAYEE_ADDRESS = process.env.EVM_PAYEE_ADDRESS as `0x${string}`;
export const SVM_PAYEE_ADDRESS = process.env.SVM_PAYEE_ADDRESS as string;
export const APTOS_PAYEE_ADDRESS = process.env.APTOS_PAYEE_ADDRESS as string;
+export const STELLAR_PAYEE_ADDRESS = process.env.STELLAR_PAYEE_ADDRESS as string | undefined;
export const EVM_NETWORK = (process.env.EVM_NETWORK || "eip155:84532") as `${string}:${string}`;
-export const SVM_NETWORK = (process.env.SVM_NETWORK || "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") as `${string}:${string}`;
+export const SVM_NETWORK = (process.env.SVM_NETWORK ||
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1") as `${string}:${string}`;
export const APTOS_NETWORK = (process.env.APTOS_NETWORK || "aptos:2") as `${string}:${string}`;
+export const STELLAR_NETWORK = (process.env.STELLAR_NETWORK ||
+ "stellar:testnet") as `${string}:${string}`;
+const EVM_PERMIT2_ASSET = process.env.EVM_PERMIT2_ASSET as `0x${string}`;
const facilitatorUrl = process.env.FACILITATOR_URL;
if (!facilitatorUrl) {
@@ -22,18 +29,26 @@ if (!facilitatorUrl) {
process.exit(1);
}
-// Create HTTP facilitator client
-const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+// Create facilitator clients (mock facilitator as fallback for startup validation)
+const facilitatorClients = [new HTTPFacilitatorClient({ url: facilitatorUrl })];
+const mockFacilitatorUrl = process.env.MOCK_FACILITATOR_URL;
+if (mockFacilitatorUrl) {
+ facilitatorClients.push(new HTTPFacilitatorClient({ url: mockFacilitatorUrl }));
+}
// Create x402 resource server with builder pattern (cleaner!)
-export const server = new x402ResourceServer(facilitatorClient);
+export const server = new x402ResourceServer(facilitatorClients);
// Register server schemes
server.register("eip155:*", new ExactEvmScheme());
+server.register("eip155:*", new UptoEvmScheme());
server.register("solana:*", new ExactSvmScheme());
if (APTOS_PAYEE_ADDRESS) {
server.register("aptos:*", new ExactAptosScheme());
}
+if (STELLAR_PAYEE_ADDRESS) {
+ server.register("stellar:*", new ExactStellarScheme());
+}
// Register Bazaar discovery extension
server.registerExtension(bazaarResourceServerExtension);
@@ -42,7 +57,7 @@ console.log(`Using remote facilitator at: ${facilitatorUrl}`);
export const proxy = paymentProxy(
{
- "/api/protected-proxy": {
+ "/api/exact/evm/eip3009/proxy": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
@@ -67,7 +82,7 @@ export const proxy = paymentProxy(
}),
},
},
- "/api/protected-svm-proxy": {
+ "/api/exact/svm": {
accepts: {
payTo: SVM_PAYEE_ADDRESS,
scheme: "exact",
@@ -94,45 +109,72 @@ export const proxy = paymentProxy(
},
...(APTOS_PAYEE_ADDRESS
? {
- "/api/protected-aptos-proxy": {
- accepts: {
- payTo: APTOS_PAYEE_ADDRESS,
- scheme: "exact",
- price: "$0.001",
- network: APTOS_NETWORK,
- },
- extensions: {
- ...declareDiscoveryExtension({
- output: {
- example: {
- message: "Protected endpoint accessed successfully",
- timestamp: "2024-01-01T00:00:00Z",
+ "/api/exact/aptos": {
+ accepts: {
+ payTo: APTOS_PAYEE_ADDRESS,
+ scheme: "exact",
+ price: "$0.001",
+ network: APTOS_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
+ },
},
- schema: {
- properties: {
- message: { type: "string" },
- timestamp: { type: "string" },
+ }),
+ },
+ },
+ }
+ : {}),
+ ...(STELLAR_PAYEE_ADDRESS
+ ? {
+ "/api/exact/stellar": {
+ accepts: {
+ payTo: STELLAR_PAYEE_ADDRESS,
+ scheme: "exact",
+ price: "$0.001",
+ network: STELLAR_NETWORK,
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Protected endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ },
+ required: ["message", "timestamp"],
},
- required: ["message", "timestamp"],
},
- },
- }),
+ }),
+ },
},
- },
- }
+ }
: {}),
- "/api/protected-permit2-proxy": {
+ "/api/exact/evm/permit2/proxy": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
network: EVM_NETWORK,
price: {
amount: "1000",
- asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ asset: EVM_PERMIT2_ASSET,
extra: {
assetTransferMethod: "permit2",
- name: "USDC",
- version: "2",
},
},
},
@@ -154,17 +196,97 @@ export const proxy = paymentProxy(
},
},
}),
+ },
+ },
+ "/api/exact/evm/permit2-eip2612GasSponsoring/proxy": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "exact",
+ network: EVM_NETWORK,
+ price: "$0.001",
+ extra: { assetTransferMethod: "permit2" },
+ },
+ extensions: {
+ ...declareDiscoveryExtension({
+ output: {
+ example: {
+ message: "Permit2 EIP-2612 endpoint accessed successfully",
+ timestamp: "2024-01-01T00:00:00Z",
+ method: "permit2-eip2612",
+ },
+ schema: {
+ properties: {
+ message: { type: "string" },
+ timestamp: { type: "string" },
+ method: { type: "string" },
+ },
+ required: ["message", "timestamp", "method"],
+ },
+ },
+ }),
...declareEip2612GasSponsoringExtension(),
},
},
- "/api/protected-permit2-erc20-proxy": {
+ "/api/exact/evm/permit2-erc20ApprovalGasSponsoring/proxy": {
accepts: {
payTo: EVM_PAYEE_ADDRESS,
scheme: "exact",
network: EVM_NETWORK,
price: {
amount: "1000",
- asset: "0xeED520980fC7C7B4eB379B96d61CEdea2423005a",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ },
+ },
+ },
+ extensions: {
+ ...declareErc20ApprovalGasSponsoringExtension(),
+ },
+ },
+ "/api/upto/evm/permit2": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ },
+ "/api/upto/evm/permit2-eip2612GasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
+ extra: {
+ assetTransferMethod: "permit2",
+ name: EVM_NETWORK == "eip155:84532" ? "USDC" : "USD Coin",
+ version: "2",
+ },
+ },
+ },
+ extensions: {
+ ...declareEip2612GasSponsoringExtension(),
+ },
+ },
+ "/api/upto/evm/permit2-erc20ApprovalGasSponsoring": {
+ accepts: {
+ payTo: EVM_PAYEE_ADDRESS,
+ scheme: "upto",
+ network: EVM_NETWORK,
+ price: {
+ amount: "2000",
+ asset: EVM_PERMIT2_ASSET,
extra: {
assetTransferMethod: "permit2",
},
@@ -179,5 +301,16 @@ export const proxy = paymentProxy(
);
export const config = {
- matcher: ["/api/protected-proxy", "/api/protected-svm-proxy", "/api/protected-aptos-proxy", "/api/protected-permit2-proxy", "/api/protected-permit2-erc20-proxy"],
+ matcher: [
+ "/api/exact/evm/eip3009/proxy",
+ "/api/exact/svm",
+ "/api/exact/aptos",
+ "/api/exact/stellar",
+ "/api/exact/evm/permit2/proxy",
+ "/api/exact/evm/permit2-eip2612GasSponsoring/proxy",
+ "/api/exact/evm/permit2-erc20ApprovalGasSponsoring/proxy",
+ "/api/upto/evm/permit2",
+ "/api/upto/evm/permit2-eip2612GasSponsoring",
+ "/api/upto/evm/permit2-erc20ApprovalGasSponsoring",
+ ],
};
diff --git a/e2e/servers/next/test.config.json b/e2e/servers/next/test.config.json
index cc502099bb..be5db0dc67 100644
--- a/e2e/servers/next/test.config.json
+++ b/e2e/servers/next/test.config.json
@@ -3,66 +3,116 @@
"type": "server",
"language": "typescript",
"x402Version": 2,
- "extensions": [
- "bazaar",
- "eip2612GasSponsoring",
- "erc20ApprovalGasSponsoring"
- ],
+ "extensions": ["bazaar", "eip2612GasSponsoring", "erc20ApprovalGasSponsoring"],
+
"endpoints": [
{
- "path": "/api/protected-proxy",
+ "path": "/api/exact/evm/eip3009/proxy",
"method": "GET",
- "description": "Protected endpoint using proxy middleware",
+ "description": "EVM EIP-3009 endpoint using proxy middleware",
"requiresPayment": true,
"protocolFamily": "evm",
"transferMethod": "eip3009"
},
{
- "path": "/api/protected-permit2-proxy",
+ "path": "/api/exact/evm/permit2/proxy",
+ "method": "GET",
+ "description": "EVM Permit2 direct endpoint (standard settle, no gas sponsoring)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "permit2",
+ "permit2Direct": true
+ },
+ {
+ "path": "/api/exact/evm/permit2-eip2612GasSponsoring/proxy",
"method": "GET",
- "description": "Protected Permit2 endpoint using proxy middleware",
+ "description": "EVM Permit2 endpoint with EIP-2612 gas sponsoring using proxy middleware",
"requiresPayment": true,
"protocolFamily": "evm",
- "permit2": true
+ "transferMethod": "permit2",
+ "coldstart": true
},
{
- "path": "/api/protected-permit2-erc20-proxy",
+ "path": "/api/exact/evm/permit2-erc20ApprovalGasSponsoring/proxy",
"method": "GET",
- "description": "Protected Permit2 endpoint with ERC-20 approval gas sponsoring using proxy middleware",
+ "description": "EVM Permit2 endpoint with ERC-20 approval gas sponsoring using proxy middleware",
"requiresPayment": true,
"protocolFamily": "evm",
"transferMethod": "permit2",
- "extensions": ["erc20ApprovalGasSponsoring"]
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
},
{
- "path": "/api/protected-svm-proxy",
+ "path": "/api/upto/evm/permit2",
"method": "GET",
- "description": "Protected endpoint using proxy middleware on SVM",
+ "description": "Protected Upto Permit2 endpoint (direct, client must pre-approve)",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "permit2Direct": true
+ },
+ {
+ "path": "/api/upto/evm/permit2-eip2612GasSponsoring",
+ "method": "GET",
+ "description": "Protected Upto Permit2 endpoint with EIP-2612 gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "coldstart": true
+ },
+ {
+ "path": "/api/upto/evm/permit2-erc20ApprovalGasSponsoring",
+ "method": "GET",
+ "description": "Protected Upto Permit2 endpoint with ERC-20 approval gas sponsoring",
+ "requiresPayment": true,
+ "protocolFamily": "evm",
+ "transferMethod": "upto",
+ "extensions": ["erc20ApprovalGasSponsoring"],
+ "coldstart": true
+ },
+ {
+ "path": "/api/exact/svm",
+ "method": "GET",
+ "description": "SVM endpoint using proxy middleware",
"requiresPayment": true,
"protocolFamily": "svm"
},
{
- "path": "/api/protected-aptos-proxy",
+ "path": "/api/exact/aptos",
"method": "GET",
- "description": "Protected endpoint using proxy middleware on Aptos",
+ "description": "Aptos endpoint using proxy middleware",
"requiresPayment": true,
"protocolFamily": "aptos"
},
{
- "path": "/api/protected-withx402",
+ "path": "/api/exact/stellar",
"method": "GET",
- "description": "Protected endpoint using withX402 wrapper",
+ "description": "Stellar endpoint using proxy middleware",
+ "requiresPayment": true,
+ "protocolFamily": "stellar"
+ },
+ {
+ "path": "/api/exact/evm/eip3009/withx402",
+ "method": "GET",
+ "description": "EVM EIP-3009 endpoint using withX402 wrapper",
"requiresPayment": true,
"protocolFamily": "evm",
"transferMethod": "eip3009"
},
{
- "path": "/api/protected-svm-withx402",
+ "path": "/api/exact/svm/withx402",
"method": "GET",
- "description": "Protected endpoint using withX402 wrapper on SVM",
+ "description": "SVM endpoint using withX402 wrapper",
"requiresPayment": true,
"protocolFamily": "svm"
},
+ {
+ "path": "/api/exact/stellar/withx402",
+ "method": "GET",
+ "description": "Stellar endpoint using withX402 wrapper",
+ "requiresPayment": true,
+ "protocolFamily": "stellar"
+ },
{
"path": "/api/health",
"method": "GET",
@@ -77,14 +127,7 @@
}
],
"environment": {
- "required": [
- "PORT",
- "EVM_PAYEE_ADDRESS",
- "SVM_PAYEE_ADDRESS",
- "FACILITATOR_URL"
- ],
- "optional": [
- "APTOS_PAYEE_ADDRESS"
- ]
+ "required": ["PORT", "EVM_PAYEE_ADDRESS", "SVM_PAYEE_ADDRESS", "FACILITATOR_URL"],
+ "optional": ["APTOS_PAYEE_ADDRESS", "STELLAR_PAYEE_ADDRESS"]
}
-}
\ No newline at end of file
+}
diff --git a/e2e/servers/next/tsconfig.json b/e2e/servers/next/tsconfig.json
index aa175a3f81..6a3c51ac3f 100644
--- a/e2e/servers/next/tsconfig.json
+++ b/e2e/servers/next/tsconfig.json
@@ -1,11 +1,7 @@
{
"compilerOptions": {
"target": "ES2022",
- "lib": [
- "dom",
- "dom.iterable",
- "esnext"
- ],
+ "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
@@ -23,9 +19,7 @@
}
],
"paths": {
- "@/*": [
- "./*"
- ]
+ "@/*": ["./*"]
}
},
"include": [
@@ -36,7 +30,5 @@
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
- "exclude": [
- "node_modules"
- ]
+ "exclude": ["node_modules"]
}
diff --git a/e2e/servers/text-server-protocol.txt b/e2e/servers/text-server-protocol.txt
index 10fbd43e80..956e573b0c 100644
--- a/e2e/servers/text-server-protocol.txt
+++ b/e2e/servers/text-server-protocol.txt
@@ -18,6 +18,13 @@ Servers must declare which x402 protocol version they implement using the `x402V
Servers may declare which protocol extensions they support using the `extensions` field:
- **extensions**: Array of supported extension names (e.g., ["bazaar"])
+## Coldstart Tests
+Some endpoints require special pre-test state setup before they can be exercised (e.g., revoking a Permit2 approval so that the gas-sponsoring extension path is triggered). This setup is expensive: it involves funding the client wallet, submitting on-chain transactions, and draining ETH back.
+
+Endpoints that require this setup declare `"coldstart": true` in their test config. The test runner treats the **first** test for a given endpoint path within a combo as the "coldstart" test — it performs the full setup. Subsequent tests for the same endpoint path within the same combo skip the setup and run directly, exercising the "warm" (already-approved) code path.
+
+All explicit gas sponsorship extension endpoints (EIP-2612 and ERC-20 approval) should have `coldstart` enabled, as they must succeed without the user having gas or approval ahead of time.
+
## EVM Transfer Method
For EVM endpoints, servers must declare the `transferMethod` used for the payment transfer:
- **eip3009**: EIP-3009 transferWithAuthorization (default if omitted)
diff --git a/e2e/src/cli/args.ts b/e2e/src/cli/args.ts
index bab1b6b019..ec6332ff71 100644
--- a/e2e/src/cli/args.ts
+++ b/e2e/src/cli/args.ts
@@ -16,6 +16,7 @@ export interface ParsedArgs {
networkMode?: NetworkMode; // undefined = prompt user, set = skip prompt
parallel: boolean;
concurrency: number;
+ endpoints?: string[];
}
export function parseArgs(): ParsedArgs {
@@ -35,14 +36,15 @@ export function parseArgs(): ParsedArgs {
}
// Check if any filter args present -> programmatic mode
- const hasFilterArgs = args.some(arg =>
+ const hasFilterArgs = args.some(arg =>
arg.startsWith('--transport=') ||
arg.startsWith('--facilitators=') ||
arg.startsWith('--servers=') ||
arg.startsWith('--clients=') ||
arg.startsWith('--extensions=') ||
arg.startsWith('--versions=') ||
- arg.startsWith('--families=')
+ arg.startsWith('--families=') ||
+ arg.startsWith('--endpoints=')
);
const mode: 'interactive' | 'programmatic' = hasFilterArgs ? 'programmatic' : 'interactive';
@@ -50,8 +52,20 @@ export function parseArgs(): ParsedArgs {
// Parse verbose
const verbose = args.includes('-v') || args.includes('--verbose');
- // Parse log file
- const logFile = args.find(arg => arg.startsWith('--log-file='))?.split('=')[1];
+ // Parse log file — supports --log (timestamped default), --log=path, and legacy --log-file=path
+ let logFile: string | undefined;
+ const logArg = args.find(arg => arg === '--log' || arg.startsWith('--log='));
+ const legacyLogArg = args.find(arg => arg.startsWith('--log-file='));
+ if (logArg) {
+ if (logArg.includes('=')) {
+ logFile = logArg.split('=').slice(1).join('=');
+ } else {
+ const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
+ logFile = `logs/e2e-run-${ts}.log`;
+ }
+ } else if (legacyLogArg) {
+ logFile = legacyLogArg.split('=')[1];
+ }
// Parse JSON output file
const outputJson = args.find(arg => arg.startsWith('--output-json='))?.split('=')[1];
@@ -80,6 +94,7 @@ export function parseArgs(): ParsedArgs {
const extensions = parseListArg(args, '--extensions');
const versions = parseListArg(args, '--versions')?.map(v => parseInt(v));
const families = parseListArg(args, '--families');
+ const endpoints = parseListArg(args, '--endpoints');
return {
mode,
@@ -94,12 +109,14 @@ export function parseArgs(): ParsedArgs {
extensions,
versions,
protocolFamilies: families,
+ endpoints,
},
showHelp: false,
minimize,
networkMode,
parallel,
concurrency,
+ endpoints,
};
}
@@ -130,10 +147,12 @@ export function printHelp(): void {
console.log(' --extensions= Comma-separated extensions (e.g., bazaar)');
console.log(' --versions= Comma-separated version numbers (e.g., 1,2)');
console.log(' --families= Comma-separated protocol families (e.g., evm,svm)');
+ console.log(' --endpoints= Comma-separated endpoint paths or regex patterns (auto-anchored)');
console.log('');
console.log('Options:');
console.log(' -v, --verbose Enable verbose logging');
- console.log(' --log-file= Save verbose output to file');
+ console.log(' --log[=] Write output to file (default: logs/e2e-run-.log)');
+ console.log(' --log-file= Alias for --log= (legacy)');
console.log(' --output-json= Write structured JSON results to file');
console.log(' --min Minimize tests (coverage-based skipping)');
console.log(' --parallel Run server+facilitator combos concurrently');
@@ -147,6 +166,8 @@ export function printHelp(): void {
console.log(' pnpm test --min -v # Minimize with verbose');
console.log(' pnpm test --transport=mcp # MCP transport only');
console.log(' pnpm test --mainnet --facilitators=go --servers=express # Mainnet programmatic');
+ console.log(" pnpm test --testnet --endpoints='/protected' # Exact path match");
+ console.log(" pnpm test --testnet --endpoints='/protected-permit2.*' # Regex: all permit2 routes");
console.log(' pnpm test --testnet --min --parallel -v # Parallel mode');
console.log(' pnpm test --testnet --min --parallel --concurrency=2 -v # Limited concurrency');
console.log('');
diff --git a/e2e/src/cli/filters.ts b/e2e/src/cli/filters.ts
index 4ce7435c43..bf300f3cd9 100644
--- a/e2e/src/cli/filters.ts
+++ b/e2e/src/cli/filters.ts
@@ -8,6 +8,7 @@ export interface TestFilters {
extensions?: string[]; // For test output control (doesn't filter scenarios)
versions?: number[];
protocolFamilies?: string[];
+ endpoints?: string[]; // Regex patterns to filter by endpoint path
}
/**
@@ -64,6 +65,30 @@ export function filterScenarios(
}
}
+ // Endpoint filter — each entry is treated as a regex pattern.
+ // Patterns are auto-anchored (^...$) so that "/protected" matches only
+ // that exact path. To match a prefix, use "/protected.*"; for a substring
+ // anywhere, use ".*permit2.*" or omit anchors explicitly via ^ / $.
+ if (filters.endpoints && filters.endpoints.length > 0) {
+ const endpointPath = scenario.endpoint.path;
+ const matched = filters.endpoints.some(rawPattern => {
+ // Ensure patterns that look like paths start with /
+ const pattern = (!rawPattern.startsWith('/') && !rawPattern.startsWith('^'))
+ ? `/${rawPattern}`
+ : rawPattern;
+ try {
+ const anchored = (pattern.startsWith('^') || pattern.endsWith('$'))
+ ? pattern
+ : `^${pattern}$`;
+ return new RegExp(anchored).test(endpointPath);
+ } catch {
+ // Fall back to exact match if pattern is not valid regex
+ return endpointPath === pattern;
+ }
+ });
+ if (!matched) return false;
+ }
+
// NOTE: Extensions filter NOT applied - it only controls test output visibility
// Extensions are stored separately and passed to test execution logic
diff --git a/e2e/src/cli/interactive.ts b/e2e/src/cli/interactive.ts
index 655d606254..db4eab771e 100644
--- a/e2e/src/cli/interactive.ts
+++ b/e2e/src/cli/interactive.ts
@@ -27,16 +27,13 @@ export async function runInteractiveMode(
preselectedNetworkMode?: NetworkMode
): Promise {
- log('\n🎯 Interactive Mode');
- log('==================\n');
-
// Question 1: Select facilitators (multi-select)
// Sort facilitators: regular ones first, external ones at the bottom
const regularFacilitators = allFacilitators.filter(f => !f.isExternal);
const externalFacilitators = allFacilitators.filter(f => f.isExternal);
-
+
const facilitatorChoices: any[] = [];
-
+
// Add regular facilitators
regularFacilitators.forEach(f => {
facilitatorChoices.push({
@@ -45,7 +42,7 @@ export async function runInteractiveMode(
selected: minimize // With --min: all selected. Without --min: none selected
});
});
-
+
// Add external facilitators section if any exist
if (externalFacilitators.length > 0) {
// Add separator/header for external facilitators
@@ -54,7 +51,7 @@ export async function runInteractiveMode(
value: '__external_separator__',
disabled: true
});
-
+
externalFacilitators.forEach(f => {
facilitatorChoices.push({
title: `${f.name} (${formatVersions(f.config.x402Versions)}) [${f.config.protocolFamilies?.join(', ') || ''}]${f.config.extensions ? ' {' + f.config.extensions.join(', ') + '}' : ''}`,
@@ -283,7 +280,24 @@ export async function runInteractiveMode(
selectedFamilies = availableFamilies;
}
- // Question 8: Select network mode (testnet/mainnet) - LAST question
+ // Question 8: Endpoint filter (optional free-text, comma-separated regex patterns)
+ const endpointsResponse = await prompts({
+ type: 'text',
+ name: 'endpoints',
+ message: 'Filter endpoints (comma-separated patterns, blank = all)',
+ initial: '',
+ hint: 'e.g. /protected, permit2.* — supports regex',
+ });
+
+ // null means Ctrl-C; empty string means "all"
+ if (endpointsResponse.endpoints === undefined) {
+ return null;
+ }
+ const selectedEndpoints: string[] | undefined = endpointsResponse.endpoints
+ ? (endpointsResponse.endpoints as string).split(',').map((p: string) => p.trim()).filter((p: string) => p.length > 0)
+ : undefined;
+
+ // Question 9: Select network mode (testnet/mainnet) - LAST question
// Skip if preselected via CLI flag
let networkMode: NetworkMode;
@@ -332,6 +346,7 @@ export async function runInteractiveMode(
extensions: selectedExtensions,
versions: selectedVersions,
protocolFamilies: selectedFamilies,
+ endpoints: selectedEndpoints,
networkMode,
};
}
diff --git a/e2e/src/clients/generic-client.ts b/e2e/src/clients/generic-client.ts
index c4d0ec8142..aff2ed44a0 100644
--- a/e2e/src/clients/generic-client.ts
+++ b/e2e/src/clients/generic-client.ts
@@ -23,8 +23,11 @@ export class GenericClientProxy extends BaseProxy implements ClientProxy {
EVM_PRIVATE_KEY: config.evmPrivateKey,
SVM_PRIVATE_KEY: config.svmPrivateKey,
APTOS_PRIVATE_KEY: config.aptosPrivateKey,
+ STELLAR_PRIVATE_KEY: config.stellarPrivateKey,
RESOURCE_SERVER_URL: config.serverUrl,
ENDPOINT_PATH: config.endpointPath,
+ EVM_NETWORK: config.evmNetwork,
+ EVM_RPC_URL: config.evmRpcUrl,
}
};
diff --git a/e2e/src/discovery.ts b/e2e/src/discovery.ts
index 7f9b062ffb..46ea72118b 100644
--- a/e2e/src/discovery.ts
+++ b/e2e/src/discovery.ts
@@ -257,7 +257,6 @@ export class TestDiscovery {
}) || [];
for (const endpoint of testableEndpoints) {
- // Default to EVM if no protocol family specified for backward compatibility
const endpointProtocolFamily = endpoint.protocolFamily || 'evm';
// Only create scenarios where client supports endpoint's protocol family
@@ -313,12 +312,12 @@ export class TestDiscovery {
const facilitators = this.discoverFacilitators();
const scenarios = this.generateTestScenarios();
- log('🔍 Test Discovery Summary');
- log('========================');
+ verboseLog('🔍 Test Discovery Summary');
+ verboseLog('========================');
if (this.includeLegacy) {
- log('🔄 Legacy mode enabled - including legacy implementations');
+ verboseLog('🔄 Legacy mode enabled - including legacy implementations');
}
- log(`📡 Servers found: ${servers.length}`);
+ verboseLog(`📡 Servers found: ${servers.length}`);
servers.forEach(server => {
const paidEndpoints = server.config.endpoints?.filter(e => e.requiresPayment).length || 0;
const protocolFamilies = new Set(
@@ -326,22 +325,21 @@ export class TestDiscovery {
);
const version = server.config.x402Version || 1;
const transport = server.config.transport || 'http';
- log(` - ${server.name} (${server.config.language}) [${transport}] v${version} - ${paidEndpoints} x402 endpoints [${Array.from(protocolFamilies).join(', ')}]`);
+ verboseLog(` - ${server.name} (${server.config.language}) [${transport}] v${version} - ${paidEndpoints} x402 endpoints [${Array.from(protocolFamilies).join(', ')}]`);
});
- log(`📱 Clients found: ${clients.length}`);
+ verboseLog(`📱 Clients found: ${clients.length}`);
clients.forEach(client => {
const protocolFamilies = client.config.protocolFamilies || ['evm'];
const versions = client.config.x402Versions || [1];
const transport = client.config.transport || 'http';
const evmTransferMethods = client.config.evm?.transferMethods || ['eip3009'];
const evmInfo = protocolFamilies.includes('evm') ? ` evm:${evmTransferMethods.join(',')}` : '';
- log(` - ${client.name} (${client.config.language}) [${transport}] v[${versions.join(', ')}] [${protocolFamilies.join(', ')}]${evmInfo}`);
const extInfo = client.config.extensions ? ` {${client.config.extensions.join(', ')}}` : '';
- log(` - ${client.name} (${client.config.language}) [${transport}] v[${versions.join(', ')}] [${protocolFamilies.join(', ')}]${extInfo}`);
+ verboseLog(` - ${client.name} (${client.config.language}) [${transport}] v[${versions.join(', ')}] [${protocolFamilies.join(', ')}]${evmInfo}${extInfo}`);
});
- log(`🏛️ Facilitators found: ${facilitators.length}`);
+ verboseLog(`🏛️ Facilitators found: ${facilitators.length}`);
const regularFacilitators = facilitators.filter(f => !f.isExternal);
const externalFacilitators = facilitators.filter(f => f.isExternal);
@@ -351,17 +349,17 @@ export class TestDiscovery {
const versions = facilitator.config.x402Versions || [2];
const evmTransferMethods = facilitator.config.evm?.transferMethods || ['eip3009'];
const evmInfo = protocolFamilies.includes('evm') ? ` evm:${evmTransferMethods.join(',')}` : '';
- log(` - ${facilitator.name} (${facilitator.config.language}) v[${versions.join(', ')}] [${protocolFamilies.join(', ')}]${evmInfo}`);
+ verboseLog(` - ${facilitator.name} (${facilitator.config.language}) v[${versions.join(', ')}] [${protocolFamilies.join(', ')}]${evmInfo}`);
});
if (externalFacilitators.length > 0) {
- log(` External:`);
+ verboseLog(` External:`);
externalFacilitators.forEach(facilitator => {
const protocolFamilies = facilitator.config.protocolFamilies || ['evm'];
const versions = facilitator.config.x402Versions || [2];
const evmTransferMethods = facilitator.config.evm?.transferMethods || ['eip3009'];
const evmInfo = protocolFamilies.includes('evm') ? ` evm:${evmTransferMethods.join(',')}` : '';
- log(` - ${facilitator.name} (${facilitator.config.language}) v[${versions.join(', ')}] [${protocolFamilies.join(', ')}]${evmInfo}`);
+ verboseLog(` - ${facilitator.name} (${facilitator.config.language}) v[${versions.join(', ')}] [${protocolFamilies.join(', ')}]${evmInfo}`);
});
}
@@ -371,10 +369,10 @@ export class TestDiscovery {
return acc;
}, {} as Record);
- log(`📊 Test scenarios: ${scenarios.length}`);
+ verboseLog(`📊 Test scenarios: ${scenarios.length}`);
Object.entries(protocolBreakdown).forEach(([protocol, count]) => {
- log(` - ${protocol.toUpperCase()}: ${count} scenarios`);
+ verboseLog(` - ${protocol.toUpperCase()}: ${count} scenarios`);
});
- log('');
+ verboseLog('');
}
}
diff --git a/e2e/src/facilitators/facilitator-manager.ts b/e2e/src/facilitators/facilitator-manager.ts
index 9583c31228..e54be2da78 100644
--- a/e2e/src/facilitators/facilitator-manager.ts
+++ b/e2e/src/facilitators/facilitator-manager.ts
@@ -36,6 +36,7 @@ export class FacilitatorManager {
evmPrivateKey: process.env.FACILITATOR_EVM_PRIVATE_KEY,
svmPrivateKey: process.env.FACILITATOR_SVM_PRIVATE_KEY,
aptosPrivateKey: process.env.FACILITATOR_APTOS_PRIVATE_KEY,
+ stellarPrivateKey: process.env.FACILITATOR_STELLAR_PRIVATE_KEY,
networks,
});
diff --git a/e2e/src/facilitators/generic-facilitator.ts b/e2e/src/facilitators/generic-facilitator.ts
index 03f510bbae..672f758a8a 100644
--- a/e2e/src/facilitators/generic-facilitator.ts
+++ b/e2e/src/facilitators/generic-facilitator.ts
@@ -54,6 +54,7 @@ export interface FacilitatorConfig {
evmPrivateKey?: string;
svmPrivateKey?: string;
aptosPrivateKey?: string;
+ stellarPrivateKey?: string;
networks: NetworkSet;
}
@@ -114,6 +115,7 @@ export class GenericFacilitatorProxy extends BaseProxy implements FacilitatorPro
EVM_PRIVATE_KEY: config.evmPrivateKey || '',
SVM_PRIVATE_KEY: config.svmPrivateKey || '',
APTOS_PRIVATE_KEY: config.aptosPrivateKey || '',
+ STELLAR_PRIVATE_KEY: config.stellarPrivateKey || '',
// Network configs from NetworkSet
EVM_NETWORK: config.networks.evm.caip2,
@@ -122,6 +124,8 @@ export class GenericFacilitatorProxy extends BaseProxy implements FacilitatorPro
SVM_RPC_URL: config.networks.svm.rpcUrl,
APTOS_NETWORK: config.networks.aptos.caip2,
APTOS_RPC_URL: config.networks.aptos.rpcUrl,
+ STELLAR_NETWORK: config.networks.stellar.caip2,
+ STELLAR_RPC_URL: config.networks.stellar.rpcUrl,
};
// Pass through any additional environment variables required by the facilitator
diff --git a/e2e/src/networks/networks.ts b/e2e/src/networks/networks.ts
index c4d64d65c3..5d8441a1b6 100644
--- a/e2e/src/networks/networks.ts
+++ b/e2e/src/networks/networks.ts
@@ -6,18 +6,20 @@
*/
export type NetworkMode = 'testnet' | 'mainnet';
-export type ProtocolFamily = 'evm' | 'svm' | 'aptos';
+export type ProtocolFamily = 'evm' | 'svm' | 'aptos' | 'stellar';
export type NetworkConfig = {
name: string;
caip2: `${string}:${string}`;
rpcUrl: string;
+ permit2Asset?: string;
};
export type NetworkSet = {
evm: NetworkConfig;
svm: NetworkConfig;
aptos: NetworkConfig;
+ stellar: NetworkConfig;
};
/**
@@ -29,6 +31,7 @@ const NETWORK_SETS: Record = {
name: 'Base Sepolia',
caip2: 'eip155:84532',
rpcUrl: process.env.BASE_SEPOLIA_RPC_URL || 'https://sepolia.base.org',
+ permit2Asset: '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
},
svm: {
name: 'Solana Devnet',
@@ -40,12 +43,18 @@ const NETWORK_SETS: Record = {
caip2: 'aptos:2',
rpcUrl: process.env.APTOS_TESTNET_RPC_URL || 'https://fullnode.testnet.aptoslabs.com/v1',
},
+ stellar: {
+ name: 'Stellar Testnet',
+ caip2: 'stellar:testnet',
+ rpcUrl: process.env.STELLAR_TESTNET_RPC_URL || 'https://soroban-testnet.stellar.org',
+ },
},
mainnet: {
evm: {
name: 'Base',
caip2: 'eip155:8453',
rpcUrl: process.env.BASE_RPC_URL || 'https://mainnet.base.org',
+ permit2Asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
},
svm: {
name: 'Solana',
@@ -57,6 +66,11 @@ const NETWORK_SETS: Record = {
caip2: 'aptos:1',
rpcUrl: process.env.APTOS_RPC_URL || 'https://fullnode.mainnet.aptoslabs.com/v1',
},
+ stellar: {
+ name: 'Stellar Pubnet',
+ caip2: 'stellar:pubnet',
+ rpcUrl: process.env.STELLAR_RPC_URL || 'https://mainnet.sorobanrpc.com',
+ },
},
};
@@ -74,7 +88,7 @@ export function getNetworkSet(mode: NetworkMode): NetworkSet {
* Get network config for a protocol family in a given mode
*
* @param mode - 'testnet' or 'mainnet'
- * @param protocolFamily - 'evm', 'svm', or 'aptos'
+ * @param protocolFamily - 'evm', 'svm', 'aptos', or 'stellar'
* @returns NetworkConfig for the specified protocol
*/
export function getNetworkForProtocol(
@@ -92,5 +106,6 @@ export function getNetworkForProtocol(
*/
export function getNetworkModeDescription(mode: NetworkMode): string {
const set = NETWORK_SETS[mode];
- return `${set.evm.name} + ${set.svm.name} + ${set.aptos.name}`;
+ const networks = [set.evm.name, set.svm.name, set.aptos.name, set.stellar.name];
+ return networks.join(' + ');
}
diff --git a/e2e/src/servers/generic-server.ts b/e2e/src/servers/generic-server.ts
index 83783aa513..f769dbed0d 100644
--- a/e2e/src/servers/generic-server.ts
+++ b/e2e/src/servers/generic-server.ts
@@ -92,6 +92,7 @@ export class GenericServerProxy extends BaseProxy implements ServerProxy {
EVM_NETWORK: evmNetwork,
EVM_RPC_URL: config.networks.evm.rpcUrl,
EVM_PAYEE_ADDRESS: config.evmPayTo,
+ EVM_PERMIT2_ASSET: config.networks.evm.permit2Asset || '',
// SVM network config
SVM_NETWORK: svmNetwork,
@@ -103,8 +104,14 @@ export class GenericServerProxy extends BaseProxy implements ServerProxy {
APTOS_RPC_URL: config.networks.aptos.rpcUrl,
APTOS_PAYEE_ADDRESS: config.aptosPayTo,
+ // Stellar network config
+ STELLAR_NETWORK: config.networks.stellar.caip2,
+ STELLAR_RPC_URL: config.networks.stellar.rpcUrl,
+ STELLAR_PAYEE_ADDRESS: config.stellarPayTo,
+
// Facilitator
FACILITATOR_URL: config.facilitatorUrl || '',
+ MOCK_FACILITATOR_URL: config.mockFacilitatorUrl || '',
}
};
diff --git a/e2e/src/types.ts b/e2e/src/types.ts
index 991e6050cb..841f200c39 100644
--- a/e2e/src/types.ts
+++ b/e2e/src/types.ts
@@ -1,8 +1,8 @@
import type { NetworkSet } from './networks/networks';
-export type ProtocolFamily = 'evm' | 'svm' | 'aptos';
+export type ProtocolFamily = 'evm' | 'svm' | 'aptos' | 'stellar';
export type Transport = 'http' | 'mcp';
-export type TransferMethod = 'eip3009' | 'permit2';
+export type TransferMethod = 'eip3009' | 'permit2' | 'upto';
export interface ClientResult {
success: boolean;
@@ -16,8 +16,11 @@ export interface ClientConfig {
evmPrivateKey: string;
svmPrivateKey: string;
aptosPrivateKey: string;
+ stellarPrivateKey: string;
serverUrl: string;
endpointPath: string;
+ evmNetwork: string;
+ evmRpcUrl: string;
}
export interface ServerConfig {
@@ -25,8 +28,10 @@ export interface ServerConfig {
evmPayTo: string;
svmPayTo: string;
aptosPayTo: string;
+ stellarPayTo: string;
networks: NetworkSet;
facilitatorUrl?: string;
+ mockFacilitatorUrl?: string;
}
export interface ServerProxy {
@@ -48,6 +53,11 @@ export interface TestEndpoint {
requiresPayment?: boolean;
protocolFamily?: ProtocolFamily;
transferMethod?: TransferMethod;
+ extensions?: string[];
+ /** True for Permit2 standard/direct settle - requires pre-approval (approve before test, not revoke) */
+ permit2Direct?: boolean;
+ /** True for endpoints that require Permit2 revocation + fund/drain state setup before the first test (coldstart). */
+ coldstart?: boolean;
health?: boolean;
close?: boolean;
}
diff --git a/e2e/test.ts b/e2e/test.ts
index 8d0fcb17e1..4349597506 100644
--- a/e2e/test.ts
+++ b/e2e/test.ts
@@ -1,6 +1,10 @@
import { config } from 'dotenv';
-import { spawn, execSync } from 'child_process';
+import { spawn, execSync, ChildProcess } from 'child_process';
import { writeFileSync } from 'fs';
+import { join } from 'path';
+import { createWalletClient, createPublicClient, http, parseEther, formatEther } from 'viem';
+import { privateKeyToAccount } from 'viem/accounts';
+import { base, baseSepolia } from 'viem/chains';
import { TestDiscovery } from './src/discovery';
import { ClientConfig, ScenarioResult, ServerConfig, TestScenario } from './src/types';
import { config as loggerConfig, log, verboseLog, errorLog, close as closeLogger, createComboLogger } from './src/logger';
@@ -15,14 +19,24 @@ import { Semaphore, FacilitatorLock } from './src/concurrency';
import { FacilitatorManager } from './src/facilitators/facilitator-manager';
import { waitForHealth } from './src/health';
+// Base Sepolia token addresses used by permit2 E2E tests
+const USDC_BASE_SEPOLIA = '0x036CbD53842c5426634e7929541eC2318f3dCF7e';
+const MOCK_ERC20_BASE_SEPOLIA = '0xeED520980fC7C7B4eB379B96d61CEdea2423005a';
+
/**
- * Run Permit2 setup script to ensure the client wallet has approved the Permit2 contract
+ * Approve Permit2 so that the standard/direct settle path can be exercised.
+ * Grants unlimited Permit2 allowance for the given token (or USDC by default).
*/
-async function setupPermit2Approval(): Promise {
+async function approvePermit2Approval(tokenAddress?: string): Promise {
return new Promise((resolve) => {
- log('\n🔑 Setting up Permit2 approval for EVM client wallet...');
+ const label = tokenAddress ? `token ${tokenAddress}` : 'USDC (default)';
+ verboseLog(` 🔓 Approving Permit2 for ${label}...`);
- const child = spawn('pnpm', ['permit2:approve'], {
+ const args = ['scripts/permit2-approval.ts', 'approve'];
+ if (tokenAddress) {
+ args.push(tokenAddress);
+ }
+ const child = spawn('tsx', args, {
cwd: process.cwd(),
stdio: 'pipe',
shell: true,
@@ -41,10 +55,10 @@ async function setupPermit2Approval(): Promise {
child.on('close', (code) => {
if (code === 0) {
- log(' ✅ Permit2 approval setup complete');
+ verboseLog(' ✅ Permit2 approval granted');
resolve(true);
} else {
- errorLog(` ❌ Permit2 setup failed (exit code ${code})`);
+ errorLog(` ❌ Permit2 approve failed (exit code ${code})`);
if (stderr) {
errorLog(` Error: ${stderr}`);
}
@@ -53,21 +67,27 @@ async function setupPermit2Approval(): Promise {
});
child.on('error', (error) => {
- errorLog(` ❌ Failed to run Permit2 setup: ${error.message}`);
+ errorLog(` ❌ Failed to run Permit2 approve: ${error.message}`);
resolve(false);
});
});
}
/**
- * Revoke Permit2 approval so that EIP-2612 gas sponsoring extension is exercised.
- * Sets the Permit2 allowance to 0, forcing the client to use the EIP-2612 permit path.
+ * Revoke Permit2 approval so that gas sponsoring extensions are exercised.
+ * Sets the Permit2 allowance to 0 for the given token (or USDC by default),
+ * forcing the client into the EIP-2612 or ERC-20 approval extension path.
*/
-async function revokePermit2Approval(): Promise {
+async function revokePermit2Approval(tokenAddress?: string): Promise {
return new Promise((resolve) => {
- verboseLog(' 🔓 Revoking Permit2 approval for EIP-2612 test...');
+ const label = tokenAddress ? `token ${tokenAddress}` : 'USDC (default)';
+ verboseLog(` 🔓 Revoking Permit2 approval for ${label}...`);
- const child = spawn('pnpm', ['permit2:revoke'], {
+ const args = ['scripts/permit2-approval.ts', 'revoke'];
+ if (tokenAddress) {
+ args.push(tokenAddress);
+ }
+ const child = spawn('tsx', args, {
cwd: process.cwd(),
stdio: 'pipe',
shell: true,
@@ -104,6 +124,133 @@ async function revokePermit2Approval(): Promise {
});
}
+/**
+ * Shared EVM clients for the ETH sandwich helpers.
+ * Lazily initialised on first use so that missing env vars don't blow up
+ * non-EVM test runs.
+ */
+function getEvmClients() {
+ const evmNetwork = process.env.EVM_NETWORK || 'eip155:84532';
+ const evmRpcUrl = process.env.EVM_RPC_URL;
+ const evmChain = evmNetwork === 'eip155:8453' ? base : baseSepolia;
+
+ const facilitatorKey = process.env.FACILITATOR_EVM_PRIVATE_KEY;
+ const clientKey = process.env.CLIENT_EVM_PRIVATE_KEY;
+ if (!facilitatorKey || !clientKey) {
+ throw new Error('FACILITATOR_EVM_PRIVATE_KEY and CLIENT_EVM_PRIVATE_KEY must be set');
+ }
+
+ const facilitatorAccount = privateKeyToAccount(facilitatorKey as `0x${string}`);
+ const clientAccount = privateKeyToAccount(clientKey as `0x${string}`);
+
+ const publicClient = createPublicClient({
+ chain: evmChain,
+ transport: http(evmRpcUrl),
+ });
+ const facilitatorWallet = createWalletClient({
+ account: facilitatorAccount,
+ chain: evmChain,
+ transport: http(evmRpcUrl),
+ });
+ const clientWallet = createWalletClient({
+ account: clientAccount,
+ chain: evmChain,
+ transport: http(evmRpcUrl),
+ });
+
+ return { publicClient, facilitatorWallet, clientWallet, facilitatorAccount, clientAccount };
+}
+
+const REVOKE_FUND_AMOUNT = parseEther('0.001');
+
+/**
+ * Send a small amount of ETH from the facilitator wallet to the client wallet
+ * so the client can pay gas for Permit2 revocation transactions.
+ */
+async function fundClientForRevoke(): Promise {
+ const { publicClient, facilitatorWallet, facilitatorAccount, clientAccount } = getEvmClients();
+
+ const clientBalance = await publicClient.getBalance({ address: clientAccount.address });
+ if (clientBalance >= REVOKE_FUND_AMOUNT) {
+ verboseLog(` ℹ️ Client already has ${formatEther(clientBalance)} ETH, skipping fund`);
+ return true;
+ }
+
+ const facilitatorBalance = await publicClient.getBalance({ address: facilitatorAccount.address });
+ if (facilitatorBalance < REVOKE_FUND_AMOUNT) {
+ errorLog(` ❌ Facilitator wallet ${facilitatorAccount.address} has insufficient ETH (${formatEther(facilitatorBalance)}) to fund client for revoke.`);
+ errorLog(` Please fund the facilitator wallet with testnet ETH (need at least ${formatEther(REVOKE_FUND_AMOUNT)} ETH).`);
+ return false;
+ }
+
+ verboseLog(` 💸 Funding client ${clientAccount.address} with ${formatEther(REVOKE_FUND_AMOUNT)} ETH for revoke...`);
+ // Retry on nonce errors: load-balanced RPCs can return stale pending nonces,
+ // especially when the facilitator SERVICE process (same private key) is settling
+ // payments concurrently. A fresh nonce fetch + small delay usually resolves it.
+ let lastErr: Error | null = null;
+ for (let attempt = 0; attempt < 3; attempt++) {
+ if (attempt > 0) await new Promise(r => setTimeout(r, 500));
+ try {
+ const nonce = await publicClient.getTransactionCount({
+ address: facilitatorAccount.address,
+ blockTag: 'pending',
+ });
+ const hash = await facilitatorWallet.sendTransaction({
+ to: clientAccount.address,
+ value: REVOKE_FUND_AMOUNT,
+ nonce,
+ });
+ verboseLog(` ✅ Funded client (tx: ${hash})`);
+ return true;
+ } catch (err) {
+ lastErr = err instanceof Error ? err : new Error(String(err));
+ const isNonceError = lastErr.message.toLowerCase().includes('nonce');
+ if (!isNonceError) break;
+ }
+ }
+ const errLines = lastErr!.message.split('\n');
+ errorLog(` ❌ Failed to fund client for revoke: ${errLines[0].trim()}`);
+ if (errLines.length > 1) verboseLog(errLines.slice(1).join('\n'));
+ return false;
+}
+
+/**
+ * Drain all ETH from the client wallet back to the facilitator wallet,
+ * leaving the client with ~0 ETH so the gas sponsoring funding step is
+ * exercised during the test.
+ */
+async function drainClientETH(): Promise {
+ try {
+ const { publicClient, clientWallet, facilitatorAccount, clientAccount } = getEvmClients();
+
+ // Use pending balance so we see any in-flight fund transaction that hasn't confirmed yet.
+ const balance = await publicClient.getBalance({ address: clientAccount.address, blockTag: 'pending' });
+
+ // Reserve enough for gas. On L2s getGasPrice() returns a tiny value but
+ // viem's sendTransaction uses a higher maxFeePerGas with safety margin.
+ // Use a generous fixed buffer to avoid "insufficient funds" from the
+ // estimateGas pre-check.
+ const GAS_RESERVE = parseEther('0.0001');
+ const sendAmount = balance - GAS_RESERVE;
+
+ if (sendAmount <= 0n) {
+ verboseLog(` ℹ️ Client balance (${formatEther(balance)} ETH) too small to drain, leaving as dust`);
+ return true;
+ }
+
+ verboseLog(` 💸 Draining ${formatEther(sendAmount)} ETH from client back to facilitator...`);
+ const hash = await clientWallet.sendTransaction({
+ to: facilitatorAccount.address,
+ value: sendAmount,
+ });
+ verboseLog(` ✅ Drained client ETH (tx: ${hash})`);
+ return true;
+ } catch (err) {
+ errorLog(` ❌ Failed to drain client ETH: ${err instanceof Error ? err.message : err}`);
+ return false;
+ }
+}
+
// Load environment variables
config();
@@ -237,13 +384,15 @@ async function runTest() {
const serverEvmAddress = process.env.SERVER_EVM_ADDRESS;
const serverSvmAddress = process.env.SERVER_SVM_ADDRESS;
const serverAptosAddress = process.env.SERVER_APTOS_ADDRESS;
+ const serverStellarAddress = process.env.SERVER_STELLAR_ADDRESS;
const clientEvmPrivateKey = process.env.CLIENT_EVM_PRIVATE_KEY;
const clientSvmPrivateKey = process.env.CLIENT_SVM_PRIVATE_KEY;
const clientAptosPrivateKey = process.env.CLIENT_APTOS_PRIVATE_KEY;
+ const clientStellarPrivateKey = process.env.CLIENT_STELLAR_PRIVATE_KEY;
const facilitatorEvmPrivateKey = process.env.FACILITATOR_EVM_PRIVATE_KEY;
const facilitatorSvmPrivateKey = process.env.FACILITATOR_SVM_PRIVATE_KEY;
const facilitatorAptosPrivateKey = process.env.FACILITATOR_APTOS_PRIVATE_KEY;
-
+ const facilitatorStellarPrivateKey = process.env.FACILITATOR_STELLAR_PRIVATE_KEY;
if (!serverEvmAddress || !serverSvmAddress || !clientEvmPrivateKey || !clientSvmPrivateKey || !facilitatorEvmPrivateKey || !facilitatorSvmPrivateKey) {
errorLog('❌ Missing required environment variables:');
errorLog(' SERVER_EVM_ADDRESS, SERVER_SVM_ADDRESS, CLIENT_EVM_PRIVATE_KEY, CLIENT_SVM_PRIVATE_KEY, FACILITATOR_EVM_PRIVATE_KEY, and FACILITATOR_SVM_PRIVATE_KEY must be set');
@@ -320,6 +469,7 @@ async function runTest() {
log(` EVM: ${networks.evm.name} (${networks.evm.caip2})`);
log(` SVM: ${networks.svm.name} (${networks.svm.caip2})`);
log(` APTOS: ${networks.aptos.name} (${networks.aptos.caip2})`);
+ log(` STELLAR: ${networks.stellar.name} (${networks.stellar.caip2})`);
if (networkMode === 'mainnet') {
log('\n⚠️ WARNING: Running on MAINNET - real funds will be used!');
@@ -353,31 +503,40 @@ async function runTest() {
}
log('');
- // Auto-detect Permit2 scenarios
+ // Branch coverage assertions for EVM scenarios
+ const evmScenarios = filteredScenarios.filter(s => s.protocolFamily === 'evm');
+ if (evmScenarios.length > 0) {
+ const hasEip3009 = evmScenarios.some(s => (s.endpoint.transferMethod || 'eip3009') === 'eip3009');
+ const hasPermit2 = evmScenarios.some(s => s.endpoint.transferMethod === 'permit2');
+ const hasPermit2Direct = evmScenarios.some(s => s.endpoint.transferMethod === 'permit2' && s.endpoint.permit2Direct === true);
+ const hasPermit2Eip2612 = evmScenarios.some(s => s.endpoint.transferMethod === 'permit2' && !s.endpoint.extensions?.includes('erc20ApprovalGasSponsoring') && !s.endpoint.permit2Direct);
+ const hasPermit2Erc20 = evmScenarios.some(s => s.endpoint.transferMethod === 'permit2' && s.endpoint.extensions?.includes('erc20ApprovalGasSponsoring'));
+
+ const hasUpto = evmScenarios.some(s => s.endpoint.transferMethod === 'upto');
+ const hasUptoDirect = evmScenarios.some(s => s.endpoint.transferMethod === 'upto' && s.endpoint.permit2Direct === true);
+ const hasUptoEip2612 = evmScenarios.some(s => s.endpoint.transferMethod === 'upto' && !s.endpoint.extensions?.includes('erc20ApprovalGasSponsoring') && !s.endpoint.permit2Direct);
+ const hasUptoErc20 = evmScenarios.some(s => s.endpoint.transferMethod === 'upto' && s.endpoint.extensions?.includes('erc20ApprovalGasSponsoring'));
+
+ log('🔍 EVM Branch Coverage Check:');
+ log(` EIP-3009 route: ${hasEip3009 ? '✅' : '❌ MISSING'}`);
+ log(` Permit2 route: ${hasPermit2 ? '✅' : '❌ MISSING'}`);
+ log(` Permit2+direct settle: ${hasPermit2Direct ? '✅' : '⚠️ not found'}`);
+ log(` Permit2+EIP2612 route: ${hasPermit2Eip2612 ? '✅' : '⚠️ not found (may be covered by permit2 route if eip2612 extension enabled)'}`);
+ log(` Permit2+ERC20 route: ${hasPermit2Erc20 ? '✅' : '⚠️ not found'}`);
+ log(` Upto route: ${hasUpto ? '✅' : '⚠️ not found'}`);
+ log(` Upto+direct settle: ${hasUptoDirect ? '✅' : '⚠️ not found'}`);
+ log(` Upto+EIP2612 route: ${hasUptoEip2612 ? '✅' : '⚠️ not found'}`);
+ log(` Upto+ERC20 route: ${hasUptoErc20 ? '✅' : '⚠️ not found'}`);
+ log('');
+ }
+
+ // Auto-detect Permit2 scenarios (upto uses Permit2 under the hood)
const hasPermit2Scenarios = filteredScenarios.some(
- (s) => s.endpoint.transferMethod === 'permit2'
+ (s) => s.endpoint.transferMethod === 'permit2' || s.endpoint.transferMethod === 'upto'
);
- // Check if eip2612GasSponsoring extension should be tested
- const hasEip2612Extension = selectedExtensions?.includes('eip2612GasSponsoring') ?? false;
-
if (hasPermit2Scenarios) {
- if (hasEip2612Extension) {
- log('🔐 Permit2 scenarios detected with eip2612GasSponsoring extension');
- } else {
- // Standard permit2 flow: ensure approval exists
- log('🔐 Permit2 scenarios detected - checking approval...');
- const setupSuccess = await setupPermit2Approval();
- if (!setupSuccess) {
- errorLog(
- '\n❌ Failed to setup Permit2 approval. Cannot continue with Permit2 tests.'
- );
- errorLog(
- '💡 Make sure CLIENT_EVM_PRIVATE_KEY is set and the wallet has USDC.'
- );
- process.exit(1);
- }
- }
+ log('🔐 Permit2 scenarios detected — revoke before gas-sponsored tests, approve before permit2-direct tests');
}
// Collect unique facilitators and servers
@@ -396,7 +555,21 @@ async function runTest() {
const missingEnvVars: { facilitatorName: string; missingVars: string[] }[] = [];
// Environment variables managed by the test framework (don't require user to set)
- const systemManagedVars = new Set(['PORT', 'EVM_PRIVATE_KEY', 'SVM_PRIVATE_KEY', 'APTOS_PRIVATE_KEY', 'EVM_NETWORK', 'SVM_NETWORK', 'APTOS_NETWORK', 'EVM_RPC_URL', 'SVM_RPC_URL', 'APTOS_RPC_URL']);
+ const systemManagedVars = new Set([
+ 'PORT',
+ 'EVM_PRIVATE_KEY',
+ 'SVM_PRIVATE_KEY',
+ 'APTOS_PRIVATE_KEY',
+ 'STELLAR_PRIVATE_KEY',
+ 'EVM_NETWORK',
+ 'SVM_NETWORK',
+ 'APTOS_NETWORK',
+ 'STELLAR_NETWORK',
+ 'EVM_RPC_URL',
+ 'SVM_RPC_URL',
+ 'APTOS_RPC_URL',
+ 'STELLAR_RPC_URL',
+ ]);
for (const [facilitatorName, facilitator] of uniqueFacilitators) {
const requiredVars = facilitator.config.environment?.required || [];
@@ -523,6 +696,51 @@ async function runTest() {
log(` ✅ Facilitator ${facilitatorName} ready at ${url}`);
}
+ // Start mock facilitator (claims to support everything, used as fallback so
+ // servers with routes unsupported by the real facilitator can still start)
+ const mockFacilitatorPort = currentPort++;
+ log(`\n🎭 Starting mock facilitator on port ${mockFacilitatorPort}...`);
+ const mockFacilitatorProcess: ChildProcess = spawn(
+ 'npx', ['tsx', 'index.ts'],
+ {
+ cwd: join(process.cwd(), 'mock-facilitator'),
+ env: {
+ ...process.env,
+ PORT: mockFacilitatorPort.toString(),
+ EVM_NETWORK: networks.evm.caip2,
+ SVM_NETWORK: networks.svm.caip2,
+ APTOS_NETWORK: networks.aptos.caip2,
+ STELLAR_NETWORK: networks.stellar.caip2,
+ },
+ stdio: 'pipe',
+ },
+ );
+ mockFacilitatorProcess.stderr?.on('data', (data: Buffer) => {
+ verboseLog(`[mock-facilitator] stderr: ${data.toString().trim()}`);
+ });
+ mockFacilitatorProcess.stdout?.on('data', (data: Buffer) => {
+ verboseLog(`[mock-facilitator] stdout: ${data.toString().trim()}`);
+ });
+
+ const mockFacilitatorUrl = `http://localhost:${mockFacilitatorPort}`;
+ const mockHealthy = await waitForHealth(
+ async () => {
+ try {
+ const res = await fetch(`${mockFacilitatorUrl}/health`);
+ return { success: res.ok };
+ } catch {
+ return { success: false };
+ }
+ },
+ { label: 'Mock facilitator' },
+ );
+ if (!mockHealthy) {
+ log('❌ Failed to start mock facilitator');
+ mockFacilitatorProcess.kill();
+ process.exit(1);
+ }
+ log(` ✅ Mock facilitator ready at ${mockFacilitatorUrl}`);
+
log('\n✅ All facilitators are ready! Servers will be started/restarted as needed per test scenario.\n');
log(`🔧 Server/Facilitator combinations: ${serverFacilitatorCombos.length}`);
@@ -551,8 +769,11 @@ async function runTest() {
evmPrivateKey: clientEvmPrivateKey!,
svmPrivateKey: clientSvmPrivateKey!,
aptosPrivateKey: clientAptosPrivateKey || '',
+ stellarPrivateKey: clientStellarPrivateKey || '',
serverUrl: `http://localhost:${port}`,
endpointPath: scenario.endpoint.path,
+ evmNetwork: networks.evm.caip2,
+ evmRpcUrl: networks.evm.rpcUrl,
};
try {
@@ -630,14 +851,17 @@ async function runTest() {
const facilitatorConfig = facilitatorName ? uniqueFacilitators.get(facilitatorName)?.config : undefined;
const facilitatorSupportsAptos = facilitatorConfig?.protocolFamilies?.includes('aptos') ?? false;
+ const facilitatorSupportsStellar = facilitatorConfig?.protocolFamilies?.includes('stellar') ?? false;
const serverConfig: ServerConfig = {
port,
evmPayTo: serverEvmAddress!,
svmPayTo: serverSvmAddress!,
aptosPayTo: facilitatorSupportsAptos ? (serverAptosAddress || '') : '',
+ stellarPayTo: facilitatorSupportsStellar ? (serverStellarAddress || '') : '',
networks,
facilitatorUrl,
+ mockFacilitatorUrl,
};
const started = await startServer(serverProxy, serverConfig);
@@ -657,20 +881,43 @@ async function runTest() {
cLog.log(` ✅ Server ${serverName} ready`);
const results: DetailedTestResult[] = [];
+ // Track which endpoint paths have already been "cold started" in this combo.
+ // The first test for each path runs the full state-setup (fund/revoke/drain);
+ // subsequent tests for the same path skip the setup and run warm.
+ const coldStartedEndpoints = new Set();
try {
for (const scenario of scenarios) {
const tn = nextTestNumber();
const isEvm = scenario.protocolFamily === 'evm';
- if (hasEip2612Extension && scenario.endpoint.transferMethod === 'permit2') {
- await revokePermit2Approval();
+ if (scenario.endpoint.permit2Direct) {
+ await approvePermit2Approval(USDC_BASE_SEPOLIA);
+ } else if (scenario.endpoint.coldstart) {
+ const endpointKey = scenario.endpoint.path;
+ if (!coldStartedEndpoints.has(endpointKey)) {
+ coldStartedEndpoints.add(endpointKey);
+ const token =
+ scenario.endpoint.extensions?.includes('erc20ApprovalGasSponsoring')
+ ? MOCK_ERC20_BASE_SEPOLIA
+ : USDC_BASE_SEPOLIA;
+ await fundClientForRevoke();
+ // Give fund tx 1s to propagate before submitting revoke (from client wallet)
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ await revokePermit2Approval(token);
+ // Give revoke tx 1s to propagate before drain reads pending balance
+ await new Promise(resolve => setTimeout(resolve, 1000));
+ await drainClientETH();
+ // Wait for RPC nonce propagation across load-balanced nodes before the
+ // test client (which may use a separate RPC connection) queries the nonce.
+ await new Promise(resolve => setTimeout(resolve, 1500));
+ }
}
if (isEvm && facilitatorName && evmLock) {
const releaseLock = await evmLock.acquire(facilitatorName);
try {
results.push(await runSingleTest(scenario, port, tn, cLog));
- await new Promise(resolve => setTimeout(resolve, 2000));
+ await new Promise(resolve => setTimeout(resolve, 1000));
} finally {
releaseLock();
}
@@ -679,230 +926,232 @@ async function runTest() {
}
}
} finally {
- cLog.verboseLog(` 🛑 Stopping ${serverName} (finished combo)`);
- await serverProxy.stop();
-}
+ cLog.verboseLog(` 🛑 Stopping ${serverName} (finished combo)`);
+ await serverProxy.stop();
+ }
-return results;
+ return results;
}
-// ── Unified execution: concurrency=1 for sequential, N for parallel ──
-const effectiveConcurrency = parsedArgs.parallel ? parsedArgs.concurrency : 1;
-const evmLock = parsedArgs.parallel ? new FacilitatorLock() : null;
-const semaphore = new Semaphore(effectiveConcurrency);
+ // ── Unified execution: concurrency=1 for sequential, N for parallel ──
+ const effectiveConcurrency = parsedArgs.parallel ? parsedArgs.concurrency : 1;
+ const evmLock = parsedArgs.parallel ? new FacilitatorLock() : null;
+ const semaphore = new Semaphore(effectiveConcurrency);
-let globalTestNumber = 0;
-const nextTestNumber = () => ++globalTestNumber;
+ let globalTestNumber = 0;
+ const nextTestNumber = () => ++globalTestNumber;
-const comboPromises = serverFacilitatorCombos.map(async (combo) => {
- const release = await semaphore.acquire();
- try {
- return await executeCombo(combo, evmLock, nextTestNumber);
- } finally {
- release();
- }
-});
+ const comboPromises = serverFacilitatorCombos.map(async (combo) => {
+ const release = await semaphore.acquire();
+ try {
+ return await executeCombo(combo, evmLock, nextTestNumber);
+ } finally {
+ release();
+ }
+ });
-testResults = (await Promise.all(comboPromises)).flat();
+ testResults = (await Promise.all(comboPromises)).flat();
-// Run discovery validation before cleanup (while facilitators are still running)
-const facilitatorsWithConfig = Array.from(uniqueFacilitators.values()).map((f: any) => ({
- proxy: facilitatorManagers.get(f.name)!.getProxy(),
- config: f.config,
-}));
+ // Run discovery validation before cleanup (while facilitators are still running)
+ const facilitatorsWithConfig = Array.from(uniqueFacilitators.values()).map((f: any) => ({
+ proxy: facilitatorManagers.get(f.name)!.getProxy(),
+ config: f.config,
+ }));
-const serversArray = Array.from(uniqueServers.values());
+ const serversArray = Array.from(uniqueServers.values());
-// Build a serverName→port map for discovery validation (first combo per server).
-const discoveryServerPorts = new Map();
-for (const combo of serverFacilitatorCombos) {
- if (!discoveryServerPorts.has(combo.serverName)) {
- discoveryServerPorts.set(combo.serverName, combo.port);
+ // Build a serverName→port map for discovery validation (first combo per server).
+ const discoveryServerPorts = new Map();
+ for (const combo of serverFacilitatorCombos) {
+ if (!discoveryServerPorts.has(combo.serverName)) {
+ discoveryServerPorts.set(combo.serverName, combo.port);
+ }
}
-}
-// Run discovery validation if bazaar extension is enabled
-const showBazaarOutput = shouldShowExtensionOutput('bazaar', selectedExtensions);
-if (showBazaarOutput && shouldRunDiscoveryValidation(facilitatorsWithConfig, serversArray)) {
- log('\n🔍 Running Bazaar Discovery Validation...\n');
- await handleDiscoveryValidation(
- facilitatorsWithConfig,
- serversArray,
- discoveryServerPorts,
- facilitatorServerMap
- );
-}
+ // Run discovery validation if bazaar extension is enabled
+ const showBazaarOutput = shouldShowExtensionOutput('bazaar', selectedExtensions);
+ if (showBazaarOutput && shouldRunDiscoveryValidation(facilitatorsWithConfig, serversArray)) {
+ log('\n🔍 Running Bazaar Discovery Validation...\n');
+ await handleDiscoveryValidation(
+ facilitatorsWithConfig,
+ serversArray,
+ discoveryServerPorts,
+ facilitatorServerMap
+ );
+ }
-// Clean up facilitators (servers already stopped in test loop for both modes)
-log('\n🧹 Cleaning up...');
+ // Clean up facilitators (servers already stopped in test loop for both modes)
+ log('\n🧹 Cleaning up...');
-// Stop all facilitators
-const facilitatorStopPromises: Promise[] = [];
-for (const [facilitatorName, manager] of facilitatorManagers) {
- log(` 🛑 Stopping facilitator: ${facilitatorName}`);
- facilitatorStopPromises.push(manager.stop());
-}
-await Promise.all(facilitatorStopPromises);
-
-// Calculate totals
-const passed = testResults.filter(r => r.passed).length;
-const failed = testResults.filter(r => !r.passed).length;
-
-// Summary
-log('');
-log('📊 Test Summary');
-log('==============');
-log(`🌐 Network: ${networkMode} (${getNetworkModeDescription(networkMode)})`);
-log(`✅ Passed: ${passed}`);
-log(`❌ Failed: ${failed}`);
-log(`📈 Total: ${passed + failed}`);
-log('');
-
-// Detailed results table
-log('📋 Detailed Test Results');
-log('========================');
-log('');
-
-// Group by status
-const passedTests = testResults.filter(r => r.passed);
-const failedTests = testResults.filter(r => !r.passed);
-
-if (passedTests.length > 0) {
- log('✅ PASSED TESTS:');
+ // Stop all facilitators
+ const facilitatorStopPromises: Promise[] = [];
+ for (const [facilitatorName, manager] of facilitatorManagers) {
+ log(` 🛑 Stopping facilitator: ${facilitatorName}`);
+ facilitatorStopPromises.push(manager.stop());
+ }
+ log(' 🛑 Stopping mock facilitator');
+ mockFacilitatorProcess.kill();
+ await Promise.all(facilitatorStopPromises);
+
+ // Calculate totals
+ const passed = testResults.filter(r => r.passed).length;
+ const failed = testResults.filter(r => !r.passed).length;
+
+ // Summary
log('');
- passedTests.forEach(test => {
- log(` #${test.testNumber.toString().padStart(2, ' ')}: ${test.client} → ${test.server} → ${test.endpoint}`);
- log(` Facilitator: ${test.facilitator}`);
- if (test.network) {
- log(` Network: ${test.network}`);
- }
- if (test.transaction) {
- log(` Tx: ${test.transaction}`);
- }
- });
+ log('📊 Test Summary');
+ log('==============');
+ log(`🌐 Network: ${networkMode} (${getNetworkModeDescription(networkMode)})`);
+ log(`✅ Passed: ${passed}`);
+ log(`❌ Failed: ${failed}`);
+ log(`📈 Total: ${passed + failed}`);
log('');
-}
-if (failedTests.length > 0) {
- log('❌ FAILED TESTS:');
+ // Detailed results table
+ log('📋 Detailed Test Results');
+ log('========================');
log('');
- failedTests.forEach(test => {
- log(` #${test.testNumber.toString().padStart(2, ' ')}: ${test.client} → ${test.server} → ${test.endpoint}`);
- log(` Facilitator: ${test.facilitator}`);
- if (test.network) {
- log(` Network: ${test.network}`);
- }
- log(` Error: ${test.error || 'Unknown error'}`);
+
+ // Group by status
+ const passedTests = testResults.filter(r => r.passed);
+ const failedTests = testResults.filter(r => !r.passed);
+
+ if (passedTests.length > 0) {
+ log('✅ PASSED TESTS:');
+ log('');
+ passedTests.forEach(test => {
+ log(` #${test.testNumber.toString().padStart(2, ' ')}: ${test.client} → ${test.server} → ${test.endpoint}`);
+ log(` Facilitator: ${test.facilitator}`);
+ if (test.network) {
+ log(` Network: ${test.network}`);
+ }
+ if (test.transaction) {
+ log(` Tx: ${test.transaction}`);
+ }
+ });
+ log('');
+ }
+
+ if (failedTests.length > 0) {
+ log('❌ FAILED TESTS:');
+ log('');
+ failedTests.forEach(test => {
+ log(` #${test.testNumber.toString().padStart(2, ' ')}: ${test.client} → ${test.server} → ${test.endpoint}`);
+ log(` Facilitator: ${test.facilitator}`);
+ if (test.network) {
+ log(` Network: ${test.network}`);
+ }
+ log(` Error: ${test.error || 'Unknown error'}`);
+ });
+ log('');
+ }
+
+ // Breakdown by facilitator
+ const facilitatorBreakdown = testResults.reduce((acc, test) => {
+ const key = test.facilitator;
+ if (!acc[key]) acc[key] = { passed: 0, failed: 0 };
+ if (test.passed) acc[key].passed++;
+ else acc[key].failed++;
+ return acc;
+ }, {} as Record);
+
+ log('📊 Breakdown by Facilitator:');
+ Object.entries(facilitatorBreakdown).forEach(([facilitator, stats]) => {
+ const total = stats.passed + stats.failed;
+ const passRate = total > 0 ? Math.round((stats.passed / total) * 100) : 0;
+ log(` ${facilitator.padEnd(15)} ✅ ${stats.passed} / ❌ ${stats.failed} (${passRate}%)`);
});
log('');
-}
-// Breakdown by facilitator
-const facilitatorBreakdown = testResults.reduce((acc, test) => {
- const key = test.facilitator;
- if (!acc[key]) acc[key] = { passed: 0, failed: 0 };
- if (test.passed) acc[key].passed++;
- else acc[key].failed++;
- return acc;
-}, {} as Record);
-
-log('📊 Breakdown by Facilitator:');
-Object.entries(facilitatorBreakdown).forEach(([facilitator, stats]) => {
- const total = stats.passed + stats.failed;
- const passRate = total > 0 ? Math.round((stats.passed / total) * 100) : 0;
- log(` ${facilitator.padEnd(15)} ✅ ${stats.passed} / ❌ ${stats.failed} (${passRate}%)`);
-});
-log('');
-
-// Breakdown by server
-const serverBreakdown = testResults.reduce((acc, test) => {
- const key = test.server;
- if (!acc[key]) acc[key] = { passed: 0, failed: 0 };
- if (test.passed) acc[key].passed++;
- else acc[key].failed++;
- return acc;
-}, {} as Record);
-
-log('📊 Breakdown by Server:');
-Object.entries(serverBreakdown).forEach(([server, stats]) => {
- const total = stats.passed + stats.failed;
- const passRate = total > 0 ? Math.round((stats.passed / total) * 100) : 0;
- log(` ${server.padEnd(20)} ✅ ${stats.passed} / ❌ ${stats.failed} (${passRate}%)`);
-});
-log('');
-
-// Breakdown by client
-const clientBreakdown = testResults.reduce((acc, test) => {
- const key = test.client;
- if (!acc[key]) acc[key] = { passed: 0, failed: 0 };
- if (test.passed) acc[key].passed++;
- else acc[key].failed++;
- return acc;
-}, {} as Record);
-
-log('📊 Breakdown by Client:');
-Object.entries(clientBreakdown).forEach(([client, stats]) => {
- const total = stats.passed + stats.failed;
- const passRate = total > 0 ? Math.round((stats.passed / total) * 100) : 0;
- log(` ${client.padEnd(20)} ✅ ${stats.passed} / ❌ ${stats.failed} (${passRate}%)`);
-});
-log('');
-
-// Protocol family breakdown
-const protocolBreakdown = testResults.reduce((acc, test) => {
- const key = test.protocolFamily;
- if (!acc[key]) acc[key] = { passed: 0, failed: 0 };
- if (test.passed) acc[key].passed++;
- else acc[key].failed++;
- return acc;
-}, {} as Record);
-
-if (Object.keys(protocolBreakdown).length > 1) {
- log('📊 Protocol Family Breakdown:');
- Object.entries(protocolBreakdown).forEach(([protocol, stats]) => {
+ // Breakdown by server
+ const serverBreakdown = testResults.reduce((acc, test) => {
+ const key = test.server;
+ if (!acc[key]) acc[key] = { passed: 0, failed: 0 };
+ if (test.passed) acc[key].passed++;
+ else acc[key].failed++;
+ return acc;
+ }, {} as Record);
+
+ log('📊 Breakdown by Server:');
+ Object.entries(serverBreakdown).forEach(([server, stats]) => {
const total = stats.passed + stats.failed;
- log(` ${protocol.toUpperCase()}: ✅ ${stats.passed} / ❌ ${stats.failed} / 📈 ${total} total`);
+ const passRate = total > 0 ? Math.round((stats.passed / total) * 100) : 0;
+ log(` ${server.padEnd(20)} ✅ ${stats.passed} / ❌ ${stats.failed} (${passRate}%)`);
});
log('');
-}
-// Write structured JSON output if requested
-if (parsedArgs.outputJson) {
- const breakdown = (results: DetailedTestResult[], key: keyof DetailedTestResult) =>
- results.reduce((acc, test) => {
- const k = String(test[key]);
- if (!acc[k]) acc[k] = { passed: 0, failed: 0 };
- if (test.passed) acc[k].passed++;
- else acc[k].failed++;
- return acc;
- }, {} as Record);
-
- const jsonOutput = {
- summary: {
- total: passed + failed,
- passed,
- failed,
- networkMode,
- },
- results: testResults,
- breakdowns: {
- byFacilitator: breakdown(testResults, 'facilitator'),
- byServer: breakdown(testResults, 'server'),
- byClient: breakdown(testResults, 'client'),
- byProtocolFamily: breakdown(testResults, 'protocolFamily'),
- },
- };
+ // Breakdown by client
+ const clientBreakdown = testResults.reduce((acc, test) => {
+ const key = test.client;
+ if (!acc[key]) acc[key] = { passed: 0, failed: 0 };
+ if (test.passed) acc[key].passed++;
+ else acc[key].failed++;
+ return acc;
+ }, {} as Record);
+
+ log('📊 Breakdown by Client:');
+ Object.entries(clientBreakdown).forEach(([client, stats]) => {
+ const total = stats.passed + stats.failed;
+ const passRate = total > 0 ? Math.round((stats.passed / total) * 100) : 0;
+ log(` ${client.padEnd(20)} ✅ ${stats.passed} / ❌ ${stats.failed} (${passRate}%)`);
+ });
+ log('');
- writeFileSync(parsedArgs.outputJson, JSON.stringify(jsonOutput, null, 2));
- log(`📄 JSON results written to ${parsedArgs.outputJson}`);
-}
+ // Protocol family breakdown
+ const protocolBreakdown = testResults.reduce((acc, test) => {
+ const key = test.protocolFamily;
+ if (!acc[key]) acc[key] = { passed: 0, failed: 0 };
+ if (test.passed) acc[key].passed++;
+ else acc[key].failed++;
+ return acc;
+ }, {} as Record);
+
+ if (Object.keys(protocolBreakdown).length > 1) {
+ log('📊 Protocol Family Breakdown:');
+ Object.entries(protocolBreakdown).forEach(([protocol, stats]) => {
+ const total = stats.passed + stats.failed;
+ log(` ${protocol.toUpperCase()}: ✅ ${stats.passed} / ❌ ${stats.failed} / 📈 ${total} total`);
+ });
+ log('');
+ }
-// Close logger
-closeLogger();
+ // Write structured JSON output if requested
+ if (parsedArgs.outputJson) {
+ const breakdown = (results: DetailedTestResult[], key: keyof DetailedTestResult) =>
+ results.reduce((acc, test) => {
+ const k = String(test[key]);
+ if (!acc[k]) acc[k] = { passed: 0, failed: 0 };
+ if (test.passed) acc[k].passed++;
+ else acc[k].failed++;
+ return acc;
+ }, {} as Record);
+
+ const jsonOutput = {
+ summary: {
+ total: passed + failed,
+ passed,
+ failed,
+ networkMode,
+ },
+ results: testResults,
+ breakdowns: {
+ byFacilitator: breakdown(testResults, 'facilitator'),
+ byServer: breakdown(testResults, 'server'),
+ byClient: breakdown(testResults, 'client'),
+ byProtocolFamily: breakdown(testResults, 'protocolFamily'),
+ },
+ };
-if (failed > 0) {
- process.exit(1);
-}
+ writeFileSync(parsedArgs.outputJson, JSON.stringify(jsonOutput, null, 2));
+ log(`📄 JSON results written to ${parsedArgs.outputJson}`);
+ }
+
+ // Close logger
+ closeLogger();
+
+ if (failed > 0) {
+ process.exit(1);
+ }
}
// Run the test
diff --git a/examples/go/clients/advanced/all_networks.go b/examples/go/clients/advanced/all_networks.go
index be399f1bd0..229b102536 100644
--- a/examples/go/clients/advanced/all_networks.go
+++ b/examples/go/clients/advanced/all_networks.go
@@ -38,7 +38,7 @@ func runAllNetworksExample(ctx context.Context, evmPrivateKey, svmPrivateKey, ur
if err != nil {
return fmt.Errorf("failed to create EVM signer: %w", err)
}
- client.Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ client.Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
fmt.Printf("✅ Registered EVM networks (eip155:*)\n")
}
@@ -106,7 +106,13 @@ func printPaymentDetails(headers http.Header) {
}
fmt.Println("💰 Payment Details:")
- fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
+ fmt.Printf(" Success: %v\n", settleResp.Success)
+ if settleResp.ErrorReason != "" {
+ fmt.Printf(" ErrorReason: %s\n", settleResp.ErrorReason)
+ }
+ if settleResp.Transaction != "" {
+ fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
+ }
fmt.Printf(" Network: %s\n", settleResp.Network)
fmt.Printf(" Payer: %s\n", settleResp.Payer)
}
diff --git a/examples/go/clients/advanced/custom_transport.go b/examples/go/clients/advanced/custom_transport.go
index 7b8ee4c2c9..2206722bdd 100644
--- a/examples/go/clients/advanced/custom_transport.go
+++ b/examples/go/clients/advanced/custom_transport.go
@@ -90,7 +90,7 @@ func runCustomTransportExample(ctx context.Context, evmPrivateKey, url string) e
// Create x402 client
client := x402.Newx402Client().
- Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
httpClient := x402http.Newx402HTTPClient(client)
@@ -135,6 +135,10 @@ func runCustomTransportExample(ctx context.Context, evmPrivateKey, url string) e
}
defer resp.Body.Close()
- return printResponse(resp, "Response with custom transport")
+ if err := printResponse(resp, "Response with custom transport"); err != nil {
+ return err
+ }
+ printPaymentDetails(resp.Header)
+ return nil
}
diff --git a/examples/go/clients/advanced/error_recovery.go b/examples/go/clients/advanced/error_recovery.go
index b4ad618f2e..8b65d2599e 100644
--- a/examples/go/clients/advanced/error_recovery.go
+++ b/examples/go/clients/advanced/error_recovery.go
@@ -38,7 +38,7 @@ func runErrorRecoveryExample(ctx context.Context, evmPrivateKey, url string) err
// Create x402 client with comprehensive error handling
client := x402.Newx402Client().
- Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
// Recovery counter
recoveryAttempts := 0
@@ -122,7 +122,11 @@ func runErrorRecoveryExample(ctx context.Context, evmPrivateKey, url string) err
fmt.Printf(" Successful recoveries: %d\n", successfulRecoveries)
fmt.Printf(" Final status: %d\n\n", resp.StatusCode)
- return printResponse(resp, "Response after error recovery")
+ if err := printResponse(resp, "Response after error recovery"); err != nil {
+ return err
+ }
+ printPaymentDetails(resp.Header)
+ return nil
}
// classifyError categorizes errors for targeted recovery strategies
diff --git a/examples/go/clients/advanced/hooks.go b/examples/go/clients/advanced/hooks.go
index d4a0c7e8e0..0a3bad969e 100644
--- a/examples/go/clients/advanced/hooks.go
+++ b/examples/go/clients/advanced/hooks.go
@@ -38,7 +38,7 @@ func runHooksExample(ctx context.Context, evmPrivateKey, url string) error {
// Create client with scheme registration
client := x402.Newx402Client().
- Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
// Register lifecycle hooks
@@ -107,6 +107,10 @@ func runHooksExample(ctx context.Context, evmPrivateKey, url string) error {
fmt.Println("✅ Request completed successfully with hooks\n")
- return printResponse(resp, "Response with hooks")
+ if err := printResponse(resp, "Response with hooks"); err != nil {
+ return err
+ }
+ printPaymentDetails(resp.Header)
+ return nil
}
diff --git a/examples/go/clients/advanced/multi_network_priority.go b/examples/go/clients/advanced/multi_network_priority.go
index b3cb6e1c3d..be2145585f 100644
--- a/examples/go/clients/advanced/multi_network_priority.go
+++ b/examples/go/clients/advanced/multi_network_priority.go
@@ -48,17 +48,17 @@ func runMultiNetworkPriorityExample(ctx context.Context, evmPrivateKey, url stri
// Level 1: Specific networks (highest priority)
fmt.Println("✅ Registering Ethereum Mainnet (eip155:1) with mainnet signer")
- client.Register("eip155:1", evm.NewExactEvmScheme(mainnetSigner))
+ client.Register("eip155:1", evm.NewExactEvmScheme(mainnetSigner, nil))
fmt.Println("✅ Registering Base Mainnet (eip155:8453) with base signer")
- client.Register("eip155:8453", evm.NewExactEvmScheme(baseSigner))
+ client.Register("eip155:8453", evm.NewExactEvmScheme(baseSigner, nil))
fmt.Println("✅ Registering Base Sepolia (eip155:84532) with testnet signer")
- client.Register("eip155:84532", evm.NewExactEvmScheme(testnetSigner))
+ client.Register("eip155:84532", evm.NewExactEvmScheme(testnetSigner, nil))
// Level 2: Wildcard for all other EVM networks (fallback)
fmt.Println("✅ Registering all other EVM networks (eip155:*) with primary signer\n")
- client.Register("eip155:*", evm.NewExactEvmScheme(primarySigner))
+ client.Register("eip155:*", evm.NewExactEvmScheme(primarySigner, nil))
// Add logging to show which network is being used
client.OnBeforePaymentCreation(func(ctx x402.PaymentCreationContext) (*x402.BeforePaymentCreationHookResult, error) {
@@ -111,6 +111,10 @@ func runMultiNetworkPriorityExample(ctx context.Context, evmPrivateKey, url stri
fmt.Println(" This allows fine-grained control per network while having")
fmt.Println(" sensible defaults for unknown networks.\n")
- return printResponse(resp, "Response with multi-network priority")
+ if err := printResponse(resp, "Response with multi-network priority"); err != nil {
+ return err
+ }
+ printPaymentDetails(resp.Header)
+ return nil
}
diff --git a/examples/go/clients/custom/main.go b/examples/go/clients/custom/main.go
index ee28bf4510..fa1e0dd370 100644
--- a/examples/go/clients/custom/main.go
+++ b/examples/go/clients/custom/main.go
@@ -70,17 +70,22 @@ func main() {
}
x402Client := x402.Newx402Client().
- Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
// Make the request with custom payment handling
fmt.Println("🔧 Using custom payment implementation (no wrapper)\n")
-
+
resp, err := makeRequestWithPayment(ctx, x402Client, url)
+ if resp != nil {
+ defer resp.Body.Close()
+ }
if err != nil {
fmt.Printf("❌ Request failed: %v\n", err)
+ if resp != nil {
+ displayPaymentDetails(resp)
+ }
os.Exit(1)
}
- defer resp.Body.Close()
// Read and display response
var responseData interface{}
@@ -93,16 +98,33 @@ func main() {
prettyJSON, _ := json.MarshalIndent(responseData, " ", " ")
fmt.Printf(" %s\n", string(prettyJSON))
- // Extract payment settlement details
- if paymentHeader := resp.Header.Get("PAYMENT-RESPONSE"); paymentHeader != "" {
- settleResp, err := extractSettlementResponse(paymentHeader)
- if err == nil {
- fmt.Println("\n💰 Payment Settlement Details:")
- fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
- fmt.Printf(" Network: %s\n", settleResp.Network)
- fmt.Printf(" Payer: %s\n", settleResp.Payer)
- }
+ displayPaymentDetails(resp)
+}
+
+// displayPaymentDetails extracts and prints payment settlement from response headers.
+// Payment-response header is sent on both success and error.
+func displayPaymentDetails(resp *http.Response) {
+ paymentHeader := resp.Header.Get("PAYMENT-RESPONSE")
+ if paymentHeader == "" {
+ paymentHeader = resp.Header.Get("X-PAYMENT-RESPONSE")
+ }
+ if paymentHeader == "" {
+ return
+ }
+ settleResp, err := extractSettlementResponse(paymentHeader)
+ if err != nil {
+ return
+ }
+ fmt.Println("\n💰 Payment Settlement Details:")
+ fmt.Printf(" Success: %v\n", settleResp.Success)
+ if settleResp.ErrorReason != "" {
+ fmt.Printf(" ErrorReason: %s\n", settleResp.ErrorReason)
+ }
+ if settleResp.Transaction != "" {
+ fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
}
+ fmt.Printf(" Network: %s\n", settleResp.Network)
+ fmt.Printf(" Payer: %s\n", settleResp.Payer)
}
// makeRequestWithPayment implements the complete payment flow manually
@@ -247,9 +269,8 @@ func makeRequestWithPayment(ctx context.Context, x402Client *x402.X402Client, ur
// Step 6: Verify success
// ========================================================================
if retryResp.StatusCode >= 400 {
- defer retryResp.Body.Close()
errorBody, _ := io.ReadAll(retryResp.Body)
- return nil, fmt.Errorf("payment failed: status %d, body: %s", retryResp.StatusCode, string(errorBody))
+ return retryResp, fmt.Errorf("payment failed: status %d, body: %s", retryResp.StatusCode, string(errorBody))
}
fmt.Println("✅ Step 6: Payment successful!\n")
diff --git a/examples/go/clients/http/builder_pattern.go b/examples/go/clients/http/builder_pattern.go
index ec9dd45dd7..1256006bdc 100644
--- a/examples/go/clients/http/builder_pattern.go
+++ b/examples/go/clients/http/builder_pattern.go
@@ -29,12 +29,12 @@ func createBuilderPatternClient(evmPrivateKey, svmPrivateKey string) (*x402.X402
client := x402.Newx402Client()
// Register EVM scheme for all EVM networks
- client.Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ client.Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
// You can also register specific networks for fine-grained control
// For example, use a different signer for Ethereum mainnet:
// ethereumSigner := evmsigners.NewClientSignerFromPrivateKey(ethereumKey)
- // client.Register("eip155:1", evm.NewExactEvmScheme(ethereumSigner))
+ // client.Register("eip155:1", evm.NewExactEvmScheme(ethereumSigner, nil))
// Register SVM scheme if key is provided
if svmPrivateKey != "" {
diff --git a/examples/go/clients/http/main.go b/examples/go/clients/http/main.go
index 8eab6c3446..5adbbad5f1 100644
--- a/examples/go/clients/http/main.go
+++ b/examples/go/clients/http/main.go
@@ -121,7 +121,13 @@ func makeRequest(client *x402.X402Client, url string) error {
fmt.Println("\n💰 Payment Details:")
settleResp, err := extractPaymentResponse(resp.Header)
if err == nil {
- fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
+ fmt.Printf(" Success: %v\n", settleResp.Success)
+ if settleResp.ErrorReason != "" {
+ fmt.Printf(" ErrorReason: %s\n", settleResp.ErrorReason)
+ }
+ if settleResp.Transaction != "" {
+ fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
+ }
fmt.Printf(" Network: %s\n", settleResp.Network)
fmt.Printf(" Payer: %s\n", settleResp.Payer)
}
diff --git a/examples/go/clients/http/mechanism_helper_registration.go b/examples/go/clients/http/mechanism_helper_registration.go
index cf4d79b403..d2fa8cee9c 100644
--- a/examples/go/clients/http/mechanism_helper_registration.go
+++ b/examples/go/clients/http/mechanism_helper_registration.go
@@ -31,7 +31,7 @@ func createMechanismHelperRegistrationClient(evmPrivateKey, svmPrivateKey string
// Register EVM scheme for all EVM networks using wildcard
// This registers:
// - eip155:* (all EVM networks in v2)
- client.Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ client.Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
// Register SVM scheme if key is provided
if svmPrivateKey != "" {
@@ -48,7 +48,7 @@ func createMechanismHelperRegistrationClient(evmPrivateKey, svmPrivateKey string
// The fluent API allows chaining for clean code:
// client := x402.Newx402Client().
- // Register("eip155:*", evm.NewExactEvmScheme(evmSigner)).
+ // Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil)).
// Register("solana:*", svm.NewExactSvmScheme(svmSigner))
return client, nil
diff --git a/examples/go/clients/mcp-chatbot/main.go b/examples/go/clients/mcp-chatbot/main.go
index 34acea0b8b..4ecfaa5322 100644
--- a/examples/go/clients/mcp-chatbot/main.go
+++ b/examples/go/clients/mcp-chatbot/main.go
@@ -105,7 +105,7 @@ func run() error {
// Create x402 payment client and wrap session
paymentClient := x402.Newx402Client()
- paymentClient.Register("eip155:84532", evm.NewExactEvmScheme(evmSigner))
+ paymentClient.Register("eip155:84532", evm.NewExactEvmScheme(evmSigner, nil))
x402Mcp := mcp.NewX402MCPClient(clientSession, paymentClient, mcp.Options{
AutoPayment: mcp.BoolPtr(true),
OnPaymentRequested: func(context mcp.PaymentRequiredContext) (bool, error) {
diff --git a/examples/go/clients/mcp/advanced.go b/examples/go/clients/mcp/advanced.go
index e08517c469..5855bc0583 100644
--- a/examples/go/clients/mcp/advanced.go
+++ b/examples/go/clients/mcp/advanced.go
@@ -81,7 +81,7 @@ func runAdvanced() error {
// Step 2: Create x402 payment client manually
paymentClient := x402.Newx402Client()
- paymentClient.Register("eip155:84532", evm.NewExactEvmScheme(evmSigner))
+ paymentClient.Register("eip155:84532", evm.NewExactEvmScheme(evmSigner, nil))
// Step 3: Compose into X402MCPClient with session
x402Mcp := mcp.NewX402MCPClient(clientSession, paymentClient, mcp.Options{
diff --git a/examples/go/clients/mcp/simple.go b/examples/go/clients/mcp/simple.go
index 9b324cbc61..1fc19d0530 100644
--- a/examples/go/clients/mcp/simple.go
+++ b/examples/go/clients/mcp/simple.go
@@ -73,7 +73,7 @@ func runSimple() error {
// Create x402 payment client and wrap session
paymentClient := x402.Newx402Client()
- paymentClient.Register("eip155:84532", evm.NewExactEvmScheme(evmSigner))
+ paymentClient.Register("eip155:84532", evm.NewExactEvmScheme(evmSigner, nil))
x402Mcp := mcp.NewX402MCPClient(clientSession, paymentClient, mcp.Options{
AutoPayment: mcp.BoolPtr(true),
OnPaymentRequested: func(context mcp.PaymentRequiredContext) (bool, error) {
diff --git a/examples/go/clients/payment-identifier/main.go b/examples/go/clients/payment-identifier/main.go
index 53121fd870..5cc78af831 100644
--- a/examples/go/clients/payment-identifier/main.go
+++ b/examples/go/clients/payment-identifier/main.go
@@ -145,7 +145,7 @@ func main() {
// Create client with scheme registration
client := x402.Newx402Client().
- Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+ Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
// Generate a unique payment ID for this session
paymentID := paymentidentifier.GeneratePaymentID("")
@@ -169,27 +169,29 @@ func main() {
fmt.Printf("Making request to: %s\n\n", serverURL)
startTime1 := time.Now()
- resp1, err := makeRequest(ctx, wrappedClient, serverURL)
+ resp1, httpResp1, err := makeRequest(ctx, wrappedClient, serverURL)
duration1 := time.Since(startTime1)
if err != nil {
fmt.Printf("Request failed: %v\n", err)
os.Exit(1)
}
- fmt.Printf("Response (%v): %s\n\n", duration1, resp1)
+ fmt.Printf("Response (%v): %s\n", duration1, resp1)
+ printPaymentDetails(httpResp1)
// Second request - replay the same signed payment (true retry)
- fmt.Println("Second Request (retry with cached payment)")
+ fmt.Println("\nSecond Request (retry with cached payment)")
fmt.Printf("Making request to: %s\n", serverURL)
fmt.Println("Expected: Server returns cached response (same ID, same payload)")
startTime2 := time.Now()
- resp2, err := makeRequest(ctx, wrappedClient, serverURL)
+ resp2, httpResp2, err := makeRequest(ctx, wrappedClient, serverURL)
duration2 := time.Since(startTime2)
if err != nil {
fmt.Printf("Request failed: %v\n", err)
os.Exit(1)
}
- fmt.Printf("Response (%v): %s\n\n", duration2, resp2)
+ fmt.Printf("Response (%v): %s\n", duration2, resp2)
+ printPaymentDetails(httpResp2)
// Summary
fmt.Println("Summary")
@@ -198,23 +200,52 @@ func main() {
fmt.Printf(" Second request: %v\n", duration2)
}
-func makeRequest(ctx context.Context, client *http.Client, url string) (string, error) {
+func makeRequest(ctx context.Context, client *http.Client, url string) (string, *http.Response, error) {
req, err := http.NewRequestWithContext(ctx, "POST", url, strings.NewReader(`{"item": "widget"}`))
if err != nil {
- return "", err
+ return "", nil, err
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
- return "", err
+ return "", nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
- return "", err
+ return "", resp, err
}
- return string(body), nil
+ return string(body), resp, nil
+}
+
+// printPaymentDetails extracts and prints payment settlement from response headers.
+func printPaymentDetails(resp *http.Response) {
+ paymentHeader := resp.Header.Get("PAYMENT-RESPONSE")
+ if paymentHeader == "" {
+ paymentHeader = resp.Header.Get("X-PAYMENT-RESPONSE")
+ }
+ if paymentHeader == "" {
+ return
+ }
+ decoded, err := base64.StdEncoding.DecodeString(paymentHeader)
+ if err != nil {
+ return
+ }
+ var settleResp x402.SettleResponse
+ if err := json.Unmarshal(decoded, &settleResp); err != nil {
+ return
+ }
+ fmt.Println("💰 Payment Details:")
+ fmt.Printf(" Success: %v\n", settleResp.Success)
+ if settleResp.ErrorReason != "" {
+ fmt.Printf(" ErrorReason: %s\n", settleResp.ErrorReason)
+ }
+ if settleResp.Transaction != "" {
+ fmt.Printf(" Transaction: %s\n", settleResp.Transaction)
+ }
+ fmt.Printf(" Network: %s\n", settleResp.Network)
+ fmt.Printf(" Payer: %s\n", settleResp.Payer)
}
diff --git a/examples/go/facilitator/advanced/signer.go b/examples/go/facilitator/advanced/signer.go
index 19f57d0857..eac33b0a58 100644
--- a/examples/go/facilitator/advanced/signer.go
+++ b/examples/go/facilitator/advanced/signer.go
@@ -190,6 +190,11 @@ func (s *facilitatorEvmSigner) ReadContract(
return nil, fmt.Errorf("failed to parse ABI: %w", err)
}
+ methodObj, exists := contractABI.Methods[method]
+ if !exists {
+ return nil, fmt.Errorf("method %s not found in ABI", method)
+ }
+
// Pack the method call
data, err := contractABI.Pack(method, args...)
if err != nil {
@@ -209,23 +214,11 @@ func (s *facilitatorEvmSigner) ReadContract(
return nil, fmt.Errorf("failed to call contract: %w", err)
}
- // Handle empty result
- if len(result) == 0 {
- if method == "authorizationState" {
- return false, nil
- }
- if method == "balanceOf" || method == "allowance" {
- return big.NewInt(0), nil
- }
- return nil, fmt.Errorf("empty result from contract call")
+ if len(methodObj.Outputs) == 0 {
+ return nil, nil
}
// Unpack the result
- methodObj, exists := contractABI.Methods[method]
- if !exists {
- return nil, fmt.Errorf("method %s not found in ABI", method)
- }
-
output, err := methodObj.Outputs.Unpack(result)
if err != nil {
return nil, fmt.Errorf("failed to unpack result: %w", err)
@@ -606,4 +599,3 @@ func getBigIntFromInterface(v interface{}) *big.Int {
}
return big.NewInt(0)
}
-
diff --git a/examples/go/facilitator/basic/main.go b/examples/go/facilitator/basic/main.go
index 589373b230..c9ead5daa9 100644
--- a/examples/go/facilitator/basic/main.go
+++ b/examples/go/facilitator/basic/main.go
@@ -11,6 +11,7 @@ import (
x402 "github.com/coinbase/x402/go"
evm "github.com/coinbase/x402/go/mechanisms/evm/exact/facilitator"
evmv1 "github.com/coinbase/x402/go/mechanisms/evm/exact/v1/facilitator"
+ svmmech "github.com/coinbase/x402/go/mechanisms/svm"
svm "github.com/coinbase/x402/go/mechanisms/svm/exact/facilitator"
svmv1 "github.com/coinbase/x402/go/mechanisms/svm/exact/v1/facilitator"
"github.com/gin-gonic/gin"
@@ -61,8 +62,9 @@ func main() {
facilitator.RegisterV1([]x402.Network{"base-sepolia"}, evmv1.NewExactEvmSchemeV1(evmSigner, evmV1Config))
if svmSigner != nil {
- facilitator.Register([]x402.Network{svmNetwork}, svm.NewExactSvmScheme(svmSigner))
- facilitator.RegisterV1([]x402.Network{"solana-devnet"}, svmv1.NewExactSvmSchemeV1(svmSigner))
+ settlementCache := svmmech.NewSettlementCache()
+ facilitator.Register([]x402.Network{svmNetwork}, svm.NewExactSvmScheme(svmSigner, settlementCache))
+ facilitator.RegisterV1([]x402.Network{"solana-devnet"}, svmv1.NewExactSvmSchemeV1(svmSigner, settlementCache))
}
facilitator.OnAfterVerify(func(ctx x402.FacilitatorVerifyResultContext) error {
diff --git a/examples/go/facilitator/basic/signer.go b/examples/go/facilitator/basic/signer.go
index 19f57d0857..442da10515 100644
--- a/examples/go/facilitator/basic/signer.go
+++ b/examples/go/facilitator/basic/signer.go
@@ -184,12 +184,18 @@ func (s *facilitatorEvmSigner) ReadContract(
method string,
args ...interface{},
) (interface{}, error) {
+
// Parse ABI
contractABI, err := abi.JSON(strings.NewReader(string(abiJSON)))
if err != nil {
return nil, fmt.Errorf("failed to parse ABI: %w", err)
}
+ methodObj, exists := contractABI.Methods[method]
+ if !exists {
+ return nil, fmt.Errorf("method %s not found in ABI", method)
+ }
+
// Pack the method call
data, err := contractABI.Pack(method, args...)
if err != nil {
@@ -208,24 +214,12 @@ func (s *facilitatorEvmSigner) ReadContract(
if err != nil {
return nil, fmt.Errorf("failed to call contract: %w", err)
}
-
- // Handle empty result
- if len(result) == 0 {
- if method == "authorizationState" {
- return false, nil
- }
- if method == "balanceOf" || method == "allowance" {
- return big.NewInt(0), nil
- }
- return nil, fmt.Errorf("empty result from contract call")
+
+ if len(methodObj.Outputs) == 0 {
+ return nil, nil
}
// Unpack the result
- methodObj, exists := contractABI.Methods[method]
- if !exists {
- return nil, fmt.Errorf("method %s not found in ABI", method)
- }
-
output, err := methodObj.Outputs.Unpack(result)
if err != nil {
return nil, fmt.Errorf("failed to unpack result: %w", err)
@@ -606,4 +600,3 @@ func getBigIntFromInterface(v interface{}) *big.Int {
}
return big.NewInt(0)
}
-
diff --git a/examples/go/servers/bazaar/.env-example b/examples/go/servers/bazaar/.env-example
new file mode 100644
index 0000000000..7e5cdfcaf6
--- /dev/null
+++ b/examples/go/servers/bazaar/.env-example
@@ -0,0 +1,3 @@
+EVM_PAYEE_ADDRESS=
+SVM_PAYEE_ADDRESS=
+FACILITATOR_URL=
diff --git a/examples/go/servers/bazaar/.gitignore b/examples/go/servers/bazaar/.gitignore
new file mode 100644
index 0000000000..8acdd4dc03
--- /dev/null
+++ b/examples/go/servers/bazaar/.gitignore
@@ -0,0 +1,24 @@
+# Environment variables
+.env
+
+# Binaries
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+gin
+server
+
+# Go build artifacts
+*.test
+*.out
+go.sum
+
+# IDE
+.idea/
+.vscode/
+*.swp
+*.swo
+*~
+
diff --git a/examples/go/servers/bazaar/README.md b/examples/go/servers/bazaar/README.md
new file mode 100644
index 0000000000..056b77ee35
--- /dev/null
+++ b/examples/go/servers/bazaar/README.md
@@ -0,0 +1,140 @@
+# Bazaar Discovery Example Server (Go)
+
+Gin server demonstrating how to make a paid API **discoverable** using the Bazaar extension with dynamic route parameters.
+
+The key addition over a basic x402 server is `DeclareDiscoveryExtension` -- it describes your endpoint's inputs, outputs, and path parameters so that facilitators (and agents) can automatically catalog and invoke your API.
+
+## What This Example Shows
+
+**Dynamic route parameters** -- the route `GET /weather/:city` uses a `:city` slug. The x402 middleware automatically:
+
+1. Matches `/weather/san-francisco`, `/weather/tokyo`, etc. against the route pattern
+2. Extracts `{ city: "san-francisco" }` as `pathParams` in the discovery extension
+3. Produces `routeTemplate: "/weather/:city"` so all concrete URLs consolidate into **one** catalog entry
+
+```go
+weatherExtension, _ := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET, nil, nil, "",
+ &types.OutputConfig{
+ Example: map[string]interface{}{"city": "san-francisco", "weather": "foggy", "temperature": 60},
+ },
+ bazaar.DeclareDiscoveryExtensionOpts{
+ PathParamsSchema: types.JSONSchema{
+ "properties": map[string]interface{}{
+ "city": map[string]interface{}{"type": "string", "description": "City name slug"},
+ },
+ "required": []string{"city"},
+ },
+ },
+)
+
+routes := x402http.RoutesConfig{
+ "GET /weather/:city": {
+ Accepts: x402http.PaymentOptions{{Scheme: "exact", Price: "$0.001", ...}},
+ Description: "Weather data for a city",
+ Extensions: map[string]interface{}{bazaar.BAZAAR.Key(): weatherExtension},
+ },
+}
+```
+
+Note that the x402 route key uses `:city` which aligns with both Express convention and Gin's `c.Param("city")` extraction.
+
+## Prerequisites
+
+- Go 1.24 or higher
+- Valid EVM address for receiving payments
+- Valid SVM address for receiving payments
+- URL of a facilitator supporting the desired payment network, see [facilitator list](https://www.x402.org/ecosystem?category=facilitators)
+
+## Setup
+
+1. Copy `.env-example` to `.env`:
+
+```bash
+cp .env-example .env
+```
+
+and fill required environment variables:
+
+- `FACILITATOR_URL` - Facilitator endpoint URL
+- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments
+- `SVM_PAYEE_ADDRESS` - Solana address to receive payments
+
+2. Install dependencies:
+```bash
+go mod download
+```
+
+3. Run the server:
+```bash
+go run main.go
+```
+
+Server runs at http://localhost:4021
+
+## How Discovery Works
+
+When a client hits `GET /weather/san-francisco` without a payment, the 402 response includes the enriched bazaar extension:
+
+```json
+{
+ "x402Version": 2,
+ "error": "Payment required",
+ "resource": { "url": "http://localhost:4021/weather/san-francisco" },
+ "extensions": {
+ "bazaar": {
+ "routeTemplate": "/weather/:city",
+ "info": {
+ "input": {
+ "type": "http",
+ "method": "GET",
+ "pathParams": { "city": "san-francisco" }
+ },
+ "output": {
+ "type": "json",
+ "example": { "city": "san-francisco", "weather": "foggy", "temperature": 60 }
+ }
+ }
+ }
+ },
+ "accepts": [{ "..." : "..." }]
+}
+```
+
+The facilitator uses `routeTemplate` as the canonical catalog key, so requests to `/weather/san-francisco`, `/weather/tokyo`, and `/weather/new-york` all map to a single discoverable endpoint: `/weather/:city`.
+
+## Example Endpoints
+
+| Endpoint | Payment | Price |
+|----------|---------|-------|
+| `GET /health` | No | - |
+| `GET /weather/:city` | Yes | $0.001 USDC |
+| `GET /weather/:country/:city` | Yes | $0.001 USDC |
+
+## Multiple Path Parameters
+
+Routes can have multiple `:param` segments. Param names are matched by **position in the URL**, not by the order they appear in `PathParamsSchema`:
+
+```
+GET /weather/:country/:city
+ ^ ^
+ | └── second URL segment -> "city"
+ └──────────── first URL segment -> "country"
+```
+
+A request to `/weather/us/san-francisco` produces `pathParams: { country: "us", city: "san-francisco" }`. The property order in `PathParamsSchema` does not affect matching -- only the segment position in the URL matters.
+
+## `DeclareDiscoveryExtension` API
+
+```go
+func DeclareDiscoveryExtension(
+ method interface{}, // bazaar.MethodGET, bazaar.MethodPOST, etc.
+ input interface{}, // Example input data (query params or body)
+ inputSchema types.JSONSchema, // JSON Schema for input
+ bodyType types.BodyType, // For POST/PUT/PATCH: "json", "form-data", "text"
+ output *types.OutputConfig, // Example response
+ opts ...DeclareDiscoveryExtensionOpts, // PathParamsSchema
+) (types.DiscoveryExtension, error)
+```
+
+The optional `DeclareDiscoveryExtensionOpts` struct provides `PathParamsSchema` for describing URL path parameters.
diff --git a/examples/go/servers/bazaar/go.mod b/examples/go/servers/bazaar/go.mod
new file mode 100644
index 0000000000..5b5b438c56
--- /dev/null
+++ b/examples/go/servers/bazaar/go.mod
@@ -0,0 +1,83 @@
+module github.com/coinbase/x402/examples/go/servers/bazaar
+
+go 1.24.0
+
+toolchain go1.24.1
+
+replace github.com/coinbase/x402/go => ../../../../go
+
+require (
+ github.com/coinbase/x402/go v0.0.0-00010101000000-000000000000
+ github.com/gin-gonic/gin v1.11.0
+ github.com/joho/godotenv v1.5.1
+)
+
+require (
+ filippo.io/edwards25519 v1.0.0-rc.1 // indirect
+ github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
+ github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
+ github.com/bits-and-blooms/bitset v1.20.0 // indirect
+ github.com/blendle/zapdriver v1.3.1 // indirect
+ github.com/bytedance/sonic v1.14.0 // indirect
+ github.com/bytedance/sonic/loader v0.3.0 // indirect
+ github.com/cloudwego/base64x v0.1.6 // indirect
+ github.com/consensys/gnark-crypto v0.18.0 // indirect
+ github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
+ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
+ github.com/ethereum/go-ethereum v1.16.7 // indirect
+ github.com/ethereum/go-verkle v0.2.2 // indirect
+ github.com/fatih/color v1.16.0 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.8 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
+ github.com/gagliardetto/solana-go v1.14.0 // indirect
+ github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/gin-contrib/sse v1.1.0 // indirect
+ github.com/go-playground/locales v0.14.1 // indirect
+ github.com/go-playground/universal-translator v0.18.1 // indirect
+ github.com/go-playground/validator/v10 v10.27.0 // indirect
+ github.com/goccy/go-json v0.10.4 // indirect
+ github.com/goccy/go-yaml v1.18.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/holiman/uint256 v1.3.2 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.16.0 // indirect
+ github.com/klauspost/cpuid/v2 v2.3.0 // indirect
+ github.com/leodido/go-urn v1.4.0 // indirect
+ github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mitchellh/go-testing-interface v1.14.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect
+ github.com/mr-tron/base58 v1.2.0 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.4 // indirect
+ github.com/quic-go/qpack v0.5.1 // indirect
+ github.com/quic-go/quic-go v0.55.0 // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
+ github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.3.0 // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+ github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+ go.mongodb.org/mongo-driver v1.12.2 // indirect
+ go.uber.org/atomic v1.7.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ go.uber.org/ratelimit v0.2.0 // indirect
+ go.uber.org/zap v1.21.0 // indirect
+ golang.org/x/arch v0.20.0 // indirect
+ golang.org/x/crypto v0.41.0 // indirect
+ golang.org/x/mod v0.27.0 // indirect
+ golang.org/x/net v0.43.0 // indirect
+ golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/sys v0.36.0 // indirect
+ golang.org/x/term v0.34.0 // indirect
+ golang.org/x/text v0.28.0 // indirect
+ golang.org/x/time v0.9.0 // indirect
+ golang.org/x/tools v0.36.0 // indirect
+ google.golang.org/protobuf v1.36.9 // indirect
+)
diff --git a/examples/go/servers/bazaar/main.go b/examples/go/servers/bazaar/main.go
new file mode 100644
index 0000000000..c6e6c48e0c
--- /dev/null
+++ b/examples/go/servers/bazaar/main.go
@@ -0,0 +1,172 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+ "time"
+
+ x402http "github.com/coinbase/x402/go/http"
+ "github.com/coinbase/x402/go/extensions/bazaar"
+ "github.com/coinbase/x402/go/extensions/types"
+ ginmw "github.com/coinbase/x402/go/http/gin"
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+ ginfw "github.com/gin-gonic/gin"
+ "github.com/joho/godotenv"
+)
+
+const (
+ DefaultPort = "4021"
+)
+
+func main() {
+ godotenv.Load()
+
+ evmAddress := os.Getenv("EVM_PAYEE_ADDRESS")
+ if evmAddress == "" {
+ fmt.Println("EVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ svmAddress := os.Getenv("SVM_PAYEE_ADDRESS")
+ if svmAddress == "" {
+ fmt.Println("SVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ facilitatorURL := os.Getenv("FACILITATOR_URL")
+ if facilitatorURL == "" {
+ fmt.Println("FACILITATOR_URL environment variable is required")
+ os.Exit(1)
+ }
+
+ fmt.Printf("Starting Bazaar discovery example server...\n")
+ fmt.Printf(" EVM Payee: %s\n", evmAddress)
+ fmt.Printf(" SVM Payee: %s\n", svmAddress)
+ fmt.Printf(" Facilitator: %s\n", facilitatorURL)
+
+ r := ginfw.Default()
+
+ facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+ })
+
+ paymentOptions := x402http.PaymentOptions{
+ {Scheme: "exact", Price: "$0.001", Network: "eip155:84532", PayTo: evmAddress},
+ {Scheme: "exact", Price: "$0.001", Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", PayTo: svmAddress},
+ }
+
+ // Single path param: /weather/:city
+ weatherByCityExt, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET, nil, nil, "",
+ &types.OutputConfig{
+ Example: map[string]interface{}{"city": "san-francisco", "weather": "foggy", "temperature": 60},
+ },
+ bazaar.DeclareDiscoveryExtensionOpts{
+ PathParamsSchema: types.JSONSchema{
+ "properties": map[string]interface{}{
+ "city": map[string]interface{}{"type": "string", "description": "City name slug"},
+ },
+ "required": []string{"city"},
+ },
+ },
+ )
+ if err != nil {
+ fmt.Printf("Error declaring discovery extension: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Multiple path params: /weather/:country/:city
+ // Param names are matched by position in the URL, not by declaration order in the schema.
+ // /weather/us/san-francisco -> { country: "us", city: "san-francisco" }
+ weatherByCountryCityExt, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET, nil, nil, "",
+ &types.OutputConfig{
+ Example: map[string]interface{}{"country": "us", "city": "san-francisco", "weather": "foggy", "temperature": 60},
+ },
+ bazaar.DeclareDiscoveryExtensionOpts{
+ PathParamsSchema: types.JSONSchema{
+ "properties": map[string]interface{}{
+ "country": map[string]interface{}{"type": "string", "description": "Country code"},
+ "city": map[string]interface{}{"type": "string", "description": "City name slug"},
+ },
+ "required": []string{"country", "city"},
+ },
+ },
+ )
+ if err != nil {
+ fmt.Printf("Error declaring discovery extension: %v\n", err)
+ os.Exit(1)
+ }
+
+ routes := x402http.RoutesConfig{
+ "GET /weather/:city": {
+ Accepts: paymentOptions,
+ Description: "Weather data for a city",
+ MimeType: "application/json",
+ Extensions: map[string]interface{}{bazaar.BAZAAR.Key(): weatherByCityExt},
+ },
+ "GET /weather/:country/:city": {
+ Accepts: paymentOptions,
+ Description: "Weather data for a city in a specific country",
+ MimeType: "application/json",
+ Extensions: map[string]interface{}{bazaar.BAZAAR.Key(): weatherByCountryCityExt},
+ },
+ }
+
+ r.Use(ginmw.X402Payment(ginmw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []ginmw.SchemeConfig{
+ {Network: "eip155:84532", Server: evm.NewExactEvmScheme()},
+ {Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", Server: svm.NewExactSvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+ }))
+
+ r.GET("/weather/:city", func(c *ginfw.Context) {
+ city := c.Param("city")
+ weatherData := map[string]map[string]interface{}{
+ "san-francisco": {"weather": "foggy", "temperature": 60},
+ "new-york": {"weather": "cloudy", "temperature": 55},
+ "tokyo": {"weather": "rainy", "temperature": 65},
+ }
+ data, exists := weatherData[city]
+ if !exists {
+ data = map[string]interface{}{"weather": "sunny", "temperature": 70}
+ }
+ c.JSON(http.StatusOK, ginfw.H{"city": city, "weather": data["weather"], "temperature": data["temperature"]})
+ })
+
+ r.GET("/weather/:country/:city", func(c *ginfw.Context) {
+ country := c.Param("country")
+ city := c.Param("city")
+ weatherData := map[string]map[string]map[string]interface{}{
+ "us": {
+ "san-francisco": {"weather": "foggy", "temperature": 60},
+ "new-york": {"weather": "cloudy", "temperature": 55},
+ },
+ "jp": {
+ "tokyo": {"weather": "rainy", "temperature": 65},
+ "osaka": {"weather": "clear", "temperature": 72},
+ },
+ }
+ data, exists := weatherData[country][city]
+ if !exists {
+ data = map[string]interface{}{"weather": "sunny", "temperature": 70}
+ }
+ c.JSON(http.StatusOK, ginfw.H{"country": country, "city": city, "weather": data["weather"], "temperature": data["temperature"]})
+ })
+
+ r.GET("/health", func(c *ginfw.Context) {
+ c.JSON(http.StatusOK, ginfw.H{"status": "ok"})
+ })
+
+ fmt.Printf(" Listening on http://localhost:%s\n\n", DefaultPort)
+
+ if err := r.Run(":" + DefaultPort); err != nil {
+ fmt.Printf("Error starting server: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/examples/go/servers/echo/README.md b/examples/go/servers/echo/README.md
new file mode 100644
index 0000000000..08cebaf7dc
--- /dev/null
+++ b/examples/go/servers/echo/README.md
@@ -0,0 +1,224 @@
+# x402-echo Example Server
+
+Echo framework server demonstrating how to protect API endpoints with a paywall using the
+`x402/go/http/echo` middleware.
+
+## Prerequisites
+
+- Go 1.24 or higher
+- Valid EVM address for receiving payments
+- URL of a facilitator supporting the desired payment network, see [facilitator list](https://www.x402.org/ecosystem?category=facilitators)
+
+## Setup
+
+1. Copy `.env-example` to `.env`:
+
+```bash
+cp .env-example .env
+```
+
+and fill required environment variables:
+
+- `FACILITATOR_URL` - Facilitator endpoint URL
+- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments
+- `SVM_PAYEE_ADDRESS` - Solana address to receive payments
+
+2. Install dependencies:
+```bash
+go mod download
+```
+
+3. Run the server:
+```bash
+go run main.go
+```
+
+## Testing the Server
+
+You can test the server using one of the example clients:
+
+### Using the Go HTTP Client
+```bash
+cd ../../clients/http
+# Ensure .env is setup
+go run main.go
+```
+
+These clients will demonstrate how to:
+1. Make an initial request to get payment requirements
+2. Process the payment requirements
+3. Make a second request with the payment token
+
+## Example Endpoint
+
+The server includes a single example endpoint at `/weather` that accepts payment of 0.001 USDC on either Base Sepolia (EVM) or Solana Devnet (SVM). The endpoint returns a simple weather report.
+
+## Response Format
+
+### Payment Required (402)
+
+```
+HTTP/1.1 402 Payment Required
+Content-Type: application/json
+PAYMENT-REQUIRED:
+
+null
+```
+
+The `PAYMENT-REQUIRED` header contains base64-encoded JSON with the payment requirements.
+Note: `amount` is in atomic units (e.g., 1000 = 0.001 USDC, since USDC has 6 decimals):
+
+```json
+{
+ "x402Version": 2,
+ "error": "Payment required",
+ "resource": {
+ "url": "http://localhost:4021/weather",
+ "description": "Get weather data for a city",
+ "mimeType": "application/json"
+ },
+ "accepts": [
+ {
+ "scheme": "exact",
+ "network": "eip155:84532",
+ "amount": "1000",
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ "payTo": "0x...",
+ "maxTimeoutSeconds": 300,
+ "extra": {
+ "name": "USDC",
+ "version": "2",
+ "resourceUrl": "http://localhost:4021/weather"
+ }
+ },
+ {
+ "scheme": "exact",
+ "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ "amount": "1000",
+ "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
+ "payTo": "...",
+ "maxTimeoutSeconds": 300,
+ "extra": {
+ "feePayer": "...",
+ }
+ }
+ ]
+}
+```
+
+### Successful Response
+
+```
+HTTP/1.1 200 OK
+Content-Type: application/json
+PAYMENT-RESPONSE:
+
+{"city":"San Francisco","weather":"foggy","temperature":60,"timestamp":"2025-01-01T12:00:00Z"}
+```
+
+The `PAYMENT-RESPONSE` header contains base64-encoded JSON with the settlement details:
+
+```json
+{
+ "success": true,
+ "transaction": "0x...",
+ "network": "eip155:84532",
+ "payer": "0x..."
+}
+```
+
+## Extending the Example
+
+To add more paid endpoints, follow this pattern:
+
+```go
+// First, configure the payment middleware with your routes
+routes := x402http.RoutesConfig{
+ "GET /your-endpoint": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Price: "$0.10",
+ Network: x402.Network("eip155:84532"),
+ },
+ },
+ Description: "Your endpoint description",
+ MimeType: "application/json",
+ },
+}
+
+e := echo.New()
+
+// Apply x402 payment middleware
+e.Use(echomw.X402Payment(echomw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []echomw.SchemeConfig{
+ {Network: x402.Network("eip155:*"), Server: evm.NewExactEvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+}))
+
+// Then define your routes as normal
+e.GET("/your-endpoint", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ // Your response data
+ })
+})
+
+e.Start(":4021")
+```
+
+**Network identifiers** use [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) format, for example:
+- `eip155:84532` — Base Sepolia
+- `eip155:8453` — Base Mainnet
+- `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` — Solana Devnet
+- `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` — Solana Mainnet
+
+## x402ResourceServer Config
+
+The middleware uses scheme registrations to declare how payments for each network should be processed:
+
+```go
+e.Use(echomw.X402Payment(echomw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []echomw.SchemeConfig{
+ {Network: x402.Network("eip155:*"), Server: evm.NewExactEvmScheme()}, // All EVM chains
+ // {Network: x402.Network("solana:*"), Server: svm.NewExactSvmScheme()}, // All SVM chains
+ },
+ Timeout: 30 * time.Second,
+}))
+```
+
+## Facilitator Config
+
+The `HTTPFacilitatorClient` connects to a facilitator service that verifies and settles payments on-chain:
+
+```go
+facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+})
+
+// Or use multiple facilitators for redundancy
+facilitatorClients := []x402.FacilitatorClient{
+ x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{URL: primaryFacilitatorURL}),
+ x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{URL: backupFacilitatorURL}),
+}
+```
+
+## Next Steps
+
+See [Advanced Examples](../advanced/) for:
+- **Bazaar discovery** — make your API discoverable
+- **Dynamic pricing** — price based on request context
+- **Dynamic payTo** — route payments to different recipients
+- **Lifecycle hooks** — custom logic on verify/settle
+- **Custom tokens** — accept payments in custom tokens
+
+## Related Resources
+
+- [Echo Documentation](https://echo.labstack.com/docs)
+- [x402 Go Package Documentation](../../../../go/)
+- [Client Examples](../../clients/) — build clients that can make paid requests
diff --git a/examples/go/servers/echo/go.mod b/examples/go/servers/echo/go.mod
new file mode 100644
index 0000000000..4c09ee7ccd
--- /dev/null
+++ b/examples/go/servers/echo/go.mod
@@ -0,0 +1,65 @@
+module github.com/coinbase/x402/examples/go/servers/echo
+
+go 1.24.0
+
+toolchain go1.24.1
+
+replace github.com/coinbase/x402/go => ../../../../go
+
+require (
+ github.com/coinbase/x402/go v0.0.0
+ github.com/joho/godotenv v1.5.1
+ github.com/labstack/echo/v4 v4.15.1
+)
+
+require (
+ filippo.io/edwards25519 v1.0.0-rc.1 // indirect
+ github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
+ github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
+ github.com/bits-and-blooms/bitset v1.20.0 // indirect
+ github.com/blendle/zapdriver v1.3.1 // indirect
+ github.com/consensys/gnark-crypto v0.18.0 // indirect
+ github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
+ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
+ github.com/ethereum/go-ethereum v1.16.7 // indirect
+ github.com/ethereum/go-verkle v0.2.2 // indirect
+ github.com/fatih/color v1.16.0 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
+ github.com/gagliardetto/solana-go v1.14.0 // indirect
+ github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/holiman/uint256 v1.3.2 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.16.0 // indirect
+ github.com/labstack/gommon v0.4.2 // indirect
+ github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mitchellh/go-testing-interface v1.14.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect
+ github.com/mr-tron/base58 v1.2.0 // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
+ github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/valyala/fasttemplate v1.2.2 // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+ github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+ go.mongodb.org/mongo-driver v1.12.2 // indirect
+ go.uber.org/atomic v1.7.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ go.uber.org/ratelimit v0.2.0 // indirect
+ go.uber.org/zap v1.21.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
+ golang.org/x/net v0.48.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/term v0.38.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
+ golang.org/x/time v0.14.0 // indirect
+)
diff --git a/examples/go/servers/echo/go.sum b/examples/go/servers/echo/go.sum
new file mode 100644
index 0000000000..05bd271eff
--- /dev/null
+++ b/examples/go/servers/echo/go.sum
@@ -0,0 +1,243 @@
+filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
+filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
+github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=
+github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
+github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
+github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
+github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
+github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
+github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
+github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
+github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
+github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ=
+github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
+github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
+github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
+github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
+github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
+github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
+github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
+github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
+github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/solana-go v1.14.0 h1:3WfAi70jOOjAJ0deFMjdhFYlLXATF4tOQXsDNWJtOLw=
+github.com/gagliardetto/solana-go v1.14.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
+github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
+github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
+github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
+github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/labstack/echo/v4 v4.15.1 h1:S9keusg26gZpjMmPqB5hOEvNKnmd1lNmcHrbbH2lnFs=
+github.com/labstack/echo/v4 v4.15.1/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=
+github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
+github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
+github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
+github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
+github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
+github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
+github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
+github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
+github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk=
+github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE=
+github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
+github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
+github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
+github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.12.2 h1:gbWY1bJkkmUB9jjZzcdhOL8O85N9H+Vvsf2yFN0RDws=
+go.mongodb.org/mongo-driver v1.12.2/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
+go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
+go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
+golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/examples/go/servers/echo/main.go b/examples/go/servers/echo/main.go
new file mode 100644
index 0000000000..9c34a3998e
--- /dev/null
+++ b/examples/go/servers/echo/main.go
@@ -0,0 +1,153 @@
+package main
+
+import (
+ "fmt"
+ "net/http"
+ "os"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ echomw "github.com/coinbase/x402/go/http/echo"
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+ "github.com/joho/godotenv"
+ "github.com/labstack/echo/v4"
+)
+
+const (
+ DefaultPort = "4021"
+)
+
+func main() {
+ godotenv.Load()
+
+ evmAddress := os.Getenv("EVM_PAYEE_ADDRESS")
+ if evmAddress == "" {
+ fmt.Println("❌ EVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ svmAddress := os.Getenv("SVM_PAYEE_ADDRESS")
+ if svmAddress == "" {
+ fmt.Println("❌ SVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ facilitatorURL := os.Getenv("FACILITATOR_URL")
+ if facilitatorURL == "" {
+ fmt.Println("❌ FACILITATOR_URL environment variable is required")
+ fmt.Println(" Example: https://x402.org/facilitator")
+ os.Exit(1)
+ }
+
+ // Network configuration - Base Sepolia testnet
+ evmNetwork := x402.Network("eip155:84532")
+ svmNetwork := x402.Network("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1")
+
+ fmt.Printf("🚀 Starting Echo x402 server...\n")
+ fmt.Printf(" EVM Payee address: %s\n", evmAddress)
+ fmt.Printf(" SVM Payee address: %s\n", svmAddress)
+ fmt.Printf(" EVM Network: %s\n", evmNetwork)
+ fmt.Printf(" SVM Network: %s\n", svmNetwork)
+ fmt.Printf(" Facilitator: %s\n", facilitatorURL)
+
+ // Create Echo instance
+ e := echo.New()
+ e.HideBanner = true
+
+ // Create HTTP facilitator client
+ facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+ })
+
+ /**
+ * Configure x402 payment middleware
+ *
+ * This middleware protects specific routes with payment requirements.
+ * When a client accesses a protected route without payment, they receive
+ * a 402 Payment Required response with payment details.
+ */
+ routes := x402http.RoutesConfig{
+ "GET /weather": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ Price: "$0.001",
+ Network: "eip155:84532",
+ PayTo: evmAddress,
+ },
+ {
+ Scheme: "exact",
+ Price: "$0.001",
+ Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ PayTo: svmAddress,
+ },
+ },
+ Description: "Get weather data for a city",
+ MimeType: "application/json",
+ },
+ }
+
+ // Apply x402 payment middleware
+ e.Use(echomw.X402Payment(echomw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []echomw.SchemeConfig{
+ {Network: evmNetwork, Server: evm.NewExactEvmScheme()},
+ {Network: svmNetwork, Server: svm.NewExactSvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+ }))
+
+ /**
+ * Protected endpoint - requires $0.001 USDC payment
+ *
+ * Clients must provide a valid x402 payment to access this endpoint.
+ * The payment is verified and settled before the endpoint handler runs.
+ */
+ e.GET("/weather", func(c echo.Context) error {
+ city := c.QueryParam("city")
+ if city == "" {
+ city = "San Francisco"
+ }
+
+ weatherData := map[string]map[string]interface{}{
+ "San Francisco": {"weather": "foggy", "temperature": 60},
+ "New York": {"weather": "cloudy", "temperature": 55},
+ "London": {"weather": "rainy", "temperature": 50},
+ "Tokyo": {"weather": "clear", "temperature": 65},
+ }
+
+ data, exists := weatherData[city]
+ if !exists {
+ data = map[string]interface{}{"weather": "sunny", "temperature": 70}
+ }
+
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "city": city,
+ "weather": data["weather"],
+ "temperature": data["temperature"],
+ "timestamp": time.Now().Format(time.RFC3339),
+ })
+ })
+
+ /**
+ * Health check endpoint - no payment required
+ *
+ * This endpoint is not protected by x402 middleware.
+ */
+ e.GET("/health", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{
+ "status": "ok",
+ "version": "2.0.0",
+ })
+ })
+
+ fmt.Printf(" Server listening on http://localhost:%s\n\n", DefaultPort)
+
+ if err := e.Start(":" + DefaultPort); err != nil {
+ fmt.Printf("Error starting server: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/examples/go/servers/nethttp/README.md b/examples/go/servers/nethttp/README.md
new file mode 100644
index 0000000000..14d3d44a92
--- /dev/null
+++ b/examples/go/servers/nethttp/README.md
@@ -0,0 +1,226 @@
+# x402-nethttp Example Server
+
+Standard library `net/http` server demonstrating how to protect API endpoints with a paywall using the
+`x402/go/http/nethttp` middleware.
+
+## Prerequisites
+
+- Go 1.24 or higher
+- Valid EVM address for receiving payments
+- URL of a facilitator supporting the desired payment network, see [facilitator list](https://www.x402.org/ecosystem?category=facilitators)
+
+## Setup
+
+1. Copy `.env-example` to `.env`:
+
+```bash
+cp .env-example .env
+```
+
+and fill required environment variables:
+
+- `FACILITATOR_URL` - Facilitator endpoint URL
+- `EVM_PAYEE_ADDRESS` - Ethereum address to receive payments
+- `SVM_PAYEE_ADDRESS` - Solana address to receive payments
+
+2. Install dependencies:
+```bash
+go mod download
+```
+
+3. Run the server:
+```bash
+go run main.go
+```
+
+## Testing the Server
+
+You can test the server using one of the example clients:
+
+### Using the Go HTTP Client
+```bash
+cd ../../clients/http
+# Ensure .env is setup
+go run main.go
+```
+
+These clients will demonstrate how to:
+1. Make an initial request to get payment requirements
+2. Process the payment requirements
+3. Make a second request with the payment token
+
+## Example Endpoint
+
+The server includes a single example endpoint at `/weather` that accepts payment of 0.001 USDC on either Base Sepolia (EVM) or Solana Devnet (SVM). The endpoint returns a simple weather report.
+
+## Response Format
+
+### Payment Required (402)
+
+```
+HTTP/1.1 402 Payment Required
+Content-Type: application/json
+PAYMENT-REQUIRED:
+
+null
+```
+
+The `PAYMENT-REQUIRED` header contains base64-encoded JSON with the payment requirements.
+Note: `amount` is in atomic units (e.g., 1000 = 0.001 USDC, since USDC has 6 decimals):
+
+```json
+{
+ "x402Version": 2,
+ "error": "Payment required",
+ "resource": {
+ "url": "http://localhost:4021/weather",
+ "description": "Get weather data for a city",
+ "mimeType": "application/json"
+ },
+ "accepts": [
+ {
+ "scheme": "exact",
+ "network": "eip155:84532",
+ "amount": "1000",
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ "payTo": "0x...",
+ "maxTimeoutSeconds": 300,
+ "extra": {
+ "name": "USDC",
+ "version": "2",
+ "resourceUrl": "http://localhost:4021/weather"
+ }
+ },
+ {
+ "scheme": "exact",
+ "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ "amount": "1000",
+ "asset": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
+ "payTo": "...",
+ "maxTimeoutSeconds": 300,
+ "extra": {
+ "feePayer": "...",
+ }
+ }
+ ]
+}
+```
+
+### Successful Response
+
+```
+HTTP/1.1 200 OK
+Content-Type: application/json
+PAYMENT-RESPONSE:
+
+{"city":"San Francisco","weather":"foggy","temperature":60,"timestamp":"2025-01-01T12:00:00Z"}
+```
+
+The `PAYMENT-RESPONSE` header contains base64-encoded JSON with the settlement details:
+
+```json
+{
+ "success": true,
+ "transaction": "0x...",
+ "network": "eip155:84532",
+ "payer": "0x..."
+}
+```
+
+## Extending the Example
+
+To add more paid endpoints, follow this pattern:
+
+```go
+// First, configure the payment middleware with your routes
+routes := x402http.RoutesConfig{
+ "GET /your-endpoint": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: evmPayeeAddress,
+ Price: "$0.10",
+ Network: x402.Network("eip155:84532"),
+ },
+ },
+ Description: "Your endpoint description",
+ MimeType: "application/json",
+ },
+}
+
+mux := http.NewServeMux()
+
+// Define your routes
+mux.HandleFunc("GET /your-endpoint", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(map[string]interface{}{
+ // Your response data
+ })
+})
+
+// Apply x402 payment middleware
+handler := nethttpmw.X402Payment(nethttpmw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []nethttpmw.SchemeConfig{
+ {Network: x402.Network("eip155:*"), Server: evm.NewExactEvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+})(mux)
+
+http.ListenAndServe(":4021", handler)
+```
+
+**Network identifiers** use [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) format, for example:
+- `eip155:84532` — Base Sepolia
+- `eip155:8453` — Base Mainnet
+- `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` — Solana Devnet
+- `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` — Solana Mainnet
+
+## x402ResourceServer Config
+
+The middleware uses scheme registrations to declare how payments for each network should be processed:
+
+```go
+handler := nethttpmw.X402Payment(nethttpmw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []nethttpmw.SchemeConfig{
+ {Network: x402.Network("eip155:*"), Server: evm.NewExactEvmScheme()}, // All EVM chains
+ // {Network: x402.Network("solana:*"), Server: svm.NewExactSvmScheme()}, // All SVM chains
+ },
+ Timeout: 30 * time.Second,
+})(mux)
+```
+
+## Facilitator Config
+
+The `HTTPFacilitatorClient` connects to a facilitator service that verifies and settles payments on-chain:
+
+```go
+facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+})
+
+// Or use multiple facilitators for redundancy
+facilitatorClients := []x402.FacilitatorClient{
+ x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{URL: primaryFacilitatorURL}),
+ x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{URL: backupFacilitatorURL}),
+}
+```
+
+## Next Steps
+
+See [Advanced Examples](../advanced/) for:
+- **Bazaar discovery** — make your API discoverable
+- **Dynamic pricing** — price based on request context
+- **Dynamic payTo** — route payments to different recipients
+- **Lifecycle hooks** — custom logic on verify/settle
+- **Custom tokens** — accept payments in custom tokens
+
+## Related Resources
+
+- [net/http Documentation](https://pkg.go.dev/net/http)
+- [x402 Go Package Documentation](../../../../go/)
+- [Client Examples](../../clients/) — build clients that can make paid requests
diff --git a/examples/go/servers/nethttp/go.mod b/examples/go/servers/nethttp/go.mod
new file mode 100644
index 0000000000..cf8d3a11de
--- /dev/null
+++ b/examples/go/servers/nethttp/go.mod
@@ -0,0 +1,59 @@
+module github.com/coinbase/x402/examples/go/servers/nethttp
+
+go 1.24.0
+
+toolchain go1.24.1
+
+replace github.com/coinbase/x402/go => ../../../../go
+
+require (
+ github.com/coinbase/x402/go v0.0.0
+ github.com/joho/godotenv v1.5.1
+)
+
+require (
+ filippo.io/edwards25519 v1.0.0-rc.1 // indirect
+ github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
+ github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
+ github.com/bits-and-blooms/bitset v1.20.0 // indirect
+ github.com/blendle/zapdriver v1.3.1 // indirect
+ github.com/consensys/gnark-crypto v0.18.0 // indirect
+ github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
+ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
+ github.com/ethereum/go-ethereum v1.16.7 // indirect
+ github.com/ethereum/go-verkle v0.2.2 // indirect
+ github.com/fatih/color v1.16.0 // indirect
+ github.com/gagliardetto/binary v0.8.0 // indirect
+ github.com/gagliardetto/solana-go v1.14.0 // indirect
+ github.com/gagliardetto/treeout v0.1.4 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/holiman/uint256 v1.3.2 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.16.0 // indirect
+ github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mitchellh/go-testing-interface v1.14.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 // indirect
+ github.com/mr-tron/base58 v1.2.0 // indirect
+ github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect
+ github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+ github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+ go.mongodb.org/mongo-driver v1.12.2 // indirect
+ go.uber.org/atomic v1.7.0 // indirect
+ go.uber.org/multierr v1.6.0 // indirect
+ go.uber.org/ratelimit v0.2.0 // indirect
+ go.uber.org/zap v1.21.0 // indirect
+ golang.org/x/crypto v0.41.0 // indirect
+ golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/sys v0.36.0 // indirect
+ golang.org/x/term v0.34.0 // indirect
+ golang.org/x/time v0.9.0 // indirect
+)
diff --git a/examples/go/servers/nethttp/go.sum b/examples/go/servers/nethttp/go.sum
new file mode 100644
index 0000000000..f5e8398346
--- /dev/null
+++ b/examples/go/servers/nethttp/go.sum
@@ -0,0 +1,237 @@
+filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
+filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
+github.com/AlekSi/pointer v1.1.0 h1:SSDMPcXD9jSl8FPy9cRzoRaMJtm9g9ggGTxecRUbQoI=
+github.com/AlekSi/pointer v1.1.0/go.mod h1:y7BvfRI3wXPWKXEBhU71nbnIEEZX0QTSB2Bj48UJIZE=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI=
+github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
+github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
+github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
+github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
+github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
+github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
+github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
+github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
+github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
+github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ=
+github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
+github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
+github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
+github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
+github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
+github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
+github.com/gagliardetto/binary v0.8.0 h1:U9ahc45v9HW0d15LoN++vIXSJyqR/pWw8DDlhd7zvxg=
+github.com/gagliardetto/binary v0.8.0/go.mod h1:2tfj51g5o9dnvsc+fL3Jxr22MuWzYXwx9wEoN0XQ7/c=
+github.com/gagliardetto/gofuzz v1.2.2 h1:XL/8qDMzcgvR4+CyRQW9UGdwPRPMHVJfqQ/uMvSUuQw=
+github.com/gagliardetto/gofuzz v1.2.2/go.mod h1:bkH/3hYLZrMLbfYWA0pWzXmi5TTRZnu4pMGZBkqMKvY=
+github.com/gagliardetto/solana-go v1.14.0 h1:3WfAi70jOOjAJ0deFMjdhFYlLXATF4tOQXsDNWJtOLw=
+github.com/gagliardetto/solana-go v1.14.0/go.mod h1:l/qqqIN6qJJPtxW/G1PF4JtcE3Zg2vD2EliZrr9Gn5k=
+github.com/gagliardetto/treeout v0.1.4 h1:ozeYerrLCmCubo1TcIjFiOWTTGteOOHND1twdFpgwaw=
+github.com/gagliardetto/treeout v0.1.4/go.mod h1:loUefvXTrlRG5rYmJmExNryyBRh8f89VZhmMOyCyqok=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
+github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
+github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
+github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
+github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
+github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
+github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
+github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
+github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk=
+github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE=
+github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
+github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo=
+github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
+github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.12.2 h1:gbWY1bJkkmUB9jjZzcdhOL8O85N9H+Vvsf2yFN0RDws=
+go.mongodb.org/mongo-driver v1.12.2/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA=
+go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
+go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
+golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
+golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
+golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
+golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
+golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/examples/go/servers/nethttp/main.go b/examples/go/servers/nethttp/main.go
new file mode 100644
index 0000000000..71c683874a
--- /dev/null
+++ b/examples/go/servers/nethttp/main.go
@@ -0,0 +1,156 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "os"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ nethttpmw "github.com/coinbase/x402/go/http/nethttp"
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+ "github.com/joho/godotenv"
+)
+
+const (
+ DefaultPort = "4021"
+)
+
+func main() {
+ godotenv.Load()
+
+ evmAddress := os.Getenv("EVM_PAYEE_ADDRESS")
+ if evmAddress == "" {
+ fmt.Println("❌ EVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ svmAddress := os.Getenv("SVM_PAYEE_ADDRESS")
+ if svmAddress == "" {
+ fmt.Println("❌ SVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ facilitatorURL := os.Getenv("FACILITATOR_URL")
+ if facilitatorURL == "" {
+ fmt.Println("❌ FACILITATOR_URL environment variable is required")
+ fmt.Println(" Example: https://x402.org/facilitator")
+ os.Exit(1)
+ }
+
+ // Network configuration - Base Sepolia testnet
+ evmNetwork := x402.Network("eip155:84532")
+ svmNetwork := x402.Network("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1")
+
+ fmt.Printf("🚀 Starting net/http x402 server...\n")
+ fmt.Printf(" EVM Payee address: %s\n", evmAddress)
+ fmt.Printf(" SVM Payee address: %s\n", svmAddress)
+ fmt.Printf(" EVM Network: %s\n", evmNetwork)
+ fmt.Printf(" SVM Network: %s\n", svmNetwork)
+ fmt.Printf(" Facilitator: %s\n", facilitatorURL)
+
+ // Create HTTP facilitator client
+ facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+ })
+
+ /**
+ * Configure x402 payment middleware
+ *
+ * This middleware protects specific routes with payment requirements.
+ * When a client accesses a protected route without payment, they receive
+ * a 402 Payment Required response with payment details.
+ */
+ routes := x402http.RoutesConfig{
+ "GET /weather": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ Price: "$0.001",
+ Network: "eip155:84532",
+ PayTo: evmAddress,
+ },
+ {
+ Scheme: "exact",
+ Price: "$0.001",
+ Network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ PayTo: svmAddress,
+ },
+ },
+ Description: "Get weather data for a city",
+ MimeType: "application/json",
+ },
+ }
+
+ // Create ServeMux and register handlers
+ mux := http.NewServeMux()
+
+ /**
+ * Protected endpoint - requires $0.001 USDC payment
+ *
+ * Clients must provide a valid x402 payment to access this endpoint.
+ * The payment is verified and settled before the endpoint handler runs.
+ */
+ mux.HandleFunc("GET /weather", func(w http.ResponseWriter, r *http.Request) {
+ city := r.URL.Query().Get("city")
+ if city == "" {
+ city = "San Francisco"
+ }
+
+ weatherData := map[string]map[string]interface{}{
+ "San Francisco": {"weather": "foggy", "temperature": 60},
+ "New York": {"weather": "cloudy", "temperature": 55},
+ "London": {"weather": "rainy", "temperature": 50},
+ "Tokyo": {"weather": "clear", "temperature": 65},
+ }
+
+ data, exists := weatherData[city]
+ if !exists {
+ data = map[string]interface{}{"weather": "sunny", "temperature": 70}
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _ = json.NewEncoder(w).Encode(map[string]interface{}{
+ "city": city,
+ "weather": data["weather"],
+ "temperature": data["temperature"],
+ "timestamp": time.Now().Format(time.RFC3339),
+ })
+ })
+
+ /**
+ * Health check endpoint - no payment required
+ *
+ * This endpoint is not protected by x402 middleware.
+ */
+ mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ _ = json.NewEncoder(w).Encode(map[string]interface{}{
+ "status": "ok",
+ "version": "2.0.0",
+ })
+ })
+
+ // Apply x402 payment middleware
+ handler := nethttpmw.X402Payment(nethttpmw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []nethttpmw.SchemeConfig{
+ {Network: evmNetwork, Server: evm.NewExactEvmScheme()},
+ {Network: svmNetwork, Server: svm.NewExactSvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+ })(mux)
+
+ fmt.Printf(" Server listening on http://localhost:%s\n\n", DefaultPort)
+
+ if err := http.ListenAndServe(":"+DefaultPort, handler); err != nil {
+ fmt.Printf("Error starting server: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/examples/go/servers/upto/go.mod b/examples/go/servers/upto/go.mod
new file mode 100644
index 0000000000..dceea77cf5
--- /dev/null
+++ b/examples/go/servers/upto/go.mod
@@ -0,0 +1,60 @@
+module github.com/coinbase/x402/examples/go/servers/upto
+
+go 1.24.0
+
+toolchain go1.24.1
+
+replace github.com/coinbase/x402/go => ../../../../go
+
+require (
+ github.com/coinbase/x402/go v0.0.0-00010101000000-000000000000
+ github.com/gin-gonic/gin v1.11.0
+ github.com/joho/godotenv v1.5.1
+)
+
+require (
+ github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
+ github.com/bits-and-blooms/bitset v1.20.0 // indirect
+ github.com/bytedance/sonic v1.14.0 // indirect
+ github.com/bytedance/sonic/loader v0.3.0 // indirect
+ github.com/cloudwego/base64x v0.1.6 // indirect
+ github.com/consensys/gnark-crypto v0.18.0 // indirect
+ github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
+ github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
+ github.com/ethereum/go-ethereum v1.16.7 // indirect
+ github.com/ethereum/go-verkle v0.2.2 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.8 // indirect
+ github.com/gin-contrib/sse v1.1.0 // indirect
+ github.com/go-playground/locales v0.14.1 // indirect
+ github.com/go-playground/universal-translator v0.18.1 // indirect
+ github.com/go-playground/validator/v10 v10.27.0 // indirect
+ github.com/goccy/go-json v0.10.4 // indirect
+ github.com/goccy/go-yaml v1.18.0 // indirect
+ github.com/holiman/uint256 v1.3.2 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/cpuid/v2 v2.3.0 // indirect
+ github.com/leodido/go-urn v1.4.0 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.4 // indirect
+ github.com/quic-go/qpack v0.5.1 // indirect
+ github.com/quic-go/quic-go v0.55.0 // indirect
+ github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
+ github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/ugorji/go/codec v1.3.0 // indirect
+ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
+ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
+ github.com/xeipuuv/gojsonschema v1.2.0 // indirect
+ golang.org/x/arch v0.20.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
+ golang.org/x/mod v0.30.0 // indirect
+ golang.org/x/net v0.48.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
+ golang.org/x/tools v0.39.0 // indirect
+ google.golang.org/protobuf v1.36.9 // indirect
+)
diff --git a/examples/go/servers/upto/go.sum b/examples/go/servers/upto/go.sum
new file mode 100644
index 0000000000..289a1ffb33
--- /dev/null
+++ b/examples/go/servers/upto/go.sum
@@ -0,0 +1,159 @@
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
+github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
+github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
+github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
+github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
+github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
+github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
+github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
+github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
+github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
+github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
+github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
+github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
+github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
+github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
+github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
+github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
+github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
+github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
+github.com/ethereum/go-ethereum v1.16.7 h1:qeM4TvbrWK0UC0tgkZ7NiRsmBGwsjqc64BHo20U59UQ=
+github.com/ethereum/go-ethereum v1.16.7/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
+github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
+github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
+github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
+github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
+github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
+github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
+github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
+github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
+github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
+github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
+github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
+github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
+github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
+github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
+github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
+github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
+github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
+github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
+github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
+github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk=
+github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
+github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
+github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
+github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
+go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
+golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
+golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
+golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
+golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
+google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
+google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/examples/go/servers/upto/main.go b/examples/go/servers/upto/main.go
new file mode 100644
index 0000000000..3a2c41f77a
--- /dev/null
+++ b/examples/go/servers/upto/main.go
@@ -0,0 +1,108 @@
+package main
+
+import (
+ "fmt"
+ "math/rand"
+ "net/http"
+ "os"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ ginmw "github.com/coinbase/x402/go/http/gin"
+ uptoevm "github.com/coinbase/x402/go/mechanisms/evm/upto/server"
+ ginfw "github.com/gin-gonic/gin"
+ "github.com/joho/godotenv"
+)
+
+const DefaultPort = "4021"
+
+func main() {
+ godotenv.Load()
+
+ evmAddress := os.Getenv("EVM_PAYEE_ADDRESS")
+ if evmAddress == "" {
+ fmt.Println("EVM_PAYEE_ADDRESS environment variable is required")
+ os.Exit(1)
+ }
+
+ facilitatorURL := os.Getenv("FACILITATOR_URL")
+ if facilitatorURL == "" {
+ fmt.Println("FACILITATOR_URL environment variable is required")
+ os.Exit(1)
+ }
+
+ evmNetwork := x402.Network("eip155:84532")
+
+ fmt.Printf("Starting Gin x402 upto server...\n")
+ fmt.Printf(" EVM Payee address: %s\n", evmAddress)
+ fmt.Printf(" EVM Network: %s\n", evmNetwork)
+ fmt.Printf(" Facilitator: %s\n", facilitatorURL)
+
+ r := ginfw.Default()
+
+ facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+ })
+
+ maxPrice := "$0.10" // Maximum the client authorizes
+
+ routes := x402http.RoutesConfig{
+ "GET /api/generate": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "upto",
+ Price: maxPrice,
+ Network: evmNetwork,
+ PayTo: evmAddress,
+ },
+ },
+ Description: "AI text generation - billed by token usage",
+ MimeType: "application/json",
+ },
+ }
+
+ r.Use(ginmw.X402Payment(ginmw.Config{
+ Routes: routes,
+ Facilitator: facilitatorClient,
+ Schemes: []ginmw.SchemeConfig{
+ {Network: evmNetwork, Server: uptoevm.NewUptoEvmScheme()},
+ },
+ Timeout: 30 * time.Second,
+ }))
+
+ r.GET("/api/generate", func(c *ginfw.Context) {
+ // Simulate work that produces a variable cost.
+ // In production this might be LLM token count, bytes served, compute time, etc.
+ maxAmountAtomic := 100000 // 10 cents in 6-decimal USDC atomic units
+ actualUsage := rand.Intn(maxAmountAtomic + 1)
+
+ // Tell the middleware to settle only what was actually used.
+ ginmw.SetSettlementOverrides(c, &x402.SettlementOverrides{
+ Amount: fmt.Sprintf("%d", actualUsage),
+ })
+
+ c.JSON(http.StatusOK, ginfw.H{
+ "result": "Here is your generated text...",
+ "usage": ginfw.H{
+ "authorizedMaxAtomic": fmt.Sprintf("%d", maxAmountAtomic),
+ "actualChargedAtomic": fmt.Sprintf("%d", actualUsage),
+ },
+ })
+ })
+
+ r.GET("/health", func(c *ginfw.Context) {
+ c.JSON(http.StatusOK, ginfw.H{
+ "status": "ok",
+ "version": "2.0.0",
+ })
+ })
+
+ fmt.Printf(" Server listening on http://localhost:%s\n\n", DefaultPort)
+ fmt.Println(" GET /api/generate - usage-based billing via upto scheme")
+
+ if err := r.Run(":" + DefaultPort); err != nil {
+ fmt.Printf("Error starting server: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/examples/python/clients/advanced/all_networks.py b/examples/python/clients/advanced/all_networks.py
index 0305161e72..fa4158d010 100644
--- a/examples/python/clients/advanced/all_networks.py
+++ b/examples/python/clients/advanced/all_networks.py
@@ -93,18 +93,13 @@ async def main() -> None:
print(f"Response body: {response.text}")
# Extract and print payment response if present
- if response.is_success:
- try:
- settle_response = http_client.get_payment_settle_response(
- lambda name: response.headers.get(name)
- )
- print(
- f"\nPayment response: {settle_response.model_dump_json(indent=2)}"
- )
- except ValueError:
- print("\nNo payment response header found")
- else:
- print(f"\nRequest failed (status: {response.status_code})")
+ try:
+ settle_response = http_client.get_payment_settle_response(
+ lambda name: response.headers.get(name)
+ )
+ print(f"\nPayment response: {settle_response.model_dump_json(indent=2)}")
+ except ValueError:
+ print("\nNo payment response header found")
if __name__ == "__main__":
diff --git a/examples/python/clients/advanced/builder_pattern.py b/examples/python/clients/advanced/builder_pattern.py
index 80290c7184..88de409ce9 100644
--- a/examples/python/clients/advanced/builder_pattern.py
+++ b/examples/python/clients/advanced/builder_pattern.py
@@ -92,16 +92,14 @@ async def run_builder_pattern_example(
print(f"Response status: {response.status_code}")
print(f"Response body: {response.text}")
- if response.is_success:
- try:
- settle_response = http_client.get_payment_settle_response(
- lambda name: response.headers.get(name)
- )
- print(
- f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}"
- )
- except ValueError:
- print("\nNo payment response header found")
+ # Extract and print payment response if present
+ try:
+ settle_response = http_client.get_payment_settle_response(
+ lambda name: response.headers.get(name)
+ )
+ print(f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}")
+ except ValueError:
+ print("\nNo payment response header found")
async def main() -> None:
diff --git a/examples/python/clients/advanced/hooks.py b/examples/python/clients/advanced/hooks.py
index 633c1c2658..1ce5a73bd4 100644
--- a/examples/python/clients/advanced/hooks.py
+++ b/examples/python/clients/advanced/hooks.py
@@ -122,16 +122,14 @@ async def run_hooks_example(private_key: str, url: str) -> None:
print(f"Response status: {response.status_code}")
print(f"Response body: {response.text}")
- if response.is_success:
- try:
- settle_response = http_client.get_payment_settle_response(
- lambda name: response.headers.get(name)
- )
- print(
- f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}"
- )
- except ValueError:
- print("\nNo payment response header found")
+ # Extract and print payment response if present
+ try:
+ settle_response = http_client.get_payment_settle_response(
+ lambda name: response.headers.get(name)
+ )
+ print(f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}")
+ except ValueError:
+ print("\nNo payment response header found")
async def main() -> None:
diff --git a/examples/python/clients/advanced/preferred_network.py b/examples/python/clients/advanced/preferred_network.py
index 3cb7a42471..498aef00ac 100644
--- a/examples/python/clients/advanced/preferred_network.py
+++ b/examples/python/clients/advanced/preferred_network.py
@@ -129,16 +129,14 @@ async def run_preferred_network_example(
print(f"\nResponse status: {response.status_code}")
print(f"Response body: {response.text}")
- if response.is_success:
- try:
- settle_response = http_client.get_payment_settle_response(
- lambda name: response.headers.get(name)
- )
- print(
- f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}"
- )
- except ValueError:
- print("\nNo payment response header found")
+ # Extract and print payment response if present
+ try:
+ settle_response = http_client.get_payment_settle_response(
+ lambda name: response.headers.get(name)
+ )
+ print(f"\n💰 Payment Details: {settle_response.model_dump_json(indent=2)}")
+ except ValueError:
+ print("\nNo payment response header found")
async def main() -> None:
diff --git a/examples/python/clients/httpx/main.py b/examples/python/clients/httpx/main.py
index b8e8ad0aaf..d0a6c26099 100644
--- a/examples/python/clients/httpx/main.py
+++ b/examples/python/clients/httpx/main.py
@@ -85,18 +85,13 @@ async def main() -> None:
print(f"Response body: {response.text}")
# Extract and print payment response if present
- if response.is_success:
- try:
- settle_response = http_client.get_payment_settle_response(
- lambda name: response.headers.get(name)
- )
- print(
- f"\nPayment response: {settle_response.model_dump_json(indent=2)}"
- )
- except ValueError:
- print("\nNo payment response header found")
- else:
- print(f"\nRequest failed (status: {response.status_code})")
+ try:
+ settle_response = http_client.get_payment_settle_response(
+ lambda name: response.headers.get(name)
+ )
+ print(f"\nPayment response: {settle_response.model_dump_json(indent=2)}")
+ except ValueError:
+ print("\nNo payment response header found")
if __name__ == "__main__":
diff --git a/examples/python/clients/httpx/uv.lock b/examples/python/clients/httpx/uv.lock
index 8316ef3fec..b5b3fbfc6b 100644
--- a/examples/python/clients/httpx/uv.lock
+++ b/examples/python/clients/httpx/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -1744,7 +1744,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.1.0"
+version = "2.2.0"
source = { editable = "../../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
diff --git a/examples/python/clients/payment-identifier/main.py b/examples/python/clients/payment-identifier/main.py
index 8de03d540a..a7238d45fc 100644
--- a/examples/python/clients/payment-identifier/main.py
+++ b/examples/python/clients/payment-identifier/main.py
@@ -82,14 +82,14 @@ async def before_payment_creation(context: PaymentCreationContext) -> None:
print(f"Response ({duration1}ms): {response1.text}")
- if response1.is_success:
- try:
- settle_response = http_client.get_payment_settle_response(
- lambda name: response1.headers.get(name)
- )
- print(f"\nPayment settled on {settle_response.network}")
- except ValueError:
- pass
+ # Extract and print payment response if present
+ try:
+ settle_response = http_client.get_payment_settle_response(
+ lambda name: response1.headers.get(name)
+ )
+ print(f"\nPayment response: {settle_response.model_dump_json(indent=2)}")
+ except ValueError:
+ pass
# Second request - same payment ID, should return from cache
print("\n" + "=" * 52)
@@ -105,14 +105,13 @@ async def before_payment_creation(context: PaymentCreationContext) -> None:
print(f"Response ({duration2}ms): {response2.text}")
- if response2.is_success:
- try:
- settle_response = http_client.get_payment_settle_response(
- lambda name: response2.headers.get(name)
- )
- print("\nPayment settled (unexpected - should have been cached)")
- except ValueError:
- print("\nNo payment processed - response served from cache!")
+ try:
+ settle_response = http_client.get_payment_settle_response(
+ lambda name: response2.headers.get(name)
+ )
+ print(f"\nPayment response: {settle_response.model_dump_json(indent=2)}")
+ except ValueError:
+ print("\nNo payment processed - response served from cache!")
# Summary
print("\n" + "=" * 52)
diff --git a/examples/python/clients/requests/main.py b/examples/python/clients/requests/main.py
index 9447f15d8a..f3e88758ce 100644
--- a/examples/python/clients/requests/main.py
+++ b/examples/python/clients/requests/main.py
@@ -83,18 +83,13 @@ def main() -> None:
print(f"Response body: {response.text}")
# Extract and print payment response if present
- if response.ok: # requests uses .ok
- try:
- settle_response = http_client.get_payment_settle_response(
- lambda name: response.headers.get(name)
- )
- print(
- f"\nPayment response: {settle_response.model_dump_json(indent=2)}"
- )
- except ValueError:
- print("\nNo payment response header found")
- else:
- print(f"\nRequest failed (status: {response.status_code})")
+ try:
+ settle_response = http_client.get_payment_settle_response(
+ lambda name: response.headers.get(name)
+ )
+ print(f"\nPayment response: {settle_response.model_dump_json(indent=2)}")
+ except ValueError:
+ print("\nNo payment response header found")
if __name__ == "__main__":
diff --git a/examples/python/clients/requests/uv.lock b/examples/python/clients/requests/uv.lock
index 674ea277a6..0a7f7621cc 100644
--- a/examples/python/clients/requests/uv.lock
+++ b/examples/python/clients/requests/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -1744,7 +1744,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.1.0"
+version = "2.2.0"
source = { editable = "../../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
diff --git a/examples/python/facilitator/advanced/all_networks.py b/examples/python/facilitator/advanced/all_networks.py
index e78078663b..2cc72f2656 100644
--- a/examples/python/facilitator/advanced/all_networks.py
+++ b/examples/python/facilitator/advanced/all_networks.py
@@ -145,11 +145,7 @@ async def verify(request: VerifyRequest):
# Verify payment (await async method)
response = await facilitator.verify(payload, requirements)
- return {
- "isValid": response.is_valid,
- "payer": response.payer,
- "invalidReason": response.invalid_reason,
- }
+ return response.model_dump(by_alias=True, exclude_none=True)
except Exception as e:
print(f"Verify error: {e}")
raise HTTPException(status_code=500, detail=str(e)) from e
@@ -175,24 +171,21 @@ async def settle(request: SettleRequest):
# Settle payment (await async method)
response = await facilitator.settle(payload, requirements)
- return {
- "success": response.success,
- "transaction": response.transaction,
- "network": response.network,
- "payer": response.payer,
- "errorReason": response.error_reason,
- }
+ return response.model_dump(by_alias=True, exclude_none=True)
except Exception as e:
print(f"Settle error: {e}")
# Check if this was an abort from hook
if "aborted" in str(e).lower():
- return {
- "success": False,
- "errorReason": str(e),
- "network": request.paymentPayload.get("accepted", {}).get("network", "unknown"),
- "transaction": "",
- }
+ from x402.schemas import SettleResponse
+
+ abort = SettleResponse(
+ success=False,
+ error_reason=str(e),
+ network=request.paymentPayload.get("accepted", {}).get("network", "unknown"),
+ transaction="",
+ )
+ return abort.model_dump(by_alias=True, exclude_none=True)
raise HTTPException(status_code=500, detail=str(e)) from e
@@ -208,15 +201,7 @@ async def supported():
response = facilitator.get_supported()
return {
- "kinds": [
- {
- "x402Version": k.x402_version,
- "scheme": k.scheme,
- "network": k.network,
- "extra": k.extra,
- }
- for k in response.kinds
- ],
+ "kinds": [k.model_dump(by_alias=True, exclude_none=True) for k in response.kinds],
"extensions": response.extensions,
"signers": response.signers,
}
diff --git a/examples/python/facilitator/advanced/bazaar.py b/examples/python/facilitator/advanced/bazaar.py
index 3c18fadf30..807a82e7f5 100644
--- a/examples/python/facilitator/advanced/bazaar.py
+++ b/examples/python/facilitator/advanced/bazaar.py
@@ -205,11 +205,7 @@ async def verify(request: VerifyRequest):
# - Extract and catalog discovery info (on_after_verify)
response = await facilitator.verify(payload, requirements)
- return {
- "isValid": response.is_valid,
- "payer": response.payer,
- "invalidReason": response.invalid_reason,
- }
+ return response.model_dump(by_alias=True, exclude_none=True)
except Exception as e:
print(f"Verify error: {e}")
raise HTTPException(status_code=500, detail=str(e)) from e
@@ -234,25 +230,21 @@ async def settle(request: SettleRequest):
response = await facilitator.settle(payload, requirements)
- return {
- "success": response.success,
- "transaction": response.transaction,
- "network": response.network,
- "payer": response.payer,
- "errorReason": response.error_reason,
- }
+ return response.model_dump(by_alias=True, exclude_none=True)
except Exception as e:
print(f"Settle error: {e}")
# Check if this was an abort from hook
if "aborted" in str(e).lower() or "Settlement aborted" in str(e):
- return {
- "success": False,
- "errorReason": str(e).replace("Settlement aborted: ", ""),
- "network": request.paymentPayload.get("accepted", {}).get("network", "unknown"),
- "transaction": "",
- "payer": None,
- }
+ from x402.schemas import SettleResponse
+
+ abort = SettleResponse(
+ success=False,
+ error_reason=str(e).replace("Settlement aborted: ", ""),
+ network=request.paymentPayload.get("accepted", {}).get("network", "unknown"),
+ transaction="",
+ )
+ return abort.model_dump(by_alias=True, exclude_none=True)
raise HTTPException(status_code=500, detail=str(e)) from e
@@ -268,15 +260,7 @@ async def supported():
response = facilitator.get_supported()
return {
- "kinds": [
- {
- "x402Version": k.x402_version,
- "scheme": k.scheme,
- "network": k.network,
- "extra": k.extra,
- }
- for k in response.kinds
- ],
+ "kinds": [k.model_dump(by_alias=True, exclude_none=True) for k in response.kinds],
"extensions": response.extensions,
"signers": response.signers,
}
diff --git a/examples/python/facilitator/basic/main.py b/examples/python/facilitator/basic/main.py
index 66197be635..4f71e39a37 100644
--- a/examples/python/facilitator/basic/main.py
+++ b/examples/python/facilitator/basic/main.py
@@ -145,11 +145,7 @@ async def verify(request: VerifyRequest):
# Verify payment (await async method)
response = await facilitator.verify(payload, requirements)
- return {
- "isValid": response.is_valid,
- "payer": response.payer,
- "invalidReason": response.invalid_reason,
- }
+ return response.model_dump(by_alias=True, exclude_none=True)
except Exception as e:
print(f"Verify error: {e}")
raise HTTPException(status_code=500, detail=str(e)) from e
@@ -175,24 +171,21 @@ async def settle(request: SettleRequest):
# Settle payment (await async method)
response = await facilitator.settle(payload, requirements)
- return {
- "success": response.success,
- "transaction": response.transaction,
- "network": response.network,
- "payer": response.payer,
- "errorReason": response.error_reason,
- }
+ return response.model_dump(by_alias=True, exclude_none=True)
except Exception as e:
print(f"Settle error: {e}")
# Check if this was an abort from hook
if "aborted" in str(e).lower():
- return {
- "success": False,
- "errorReason": str(e),
- "network": request.paymentPayload.get("accepted", {}).get("network", "unknown"),
- "transaction": "",
- }
+ from x402.schemas import SettleResponse
+
+ abort = SettleResponse(
+ success=False,
+ error_reason=str(e),
+ network=request.paymentPayload.get("accepted", {}).get("network", "unknown"),
+ transaction="",
+ )
+ return abort.model_dump(by_alias=True, exclude_none=True)
raise HTTPException(status_code=500, detail=str(e)) from e
@@ -208,15 +201,7 @@ async def supported():
response = facilitator.get_supported()
return {
- "kinds": [
- {
- "x402Version": k.x402_version,
- "scheme": k.scheme,
- "network": k.network,
- "extra": k.extra,
- }
- for k in response.kinds
- ],
+ "kinds": [k.model_dump(by_alias=True, exclude_none=True) for k in response.kinds],
"extensions": response.extensions,
"signers": response.signers,
}
diff --git a/examples/python/facilitator/basic/uv.lock b/examples/python/facilitator/basic/uv.lock
index c6a3f7d852..78300b2b72 100644
--- a/examples/python/facilitator/basic/uv.lock
+++ b/examples/python/facilitator/basic/uv.lock
@@ -1,5 +1,5 @@
version = 1
-revision = 3
+revision = 2
requires-python = ">=3.10"
[[package]]
@@ -2976,7 +2976,7 @@ wheels = [
[[package]]
name = "x402"
-version = "2.1.0"
+version = "2.5.0"
source = { editable = "../../../../python/x402" }
dependencies = [
{ name = "nest-asyncio" },
diff --git a/examples/python/servers/bazaar/.env-local b/examples/python/servers/bazaar/.env-local
new file mode 100644
index 0000000000..aeea0693ab
--- /dev/null
+++ b/examples/python/servers/bazaar/.env-local
@@ -0,0 +1,3 @@
+EVM_ADDRESS=
+SVM_ADDRESS=
+FACILITATOR_URL=https://x402.org/facilitator
diff --git a/examples/python/servers/bazaar/README.md b/examples/python/servers/bazaar/README.md
new file mode 100644
index 0000000000..2f0efac9f7
--- /dev/null
+++ b/examples/python/servers/bazaar/README.md
@@ -0,0 +1,139 @@
+# Bazaar Discovery Example Server (Python)
+
+FastAPI server demonstrating how to make a paid API **discoverable** using the Bazaar extension with dynamic route parameters.
+
+The key addition over a basic x402 server is `declare_discovery_extension` -- it describes your endpoint's inputs, outputs, and path parameters so that facilitators (and agents) can automatically catalog and invoke your API.
+
+## What This Example Shows
+
+**Dynamic route parameters** -- the route `GET /weather/:city` uses a `:city` slug. The x402 middleware automatically:
+
+1. Matches `/weather/san-francisco`, `/weather/tokyo`, etc. against the route pattern
+2. Extracts `{ city: "san-francisco" }` as `pathParams` in the discovery extension
+3. Produces `routeTemplate: "/weather/:city"` so all concrete URLs consolidate into **one** catalog entry
+
+```python
+from x402.extensions.bazaar import declare_discovery_extension, OutputConfig
+
+routes = {
+ "GET /weather/:city": RouteConfig(
+ accepts=[
+ PaymentOption(scheme="exact", price="$0.01", network=EVM_NETWORK, pay_to=EVM_ADDRESS),
+ ],
+ description="Weather data for a city",
+ extensions=declare_discovery_extension(
+ path_params_schema={
+ "properties": {"city": {"type": "string", "description": "City name slug"}},
+ "required": ["city"],
+ },
+ output=OutputConfig(
+ example={"city": "san-francisco", "weather": "foggy", "temperature": 60}
+ ),
+ ),
+ ),
+}
+
+@app.get("/weather/{city}")
+async def get_weather(city: str) -> dict:
+ ...
+```
+
+Note that the x402 route key uses `:city` (Express convention) while the FastAPI handler uses `{city}` (FastAPI convention). The x402 middleware handles the `:city` matching; FastAPI handles the `{city}` extraction for your handler.
+
+## Prerequisites
+
+- Python 3.10+
+- uv (install via [docs.astral.sh/uv](https://docs.astral.sh/uv/getting-started/installation/))
+- Valid EVM address for receiving payments (Base Sepolia)
+- Valid SVM address for receiving payments (Solana Devnet)
+- URL of a facilitator supporting the desired payment network, see [facilitator list](https://www.x402.org/ecosystem?category=facilitators)
+
+## Setup
+
+1. Copy `.env-local` to `.env`:
+
+```bash
+cp .env-local .env
+```
+
+2. Fill required environment variables:
+
+- `EVM_ADDRESS` - Ethereum address to receive payments (Base Sepolia)
+- `SVM_ADDRESS` - Solana address to receive payments (Solana Devnet)
+- `FACILITATOR_URL` - Facilitator endpoint URL (optional, defaults to production)
+
+3. Install dependencies:
+
+```bash
+uv sync
+```
+
+4. Run the server:
+
+```bash
+uv run python main.py
+```
+
+Server runs at http://localhost:4021
+
+## How Discovery Works
+
+When a client hits `GET /weather/san-francisco` without a payment, the 402 response includes the enriched bazaar extension:
+
+```json
+{
+ "x402Version": 2,
+ "error": "Payment required",
+ "resource": { "url": "http://localhost:4021/weather/san-francisco" },
+ "extensions": {
+ "bazaar": {
+ "routeTemplate": "/weather/:city",
+ "info": {
+ "input": {
+ "type": "http",
+ "method": "GET",
+ "pathParams": { "city": "san-francisco" }
+ },
+ "output": {
+ "type": "json",
+ "example": { "city": "san-francisco", "weather": "foggy", "temperature": 60 }
+ }
+ }
+ }
+ },
+ "accepts": [{ "..." : "..." }]
+}
+```
+
+The facilitator uses `routeTemplate` as the canonical catalog key, so requests to `/weather/san-francisco`, `/weather/tokyo`, and `/weather/new-york` all map to a single discoverable endpoint: `/weather/:city`.
+
+## Example Endpoints
+
+| Endpoint | Payment | Price |
+|----------|---------|-------|
+| `GET /health` | No | - |
+| `GET /weather/:city` | Yes | $0.01 USDC |
+| `GET /weather/:country/:city` | Yes | $0.01 USDC |
+
+## Multiple Path Parameters
+
+Routes can have multiple `:param` segments. Param names are matched by **position in the URL**, not by the order they appear in `path_params_schema`:
+
+```
+GET /weather/:country/:city
+ ^ ^
+ | └── second URL segment -> "city"
+ └──────────── first URL segment -> "country"
+```
+
+A request to `/weather/us/san-francisco` produces `pathParams: { country: "us", city: "san-francisco" }`. The property order in `path_params_schema` does not affect matching -- only the segment position in the URL matters.
+
+## `declare_discovery_extension` API
+
+| Parameter | Purpose |
+|-----------|---------|
+| `input` | Example query parameter values (for GET/HEAD/DELETE) |
+| `input_schema` | JSON Schema for query parameters |
+| `path_params_schema` | JSON Schema for URL path parameters (`:param` segments) |
+| `output` | `OutputConfig(example=...)` -- example response body |
+| `body_type` | For POST/PUT/PATCH: `"json"`, `"form-data"`, or `"text"` |
diff --git a/examples/python/servers/bazaar/main.py b/examples/python/servers/bazaar/main.py
new file mode 100644
index 0000000000..cde583132e
--- /dev/null
+++ b/examples/python/servers/bazaar/main.py
@@ -0,0 +1,124 @@
+import os
+
+from dotenv import load_dotenv
+from fastapi import FastAPI
+
+from x402.extensions.bazaar import OutputConfig, declare_discovery_extension
+from x402.http import FacilitatorConfig, HTTPFacilitatorClient, PaymentOption
+from x402.http.middleware.fastapi import PaymentMiddlewareASGI
+from x402.http.types import RouteConfig
+from x402.mechanisms.evm.exact import ExactEvmServerScheme
+from x402.mechanisms.svm.exact import ExactSvmServerScheme
+from x402.schemas import Network
+from x402.server import x402ResourceServer
+
+load_dotenv()
+
+# Config
+EVM_ADDRESS = os.getenv("EVM_ADDRESS")
+SVM_ADDRESS = os.getenv("SVM_ADDRESS")
+EVM_NETWORK: Network = "eip155:84532" # Base Sepolia
+SVM_NETWORK: Network = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1" # Solana Devnet
+FACILITATOR_URL = os.getenv("FACILITATOR_URL", "https://x402.org/facilitator")
+
+if not EVM_ADDRESS or not SVM_ADDRESS:
+ raise ValueError("Missing required environment variables")
+
+
+# App
+app = FastAPI()
+
+
+# x402 Middleware
+facilitator = HTTPFacilitatorClient(FacilitatorConfig(url=FACILITATOR_URL))
+server = x402ResourceServer(facilitator)
+server.register(EVM_NETWORK, ExactEvmServerScheme())
+server.register(SVM_NETWORK, ExactSvmServerScheme())
+
+payment_options = [
+ PaymentOption(scheme="exact", pay_to=EVM_ADDRESS, price="$0.01", network=EVM_NETWORK),
+ PaymentOption(scheme="exact", pay_to=SVM_ADDRESS, price="$0.01", network=SVM_NETWORK),
+]
+
+routes = {
+ # Single path param: /weather/:city
+ "GET /weather/:city": RouteConfig(
+ accepts=payment_options,
+ mime_type="application/json",
+ description="Weather data for a city",
+ extensions=declare_discovery_extension(
+ path_params_schema={
+ "properties": {"city": {"type": "string", "description": "City name slug"}},
+ "required": ["city"],
+ },
+ output=OutputConfig(
+ example={"city": "san-francisco", "weather": "foggy", "temperature": 60}
+ ),
+ ),
+ ),
+ # Multiple path params: /weather/:country/:city
+ # Param names are matched by position in the URL, not by declaration order in the schema.
+ # /weather/us/san-francisco -> { country: "us", city: "san-francisco" }
+ "GET /weather/:country/:city": RouteConfig(
+ accepts=payment_options,
+ mime_type="application/json",
+ description="Weather data for a city in a specific country",
+ extensions=declare_discovery_extension(
+ path_params_schema={
+ "properties": {
+ "country": {"type": "string", "description": "Country code"},
+ "city": {"type": "string", "description": "City name slug"},
+ },
+ "required": ["country", "city"],
+ },
+ output=OutputConfig(
+ example={
+ "country": "us",
+ "city": "san-francisco",
+ "weather": "foggy",
+ "temperature": 60,
+ }
+ ),
+ ),
+ ),
+}
+app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
+
+
+# Routes
+@app.get("/health")
+async def health_check() -> dict[str, str]:
+ return {"status": "ok"}
+
+
+@app.get("/weather/{city}")
+async def get_weather(city: str) -> dict:
+ weather_data = {
+ "san-francisco": {"weather": "foggy", "temperature": 60},
+ "new-york": {"weather": "cloudy", "temperature": 55},
+ "tokyo": {"weather": "rainy", "temperature": 65},
+ }
+ data = weather_data.get(city, {"weather": "sunny", "temperature": 70})
+ return {"city": city, "weather": data["weather"], "temperature": data["temperature"]}
+
+
+@app.get("/weather/{country}/{city}")
+async def get_weather_by_country(country: str, city: str) -> dict:
+ weather_data: dict[str, dict[str, dict]] = {
+ "us": {
+ "san-francisco": {"weather": "foggy", "temperature": 60},
+ "new-york": {"weather": "cloudy", "temperature": 55},
+ },
+ "jp": {
+ "tokyo": {"weather": "rainy", "temperature": 65},
+ "osaka": {"weather": "clear", "temperature": 72},
+ },
+ }
+ data = weather_data.get(country, {}).get(city, {"weather": "sunny", "temperature": 70})
+ return {"country": country, "city": city, "weather": data["weather"], "temperature": data["temperature"]}
+
+
+if __name__ == "__main__":
+ import uvicorn
+
+ uvicorn.run(app, host="0.0.0.0", port=4021)
diff --git a/examples/python/servers/bazaar/pyproject.toml b/examples/python/servers/bazaar/pyproject.toml
new file mode 100644
index 0000000000..b76ba97a62
--- /dev/null
+++ b/examples/python/servers/bazaar/pyproject.toml
@@ -0,0 +1,60 @@
+[project]
+name = "x402-v2-bazaar-example"
+version = "0.1.0"
+description = "Example of using Bazaar discovery with dynamic routes in x402"
+readme = "README.md"
+authors = [
+ { name = "logan", email = "kcs93023@gmail.com" },
+]
+requires-python = ">=3.10"
+keywords = ["x402", "payment", "protocol", "http", "402"]
+dependencies = [
+ "x402[fastapi,evm,svm]",
+ "python-dotenv>=1.2.1",
+ "uvicorn[standard]>=0.40.0",
+]
+
+[dependency-groups]
+dev = [
+ "pytest>=7.0.0",
+ "pytest-asyncio>=0.21.0",
+ "mypy>=1.0.0",
+ "ruff>=0.1.0",
+ "black>=23.0.0",
+]
+
+[tool.uv]
+package = false
+
+[tool.uv.sources]
+x402 = { path = "../../../../python/x402", editable = true }
+
+[tool.ruff]
+line-length = 100
+target-version = "py310"
+
+[tool.ruff.lint]
+select = [
+ "E", # pycodestyle errors
+ "W", # pycodestyle warnings
+ "F", # pyflakes
+ "I", # isort
+ "B", # flake8-bugbear
+ "C4", # flake8-comprehensions
+ "UP", # pyupgrade
+]
+ignore = [
+ "E501", # line too long (handled by black)
+]
+
+[tool.ruff.lint.isort]
+known-first-party = ["x402"]
+
+[tool.mypy]
+python_version = "3.10"
+strict = true
+warn_return_any = true
+warn_unused_configs = true
+
+[tool.pytest.ini_options]
+testpaths = ["tests"]
diff --git a/examples/python/servers/bazaar/uv.lock b/examples/python/servers/bazaar/uv.lock
new file mode 100644
index 0000000000..4ca7418ccd
--- /dev/null
+++ b/examples/python/servers/bazaar/uv.lock
@@ -0,0 +1,3209 @@
+version = 1
+revision = 3
+requires-python = ">=3.10"
+
+[[package]]
+name = "aiohappyeyeballs"
+version = "2.6.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" },
+]
+
+[[package]]
+name = "aiohttp"
+version = "3.13.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohappyeyeballs" },
+ { name = "aiosignal" },
+ { name = "async-timeout", marker = "python_full_version < '3.11'" },
+ { name = "attrs" },
+ { name = "frozenlist" },
+ { name = "multidict" },
+ { name = "propcache" },
+ { name = "yarl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994, upload-time = "2025-10-28T20:59:39.937Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6d/34/939730e66b716b76046dedfe0842995842fa906ccc4964bba414ff69e429/aiohttp-3.13.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2372b15a5f62ed37789a6b383ff7344fc5b9f243999b0cd9b629d8bc5f5b4155", size = 736471, upload-time = "2025-10-28T20:55:27.924Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/cf/dcbdf2df7f6ca72b0bb4c0b4509701f2d8942cf54e29ca197389c214c07f/aiohttp-3.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7f8659a48995edee7229522984bd1009c1213929c769c2daa80b40fe49a180c", size = 493985, upload-time = "2025-10-28T20:55:29.456Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/87/71c8867e0a1d0882dcbc94af767784c3cb381c1c4db0943ab4aae4fed65e/aiohttp-3.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:939ced4a7add92296b0ad38892ce62b98c619288a081170695c6babe4f50e636", size = 489274, upload-time = "2025-10-28T20:55:31.134Z" },
+ { url = "https://files.pythonhosted.org/packages/38/0f/46c24e8dae237295eaadd113edd56dee96ef6462adf19b88592d44891dc5/aiohttp-3.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6315fb6977f1d0dd41a107c527fee2ed5ab0550b7d885bc15fee20ccb17891da", size = 1668171, upload-time = "2025-10-28T20:55:36.065Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/c6/4cdfb4440d0e28483681a48f69841fa5e39366347d66ef808cbdadddb20e/aiohttp-3.13.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6e7352512f763f760baaed2637055c49134fd1d35b37c2dedfac35bfe5cf8725", size = 1636036, upload-time = "2025-10-28T20:55:37.576Z" },
+ { url = "https://files.pythonhosted.org/packages/84/37/8708cf678628216fb678ab327a4e1711c576d6673998f4f43e86e9ae90dd/aiohttp-3.13.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e09a0a06348a2dd73e7213353c90d709502d9786219f69b731f6caa0efeb46f5", size = 1727975, upload-time = "2025-10-28T20:55:39.457Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/2e/3ebfe12fdcb9b5f66e8a0a42dffcd7636844c8a018f261efb2419f68220b/aiohttp-3.13.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a09a6d073fb5789456545bdee2474d14395792faa0527887f2f4ec1a486a59d3", size = 1815823, upload-time = "2025-10-28T20:55:40.958Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/4f/ca2ef819488cbb41844c6cf92ca6dd15b9441e6207c58e5ae0e0fc8d70ad/aiohttp-3.13.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b59d13c443f8e049d9e94099c7e412e34610f1f49be0f230ec656a10692a5802", size = 1669374, upload-time = "2025-10-28T20:55:42.745Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/fe/1fe2e1179a0d91ce09c99069684aab619bf2ccde9b20bd6ca44f8837203e/aiohttp-3.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:20db2d67985d71ca033443a1ba2001c4b5693fe09b0e29f6d9358a99d4d62a8a", size = 1555315, upload-time = "2025-10-28T20:55:44.264Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/2b/f3781899b81c45d7cbc7140cddb8a3481c195e7cbff8e36374759d2ab5a5/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:960c2fc686ba27b535f9fd2b52d87ecd7e4fd1cf877f6a5cba8afb5b4a8bd204", size = 1639140, upload-time = "2025-10-28T20:55:46.626Z" },
+ { url = "https://files.pythonhosted.org/packages/72/27/c37e85cd3ece6f6c772e549bd5a253d0c122557b25855fb274224811e4f2/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6c00dbcf5f0d88796151e264a8eab23de2997c9303dd7c0bf622e23b24d3ce22", size = 1645496, upload-time = "2025-10-28T20:55:48.933Z" },
+ { url = "https://files.pythonhosted.org/packages/66/20/3af1ab663151bd3780b123e907761cdb86ec2c4e44b2d9b195ebc91fbe37/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fed38a5edb7945f4d1bcabe2fcd05db4f6ec7e0e82560088b754f7e08d93772d", size = 1697625, upload-time = "2025-10-28T20:55:50.377Z" },
+ { url = "https://files.pythonhosted.org/packages/95/eb/ae5cab15efa365e13d56b31b0d085a62600298bf398a7986f8388f73b598/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b395bbca716c38bef3c764f187860e88c724b342c26275bc03e906142fc5964f", size = 1542025, upload-time = "2025-10-28T20:55:51.861Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/2d/1683e8d67ec72d911397fe4e575688d2a9b8f6a6e03c8fdc9f3fd3d4c03f/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:204ffff2426c25dfda401ba08da85f9c59525cdc42bda26660463dd1cbcfec6f", size = 1714918, upload-time = "2025-10-28T20:55:53.515Z" },
+ { url = "https://files.pythonhosted.org/packages/99/a2/ffe8e0e1c57c5e542d47ffa1fcf95ef2b3ea573bf7c4d2ee877252431efc/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05c4dd3c48fb5f15db31f57eb35374cb0c09afdde532e7fb70a75aede0ed30f6", size = 1656113, upload-time = "2025-10-28T20:55:55.438Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/42/d511aff5c3a2b06c09d7d214f508a4ad8ac7799817f7c3d23e7336b5e896/aiohttp-3.13.2-cp310-cp310-win32.whl", hash = "sha256:e574a7d61cf10351d734bcddabbe15ede0eaa8a02070d85446875dc11189a251", size = 432290, upload-time = "2025-10-28T20:55:56.96Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ea/1c2eb7098b5bad4532994f2b7a8228d27674035c9b3234fe02c37469ef14/aiohttp-3.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:364f55663085d658b8462a1c3f17b2b84a5c2e1ba858e1b79bff7b2e24ad1514", size = 455075, upload-time = "2025-10-28T20:55:58.373Z" },
+ { url = "https://files.pythonhosted.org/packages/35/74/b321e7d7ca762638cdf8cdeceb39755d9c745aff7a64c8789be96ddf6e96/aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0", size = 743409, upload-time = "2025-10-28T20:56:00.354Z" },
+ { url = "https://files.pythonhosted.org/packages/99/3d/91524b905ec473beaf35158d17f82ef5a38033e5809fe8742e3657cdbb97/aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb", size = 497006, upload-time = "2025-10-28T20:56:01.85Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d3/7f68bc02a67716fe80f063e19adbd80a642e30682ce74071269e17d2dba1/aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9", size = 493195, upload-time = "2025-10-28T20:56:03.314Z" },
+ { url = "https://files.pythonhosted.org/packages/98/31/913f774a4708775433b7375c4f867d58ba58ead833af96c8af3621a0d243/aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613", size = 1747759, upload-time = "2025-10-28T20:56:04.904Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/63/04efe156f4326f31c7c4a97144f82132c3bb21859b7bb84748d452ccc17c/aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead", size = 1704456, upload-time = "2025-10-28T20:56:06.986Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/02/4e16154d8e0a9cf4ae76f692941fd52543bbb148f02f098ca73cab9b1c1b/aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780", size = 1807572, upload-time = "2025-10-28T20:56:08.558Z" },
+ { url = "https://files.pythonhosted.org/packages/34/58/b0583defb38689e7f06798f0285b1ffb3a6fb371f38363ce5fd772112724/aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a", size = 1895954, upload-time = "2025-10-28T20:56:10.545Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/f3/083907ee3437425b4e376aa58b2c915eb1a33703ec0dc30040f7ae3368c6/aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592", size = 1747092, upload-time = "2025-10-28T20:56:12.118Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/61/98a47319b4e425cc134e05e5f3fc512bf9a04bf65aafd9fdcda5d57ec693/aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab", size = 1606815, upload-time = "2025-10-28T20:56:14.191Z" },
+ { url = "https://files.pythonhosted.org/packages/97/4b/e78b854d82f66bb974189135d31fce265dee0f5344f64dd0d345158a5973/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30", size = 1723789, upload-time = "2025-10-28T20:56:16.101Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/fc/9d2ccc794fc9b9acd1379d625c3a8c64a45508b5091c546dea273a41929e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40", size = 1718104, upload-time = "2025-10-28T20:56:17.655Z" },
+ { url = "https://files.pythonhosted.org/packages/66/65/34564b8765ea5c7d79d23c9113135d1dd3609173da13084830f1507d56cf/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948", size = 1785584, upload-time = "2025-10-28T20:56:19.238Z" },
+ { url = "https://files.pythonhosted.org/packages/30/be/f6a7a426e02fc82781afd62016417b3948e2207426d90a0e478790d1c8a4/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf", size = 1595126, upload-time = "2025-10-28T20:56:20.836Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/c7/8e22d5d28f94f67d2af496f14a83b3c155d915d1fe53d94b66d425ec5b42/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782", size = 1800665, upload-time = "2025-10-28T20:56:22.922Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/11/91133c8b68b1da9fc16555706aa7276fdf781ae2bb0876c838dd86b8116e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8", size = 1739532, upload-time = "2025-10-28T20:56:25.924Z" },
+ { url = "https://files.pythonhosted.org/packages/17/6b/3747644d26a998774b21a616016620293ddefa4d63af6286f389aedac844/aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec", size = 431876, upload-time = "2025-10-28T20:56:27.524Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/63/688462108c1a00eb9f05765331c107f95ae86f6b197b865d29e930b7e462/aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c", size = 456205, upload-time = "2025-10-28T20:56:29.062Z" },
+ { url = "https://files.pythonhosted.org/packages/29/9b/01f00e9856d0a73260e86dd8ed0c2234a466c5c1712ce1c281548df39777/aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b", size = 737623, upload-time = "2025-10-28T20:56:30.797Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/1b/4be39c445e2b2bd0aab4ba736deb649fabf14f6757f405f0c9685019b9e9/aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc", size = 492664, upload-time = "2025-10-28T20:56:32.708Z" },
+ { url = "https://files.pythonhosted.org/packages/28/66/d35dcfea8050e131cdd731dff36434390479b4045a8d0b9d7111b0a968f1/aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7", size = 491808, upload-time = "2025-10-28T20:56:34.57Z" },
+ { url = "https://files.pythonhosted.org/packages/00/29/8e4609b93e10a853b65f8291e64985de66d4f5848c5637cddc70e98f01f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb", size = 1738863, upload-time = "2025-10-28T20:56:36.377Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/fa/4ebdf4adcc0def75ced1a0d2d227577cd7b1b85beb7edad85fcc87693c75/aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3", size = 1700586, upload-time = "2025-10-28T20:56:38.034Z" },
+ { url = "https://files.pythonhosted.org/packages/da/04/73f5f02ff348a3558763ff6abe99c223381b0bace05cd4530a0258e52597/aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f", size = 1768625, upload-time = "2025-10-28T20:56:39.75Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/49/a825b79ffec124317265ca7d2344a86bcffeb960743487cb11988ffb3494/aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6", size = 1867281, upload-time = "2025-10-28T20:56:41.471Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/48/adf56e05f81eac31edcfae45c90928f4ad50ef2e3ea72cb8376162a368f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e", size = 1752431, upload-time = "2025-10-28T20:56:43.162Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ab/593855356eead019a74e862f21523db09c27f12fd24af72dbc3555b9bfd9/aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7", size = 1562846, upload-time = "2025-10-28T20:56:44.85Z" },
+ { url = "https://files.pythonhosted.org/packages/39/0f/9f3d32271aa8dc35036e9668e31870a9d3b9542dd6b3e2c8a30931cb27ae/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d", size = 1699606, upload-time = "2025-10-28T20:56:46.519Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/3c/52d2658c5699b6ef7692a3f7128b2d2d4d9775f2a68093f74bca06cf01e1/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b", size = 1720663, upload-time = "2025-10-28T20:56:48.528Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/d4/8f8f3ff1fb7fb9e3f04fcad4e89d8a1cd8fc7d05de67e3de5b15b33008ff/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8", size = 1737939, upload-time = "2025-10-28T20:56:50.77Z" },
+ { url = "https://files.pythonhosted.org/packages/03/d3/ddd348f8a27a634daae39a1b8e291ff19c77867af438af844bf8b7e3231b/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16", size = 1555132, upload-time = "2025-10-28T20:56:52.568Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b8/46790692dc46218406f94374903ba47552f2f9f90dad554eed61bfb7b64c/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169", size = 1764802, upload-time = "2025-10-28T20:56:54.292Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/e4/19ce547b58ab2a385e5f0b8aa3db38674785085abcf79b6e0edd1632b12f/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248", size = 1719512, upload-time = "2025-10-28T20:56:56.428Z" },
+ { url = "https://files.pythonhosted.org/packages/70/30/6355a737fed29dcb6dfdd48682d5790cb5eab050f7b4e01f49b121d3acad/aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e", size = 426690, upload-time = "2025-10-28T20:56:58.736Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/0d/b10ac09069973d112de6ef980c1f6bb31cb7dcd0bc363acbdad58f927873/aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45", size = 453465, upload-time = "2025-10-28T20:57:00.795Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/78/7e90ca79e5aa39f9694dcfd74f4720782d3c6828113bb1f3197f7e7c4a56/aiohttp-3.13.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7519bdc7dfc1940d201651b52bf5e03f5503bda45ad6eacf64dda98be5b2b6be", size = 732139, upload-time = "2025-10-28T20:57:02.455Z" },
+ { url = "https://files.pythonhosted.org/packages/db/ed/1f59215ab6853fbaa5c8495fa6cbc39edfc93553426152b75d82a5f32b76/aiohttp-3.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:088912a78b4d4f547a1f19c099d5a506df17eacec3c6f4375e2831ec1d995742", size = 490082, upload-time = "2025-10-28T20:57:04.784Z" },
+ { url = "https://files.pythonhosted.org/packages/68/7b/fe0fe0f5e05e13629d893c760465173a15ad0039c0a5b0d0040995c8075e/aiohttp-3.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5276807b9de9092af38ed23ce120539ab0ac955547b38563a9ba4f5b07b95293", size = 489035, upload-time = "2025-10-28T20:57:06.894Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/04/db5279e38471b7ac801d7d36a57d1230feeee130bbe2a74f72731b23c2b1/aiohttp-3.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1237c1375eaef0db4dcd7c2559f42e8af7b87ea7d295b118c60c36a6e61cb811", size = 1720387, upload-time = "2025-10-28T20:57:08.685Z" },
+ { url = "https://files.pythonhosted.org/packages/31/07/8ea4326bd7dae2bd59828f69d7fdc6e04523caa55e4a70f4a8725a7e4ed2/aiohttp-3.13.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96581619c57419c3d7d78703d5b78c1e5e5fc0172d60f555bdebaced82ded19a", size = 1688314, upload-time = "2025-10-28T20:57:10.693Z" },
+ { url = "https://files.pythonhosted.org/packages/48/ab/3d98007b5b87ffd519d065225438cc3b668b2f245572a8cb53da5dd2b1bc/aiohttp-3.13.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2713a95b47374169409d18103366de1050fe0ea73db358fc7a7acb2880422d4", size = 1756317, upload-time = "2025-10-28T20:57:12.563Z" },
+ { url = "https://files.pythonhosted.org/packages/97/3d/801ca172b3d857fafb7b50c7c03f91b72b867a13abca982ed6b3081774ef/aiohttp-3.13.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:228a1cd556b3caca590e9511a89444925da87d35219a49ab5da0c36d2d943a6a", size = 1858539, upload-time = "2025-10-28T20:57:14.623Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/0d/4764669bdf47bd472899b3d3db91fffbe925c8e3038ec591a2fd2ad6a14d/aiohttp-3.13.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6cde5fba8d7d8c6ac963dbb0256a9854e9fafff52fbcc58fdf819357892c3e", size = 1739597, upload-time = "2025-10-28T20:57:16.399Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/52/7bd3c6693da58ba16e657eb904a5b6decfc48ecd06e9ac098591653b1566/aiohttp-3.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2bef8237544f4e42878c61cef4e2839fee6346dc60f5739f876a9c50be7fcdb", size = 1555006, upload-time = "2025-10-28T20:57:18.288Z" },
+ { url = "https://files.pythonhosted.org/packages/48/30/9586667acec5993b6f41d2ebcf96e97a1255a85f62f3c653110a5de4d346/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:16f15a4eac3bc2d76c45f7ebdd48a65d41b242eb6c31c2245463b40b34584ded", size = 1683220, upload-time = "2025-10-28T20:57:20.241Z" },
+ { url = "https://files.pythonhosted.org/packages/71/01/3afe4c96854cfd7b30d78333852e8e851dceaec1c40fd00fec90c6402dd2/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bb7fb776645af5cc58ab804c58d7eba545a97e047254a52ce89c157b5af6cd0b", size = 1712570, upload-time = "2025-10-28T20:57:22.253Z" },
+ { url = "https://files.pythonhosted.org/packages/11/2c/22799d8e720f4697a9e66fd9c02479e40a49de3de2f0bbe7f9f78a987808/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b4951125ec10c70802f2cb09736c895861cd39fd9dcb35107b4dc8ae6220b8", size = 1733407, upload-time = "2025-10-28T20:57:24.37Z" },
+ { url = "https://files.pythonhosted.org/packages/34/cb/90f15dd029f07cebbd91f8238a8b363978b530cd128488085b5703683594/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:550bf765101ae721ee1d37d8095f47b1f220650f85fe1af37a90ce75bab89d04", size = 1550093, upload-time = "2025-10-28T20:57:26.257Z" },
+ { url = "https://files.pythonhosted.org/packages/69/46/12dce9be9d3303ecbf4d30ad45a7683dc63d90733c2d9fe512be6716cd40/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fe91b87fc295973096251e2d25a811388e7d8adf3bd2b97ef6ae78bc4ac6c476", size = 1758084, upload-time = "2025-10-28T20:57:28.349Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c8/0932b558da0c302ffd639fc6362a313b98fdf235dc417bc2493da8394df7/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0c8e31cfcc4592cb200160344b2fb6ae0f9e4effe06c644b5a125d4ae5ebe23", size = 1716987, upload-time = "2025-10-28T20:57:30.233Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/8b/f5bd1a75003daed099baec373aed678f2e9b34f2ad40d85baa1368556396/aiohttp-3.13.2-cp313-cp313-win32.whl", hash = "sha256:0740f31a60848d6edb296a0df827473eede90c689b8f9f2a4cdde74889eb2254", size = 425859, upload-time = "2025-10-28T20:57:32.105Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/28/a8a9fc6957b2cee8902414e41816b5ab5536ecf43c3b1843c10e82c559b2/aiohttp-3.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:a88d13e7ca367394908f8a276b89d04a3652044612b9a408a0bb22a5ed976a1a", size = 452192, upload-time = "2025-10-28T20:57:34.166Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/36/e2abae1bd815f01c957cbf7be817b3043304e1c87bad526292a0410fdcf9/aiohttp-3.13.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2475391c29230e063ef53a66669b7b691c9bfc3f1426a0f7bcdf1216bdbac38b", size = 735234, upload-time = "2025-10-28T20:57:36.415Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/e3/1ee62dde9b335e4ed41db6bba02613295a0d5b41f74a783c142745a12763/aiohttp-3.13.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f33c8748abef4d8717bb20e8fb1b3e07c6adacb7fd6beaae971a764cf5f30d61", size = 490733, upload-time = "2025-10-28T20:57:38.205Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/aa/7a451b1d6a04e8d15a362af3e9b897de71d86feac3babf8894545d08d537/aiohttp-3.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ae32f24bbfb7dbb485a24b30b1149e2f200be94777232aeadba3eecece4d0aa4", size = 491303, upload-time = "2025-10-28T20:57:40.122Z" },
+ { url = "https://files.pythonhosted.org/packages/57/1e/209958dbb9b01174870f6a7538cd1f3f28274fdbc88a750c238e2c456295/aiohttp-3.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7f02042c1f009ffb70067326ef183a047425bb2ff3bc434ead4dd4a4a66a2b", size = 1717965, upload-time = "2025-10-28T20:57:42.28Z" },
+ { url = "https://files.pythonhosted.org/packages/08/aa/6a01848d6432f241416bc4866cae8dc03f05a5a884d2311280f6a09c73d6/aiohttp-3.13.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93655083005d71cd6c072cdab54c886e6570ad2c4592139c3fb967bfc19e4694", size = 1667221, upload-time = "2025-10-28T20:57:44.869Z" },
+ { url = "https://files.pythonhosted.org/packages/87/4f/36c1992432d31bbc789fa0b93c768d2e9047ec8c7177e5cd84ea85155f36/aiohttp-3.13.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0db1e24b852f5f664cd728db140cf11ea0e82450471232a394b3d1a540b0f906", size = 1757178, upload-time = "2025-10-28T20:57:47.216Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/b4/8e940dfb03b7e0f68a82b88fd182b9be0a65cb3f35612fe38c038c3112cf/aiohttp-3.13.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b009194665bcd128e23eaddef362e745601afa4641930848af4c8559e88f18f9", size = 1838001, upload-time = "2025-10-28T20:57:49.337Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ef/39f3448795499c440ab66084a9db7d20ca7662e94305f175a80f5b7e0072/aiohttp-3.13.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c038a8fdc8103cd51dbd986ecdce141473ffd9775a7a8057a6ed9c3653478011", size = 1716325, upload-time = "2025-10-28T20:57:51.327Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/51/b311500ffc860b181c05d91c59a1313bdd05c82960fdd4035a15740d431e/aiohttp-3.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66bac29b95a00db411cd758fea0e4b9bdba6d549dfe333f9a945430f5f2cc5a6", size = 1547978, upload-time = "2025-10-28T20:57:53.554Z" },
+ { url = "https://files.pythonhosted.org/packages/31/64/b9d733296ef79815226dab8c586ff9e3df41c6aff2e16c06697b2d2e6775/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4ebf9cfc9ba24a74cf0718f04aac2a3bbe745902cc7c5ebc55c0f3b5777ef213", size = 1682042, upload-time = "2025-10-28T20:57:55.617Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/30/43d3e0f9d6473a6db7d472104c4eff4417b1e9df01774cb930338806d36b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a4b88ebe35ce54205c7074f7302bd08a4cb83256a3e0870c72d6f68a3aaf8e49", size = 1680085, upload-time = "2025-10-28T20:57:57.59Z" },
+ { url = "https://files.pythonhosted.org/packages/16/51/c709f352c911b1864cfd1087577760ced64b3e5bee2aa88b8c0c8e2e4972/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:98c4fb90bb82b70a4ed79ca35f656f4281885be076f3f970ce315402b53099ae", size = 1728238, upload-time = "2025-10-28T20:57:59.525Z" },
+ { url = "https://files.pythonhosted.org/packages/19/e2/19bd4c547092b773caeb48ff5ae4b1ae86756a0ee76c16727fcfd281404b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:ec7534e63ae0f3759df3a1ed4fa6bc8f75082a924b590619c0dd2f76d7043caa", size = 1544395, upload-time = "2025-10-28T20:58:01.914Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/87/860f2803b27dfc5ed7be532832a3498e4919da61299b4a1f8eb89b8ff44d/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5b927cf9b935a13e33644cbed6c8c4b2d0f25b713d838743f8fe7191b33829c4", size = 1742965, upload-time = "2025-10-28T20:58:03.972Z" },
+ { url = "https://files.pythonhosted.org/packages/67/7f/db2fc7618925e8c7a601094d5cbe539f732df4fb570740be88ed9e40e99a/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:88d6c017966a78c5265d996c19cdb79235be5e6412268d7e2ce7dee339471b7a", size = 1697585, upload-time = "2025-10-28T20:58:06.189Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/07/9127916cb09bb38284db5036036042b7b2c514c8ebaeee79da550c43a6d6/aiohttp-3.13.2-cp314-cp314-win32.whl", hash = "sha256:f7c183e786e299b5d6c49fb43a769f8eb8e04a2726a2bd5887b98b5cc2d67940", size = 431621, upload-time = "2025-10-28T20:58:08.636Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/41/554a8a380df6d3a2bba8a7726429a23f4ac62aaf38de43bb6d6cde7b4d4d/aiohttp-3.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:fe242cd381e0fb65758faf5ad96c2e460df6ee5b2de1072fe97e4127927e00b4", size = 457627, upload-time = "2025-10-28T20:58:11Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/8e/3824ef98c039d3951cb65b9205a96dd2b20f22241ee17d89c5701557c826/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f10d9c0b0188fe85398c61147bbd2a657d616c876863bfeff43376e0e3134673", size = 767360, upload-time = "2025-10-28T20:58:13.358Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/0f/6a03e3fc7595421274fa34122c973bde2d89344f8a881b728fa8c774e4f1/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e7c952aefdf2460f4ae55c5e9c3e80aa72f706a6317e06020f80e96253b1accd", size = 504616, upload-time = "2025-10-28T20:58:15.339Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/aa/ed341b670f1bc8a6f2c6a718353d13b9546e2cef3544f573c6a1ff0da711/aiohttp-3.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c20423ce14771d98353d2e25e83591fa75dfa90a3c1848f3d7c68243b4fbded3", size = 509131, upload-time = "2025-10-28T20:58:17.693Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/f0/c68dac234189dae5c4bbccc0f96ce0cc16b76632cfc3a08fff180045cfa4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e96eb1a34396e9430c19d8338d2ec33015e4a87ef2b4449db94c22412e25ccdf", size = 1864168, upload-time = "2025-10-28T20:58:20.113Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/65/75a9a76db8364b5d0e52a0c20eabc5d52297385d9af9c35335b924fafdee/aiohttp-3.13.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23fb0783bc1a33640036465019d3bba069942616a6a2353c6907d7fe1ccdaf4e", size = 1719200, upload-time = "2025-10-28T20:58:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/55/8df2ed78d7f41d232f6bd3ff866b6f617026551aa1d07e2f03458f964575/aiohttp-3.13.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1a9bea6244a1d05a4e57c295d69e159a5c50d8ef16aa390948ee873478d9a5", size = 1843497, upload-time = "2025-10-28T20:58:24.672Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/e0/94d7215e405c5a02ccb6a35c7a3a6cfff242f457a00196496935f700cde5/aiohttp-3.13.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a3d54e822688b56e9f6b5816fb3de3a3a64660efac64e4c2dc435230ad23bad", size = 1935703, upload-time = "2025-10-28T20:58:26.758Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/78/1eeb63c3f9b2d1015a4c02788fb543141aad0a03ae3f7a7b669b2483f8d4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a653d872afe9f33497215745da7a943d1dc15b728a9c8da1c3ac423af35178e", size = 1792738, upload-time = "2025-10-28T20:58:29.787Z" },
+ { url = "https://files.pythonhosted.org/packages/41/75/aaf1eea4c188e51538c04cc568040e3082db263a57086ea74a7d38c39e42/aiohttp-3.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56d36e80d2003fa3fc0207fac644216d8532e9504a785ef9a8fd013f84a42c61", size = 1624061, upload-time = "2025-10-28T20:58:32.529Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/c2/3b6034de81fbcc43de8aeb209073a2286dfb50b86e927b4efd81cf848197/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:78cd586d8331fb8e241c2dd6b2f4061778cc69e150514b39a9e28dd050475661", size = 1789201, upload-time = "2025-10-28T20:58:34.618Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/38/c15dcf6d4d890217dae79d7213988f4e5fe6183d43893a9cf2fe9e84ca8d/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:20b10bbfbff766294fe99987f7bb3b74fdd2f1a2905f2562132641ad434dcf98", size = 1776868, upload-time = "2025-10-28T20:58:38.835Z" },
+ { url = "https://files.pythonhosted.org/packages/04/75/f74fd178ac81adf4f283a74847807ade5150e48feda6aef024403716c30c/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9ec49dff7e2b3c85cdeaa412e9d438f0ecd71676fde61ec57027dd392f00c693", size = 1790660, upload-time = "2025-10-28T20:58:41.507Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/80/7368bd0d06b16b3aba358c16b919e9c46cf11587dc572091031b0e9e3ef0/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:94f05348c4406450f9d73d38efb41d669ad6cd90c7ee194810d0eefbfa875a7a", size = 1617548, upload-time = "2025-10-28T20:58:43.674Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/4b/a6212790c50483cb3212e507378fbe26b5086d73941e1ec4b56a30439688/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:fa4dcb605c6f82a80c7f95713c2b11c3b8e9893b3ebd2bc9bde93165ed6107be", size = 1817240, upload-time = "2025-10-28T20:58:45.787Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/f7/ba5f0ba4ea8d8f3c32850912944532b933acbf0f3a75546b89269b9b7dde/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf00e5db968c3f67eccd2778574cf64d8b27d95b237770aa32400bd7a1ca4f6c", size = 1762334, upload-time = "2025-10-28T20:58:47.936Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/83/1a5a1856574588b1cad63609ea9ad75b32a8353ac995d830bf5da9357364/aiohttp-3.13.2-cp314-cp314t-win32.whl", hash = "sha256:d23b5fe492b0805a50d3371e8a728a9134d8de5447dce4c885f5587294750734", size = 464685, upload-time = "2025-10-28T20:58:50.642Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/4d/d22668674122c08f4d56972297c51a624e64b3ed1efaa40187607a7cb66e/aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f", size = 498093, upload-time = "2025-10-28T20:58:52.782Z" },
+]
+
+[[package]]
+name = "aiosignal"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "frozenlist" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" },
+]
+
+[[package]]
+name = "annotated-doc"
+version = "0.0.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
+]
+
+[[package]]
+name = "annotated-types"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
+]
+
+[[package]]
+name = "anyio"
+version = "4.12.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "idna" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/16/ce/8a777047513153587e5434fd752e89334ac33e379aa3497db860eeb60377/anyio-4.12.0.tar.gz", hash = "sha256:73c693b567b0c55130c104d0b43a9baf3aa6a31fc6110116509f27bf75e21ec0", size = 228266, upload-time = "2025-11-28T23:37:38.911Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7f/9c/36c5c37947ebfb8c7f22e0eb6e4d188ee2d53aa3880f3f2744fb894f0cb1/anyio-4.12.0-py3-none-any.whl", hash = "sha256:dad2376a628f98eeca4881fc56cd06affd18f659b17a747d3ff0307ced94b1bb", size = 113362, upload-time = "2025-11-28T23:36:57.897Z" },
+]
+
+[[package]]
+name = "async-timeout"
+version = "5.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" },
+]
+
+[[package]]
+name = "attrs"
+version = "25.4.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" },
+]
+
+[[package]]
+name = "backports-asyncio-runner"
+version = "1.2.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
+]
+
+[[package]]
+name = "bitarray"
+version = "3.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/95/06/92fdc84448d324ab8434b78e65caf4fb4c6c90b4f8ad9bdd4c8021bfaf1e/bitarray-3.8.0.tar.gz", hash = "sha256:3eae38daffd77c9621ae80c16932eea3fb3a4af141fb7cc724d4ad93eff9210d", size = 151991, upload-time = "2025-11-02T21:41:15.117Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/b9/8a645fd36fc4c01ee223f97eccd4699c2f2e91681ccb33c0e963881c8e58/bitarray-3.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f08342dc8d19214faa7ef99574dea6c37a2790d6d04a9793ef8fa76c188dc08d", size = 148504, upload-time = "2025-11-02T21:38:54.596Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/f4/11b562e13ff732bd0674376f367f0a272034ebc28b8efbafbeb924552d21/bitarray-3.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:792462abfeeca6cc8c6c1e6d27e14319682f0182f6b0ba37befe911af794db70", size = 145481, upload-time = "2025-11-02T21:38:56.253Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/7c/5a2487da579491b38abab3b437e01d3b05be6e16e69cc5eb304040dcebd5/bitarray-3.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0df69d26f21a9d2f1b20266f6737fa43f08aa5015c99900fb69f255fbe4dabb4", size = 322760, upload-time = "2025-11-02T21:38:57.189Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/59/f0ef82d6a878d4af1b4961d208a716317929aa172fc0dfa5f4115319a873/bitarray-3.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b4f10d3f304be7183fac79bf2cd997f82e16aa9a9f37343d76c026c6e435a8a8", size = 350332, upload-time = "2025-11-02T21:38:58.238Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/ec/d444b22fce853327d4a8adec1de9987e11b28fcc2d7204dcbc544e196ed9/bitarray-3.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fc98ff43abad61f00515ad9a06213b7716699146e46eabd256cdfe7cb522bd97", size = 360787, upload-time = "2025-11-02T21:38:59.239Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/9e/60b205f52ea9ff155e9f12249090475159c909039daa29e47cd95e115dd5/bitarray-3.8.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81c6b4a6c1af800d52a6fa32389ef8f4281583f4f99dc1a40f2bb47667281541", size = 329050, upload-time = "2025-11-02T21:39:00.455Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/da/2ce373b423bc85a0eb93ee1cba3977971259a92a116932632f417b1b04d2/bitarray-3.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f3fd8df63c41ff6a676d031956aebf68ebbc687b47c507da25501eb22eec341f", size = 320507, upload-time = "2025-11-02T21:39:01.714Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/88/437408a2674b8bdb02063dd1535969b9c73cb8fdd197485de431e506c50e/bitarray-3.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0ce9d9e07c75da8027c62b4c9f45771d1d8aae7dc9ad7fb606c6a5aedbe9741", size = 348449, upload-time = "2025-11-02T21:39:03.124Z" },
+ { url = "https://files.pythonhosted.org/packages/97/46/d799e7e731c778b6dcb4627bafd395102065e5ab15a4a31f4222a3e20706/bitarray-3.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8a9c962c64a4c08def58b9799333e33af94ec53038cf151d36edacdb41f81646", size = 344776, upload-time = "2025-11-02T21:39:04.147Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/9a/129fff56d22d316b1c848c6e13e64191485756b5cd6ceb08e640edb80020/bitarray-3.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1a54d7e7999735faacdcbe8128e30207abc2caf9f9fd7102d180b32f1b78bfce", size = 325899, upload-time = "2025-11-02T21:39:05.118Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ba/4b01e99452ecc39f4abccf9bf83fe0f01c390e9794dad2d04b2c8b893c5f/bitarray-3.8.0-cp310-cp310-win32.whl", hash = "sha256:3ea52df96566457735314794422274bd1962066bfb609e7eea9113d70cf04ffe", size = 142756, upload-time = "2025-11-02T21:39:06.402Z" },
+ { url = "https://files.pythonhosted.org/packages/18/3f/c83635a67d90f45f88012468566c233eed1e9e9a9184fa882ba4039fadb3/bitarray-3.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:82a07de83dce09b4fa1bccbdc8bde8f188b131666af0dc9048ba0a0e448d8a3b", size = 149527, upload-time = "2025-11-02T21:39:07.377Z" },
+ { url = "https://files.pythonhosted.org/packages/33/46/391b3902a523d4555313640746460b19d317c6233d9379e150af97fa1554/bitarray-3.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5ba07e58fd98c9782201e79eb8dd4225733d212a5a3700f9a84d329bd0463a6", size = 146453, upload-time = "2025-11-02T21:39:08.624Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/7d/63558f1d0eb09217a3d30c1c847890879973e224a728fcff9391fab999b8/bitarray-3.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:25b9cff6c9856bc396232e2f609ea0c5ec1a8a24c500cee4cca96ba8a3cd50b6", size = 148502, upload-time = "2025-11-02T21:39:09.993Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/7b/f957ad211cb0172965b5f0881b67b99e2b6d41512af0a1001f44a44ddf4a/bitarray-3.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d9984017314da772f5f7460add7a0301a4ffc06c72c2998bb16c300a6253607", size = 145484, upload-time = "2025-11-02T21:39:10.904Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/dc/897973734f14f91467a3a795a4624752238053ecffaec7c8bbda1e363fda/bitarray-3.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bbbbfbb7d039b20d289ce56b1beb46138d65769d04af50c199c6ac4cb6054d52", size = 330909, upload-time = "2025-11-02T21:39:12.276Z" },
+ { url = "https://files.pythonhosted.org/packages/67/be/24b4b792426d92de289e73e09682915d567c2e69d47e8857586cbdc865d0/bitarray-3.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1f723e260c35e1c7c57a09d3a6ebe681bd56c83e1208ae3ce1869b7c0d10d4f", size = 358469, upload-time = "2025-11-02T21:39:13.766Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/0e/2eda69a7a59a6998df8fb57cc9d1e0e62888c599fb5237b0a8b479a01afb/bitarray-3.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cbd1660fb48827381ce3a621a4fdc237959e1cd4e98b098952a8f624a0726425", size = 369131, upload-time = "2025-11-02T21:39:15.041Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/7b/8a372d6635a6b2622477b2f96a569b2cd0318a62bc95a4a2144c7942c987/bitarray-3.8.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df6d7bf3e15b7e6e202a16ff4948a51759354016026deb04ab9b5acbbe35e096", size = 337089, upload-time = "2025-11-02T21:39:16.124Z" },
+ { url = "https://files.pythonhosted.org/packages/93/f0/8eca934dbe5dee47a0e5ef44eeb72e85acacc8097c27cd164337bc4ec5d3/bitarray-3.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d5c931ec1c03111718cabf85f6012bb2815fa0ce578175567fa8d6f2cc15d3b4", size = 328504, upload-time = "2025-11-02T21:39:17.321Z" },
+ { url = "https://files.pythonhosted.org/packages/88/dd/928b8e23a9950f8a8bfc42bc1e7de41f4e27f57de01a716308be5f683c2b/bitarray-3.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:41b53711f89008ba2de62e4c2d2260a8b357072fd4f18e1351b28955db2719dc", size = 356461, upload-time = "2025-11-02T21:39:18.396Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/93/4fb58417aff47fa2fe1874a39c9346b589a1d78c93a9cb24cccede5dc737/bitarray-3.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4f298daaaea58d45e245a132d6d2bdfb6f856da50dc03d75ebb761439fb626cf", size = 353008, upload-time = "2025-11-02T21:39:19.828Z" },
+ { url = "https://files.pythonhosted.org/packages/da/54/aa04e4a7b45aa5913f08ee377d43319b0979925e3c0407882eb29df3be66/bitarray-3.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:30989a2451b693c3f9359d91098a744992b5431a0be4858f1fdf0ec76b457125", size = 334048, upload-time = "2025-11-02T21:39:20.924Z" },
+ { url = "https://files.pythonhosted.org/packages/da/52/e851f41076df014c05d6ac1ce34fbf7db5fa31241da3e2f09bb2be9e283d/bitarray-3.8.0-cp311-cp311-win32.whl", hash = "sha256:e5aed4754895942ae15ffa48c52d181e1c1463236fda68d2dba29c03aa61786b", size = 142907, upload-time = "2025-11-02T21:39:22.312Z" },
+ { url = "https://files.pythonhosted.org/packages/28/01/db0006148b1dd13b4ac2686df8fa57d12f5887df313a506e939af0cb0997/bitarray-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:22c540ed20167d3dbb1e2d868ca935180247d620c40eace90efa774504a40e3b", size = 149670, upload-time = "2025-11-02T21:39:23.341Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/ea/b7d55ee269b1426f758a535c9ec2a07c056f20f403fa981685c3c8b4798c/bitarray-3.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:84b52b2cf77bb7f703d16c4007b021078dbbe6cf8ffb57abe81a7bacfc175ef2", size = 146709, upload-time = "2025-11-02T21:39:24.343Z" },
+ { url = "https://files.pythonhosted.org/packages/82/a0/0c41d893eda756315491adfdbf9bc928aee3d377a7f97a8834d453aa5de1/bitarray-3.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2fcbe9b3a5996b417e030aa33a562e7e20dfc86271e53d7e841fc5df16268b8", size = 148575, upload-time = "2025-11-02T21:39:25.718Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/30/12ab2f4a4429bd844b419c37877caba93d676d18be71354fbbeb21d9f4cc/bitarray-3.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cd761d158f67e288fd0ebe00c3b158095ce80a4bc7c32b60c7121224003ba70d", size = 145454, upload-time = "2025-11-02T21:39:26.695Z" },
+ { url = "https://files.pythonhosted.org/packages/26/58/314b3e3f219533464e120f0c51ac5123e7b1c1b91f725a4073fb70c5a858/bitarray-3.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c394a3f055b49f92626f83c1a0b6d6cd2c628f1ccd72481c3e3c6aa4695f3b20", size = 332949, upload-time = "2025-11-02T21:39:27.801Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/ce/ca8c706bd8341c7a22dd92d2a528af71f7e5f4726085d93f81fd768cb03b/bitarray-3.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:969fd67de8c42affdb47b38b80f1eaa79ac0ef17d65407cdd931db1675315af1", size = 360599, upload-time = "2025-11-02T21:39:28.964Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/dc/aa181df85f933052d962804906b282acb433cb9318b08ec2aceb4ee34faf/bitarray-3.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:99d25aff3745c54e61ab340b98400c52ebec04290a62078155e0d7eb30380220", size = 371972, upload-time = "2025-11-02T21:39:30.228Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/d9/b805bfa158c7bcf4df0ac19b1be581b47e1ddb792c11023aed80a7058e78/bitarray-3.8.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e645b4c365d6f1f9e0799380ad6395268f3c3b898244a650aaeb8d9d27b74c35", size = 340303, upload-time = "2025-11-02T21:39:31.342Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/42/5308cc97ea929e30727292617a3a88293470166851e13c9e3f16f395da55/bitarray-3.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2fa23fdb3beab313950bbb49674e8a161e61449332d3997089fe3944953f1b77", size = 330494, upload-time = "2025-11-02T21:39:32.769Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/89/64f1596cb80433323efdbc8dcd0d6e57c40dfbe6ea3341623f34ec397edd/bitarray-3.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:165052a0e61c880f7093808a0c524ce1b3555bfa114c0dfb5c809cd07918a60d", size = 358123, upload-time = "2025-11-02T21:39:34.331Z" },
+ { url = "https://files.pythonhosted.org/packages/27/fd/f3d49c5443b57087f888b5e118c8dd78bb7c8e8cfeeed250f8e92128a05f/bitarray-3.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:337c8cd46a4c6568d367ed676cbf2d7de16f890bb31dbb54c44c1d6bb6d4a1de", size = 356046, upload-time = "2025-11-02T21:39:35.449Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/db/1fd0b402bd2b47142e958b6930dbb9445235d03fa703c9a24caa6e576ae2/bitarray-3.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:21ca6a47bf20db9e7ad74ca04b3d479e4d76109b68333eb23535553d2705339e", size = 336872, upload-time = "2025-11-02T21:39:36.891Z" },
+ { url = "https://files.pythonhosted.org/packages/58/73/680b47718f1313b4538af479c4732eaca0aeda34d93fc5b869f87932d57d/bitarray-3.8.0-cp312-cp312-win32.whl", hash = "sha256:178c5a4c7fdfb5cd79e372ae7f675390e670f3732e5bc68d327e01a5b3ff8d55", size = 143025, upload-time = "2025-11-02T21:39:38.303Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/11/7792587c19c79a8283e8838f44709fa4338a8f7d2a3091dfd81c07ae89c7/bitarray-3.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:75a3b6e9c695a6570ea488db75b84bb592ff70a944957efa1c655867c575018b", size = 149969, upload-time = "2025-11-02T21:39:39.715Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/00/9df64b5d8a84e8e9ec392f6f9ce93f50626a5b301cb6c6b3fe3406454d66/bitarray-3.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:5591daf81313096909d973fb2612fccd87528fdfdd39f6478bdce54543178954", size = 146907, upload-time = "2025-11-02T21:39:40.815Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/35/480364d4baf1e34c79076750914664373f561c58abb5c31c35b3fae613ff/bitarray-3.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:18214bac86341f1cc413772e66447d6cca10981e2880b70ecaf4e826c04f95e9", size = 148582, upload-time = "2025-11-02T21:39:42.268Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/a8/718b95524c803937f4edbaaf6480f39c80f6ed189d61357b345e8361ffb6/bitarray-3.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:01c5f0dc080b0ebb432f7a68ee1e88a76bd34f6d89c9568fcec65fb16ed71f0e", size = 145433, upload-time = "2025-11-02T21:39:43.552Z" },
+ { url = "https://files.pythonhosted.org/packages/03/66/4a10f30dc9e2e01e3b4ecd44a511219f98e63c86b0e0f704c90fac24059b/bitarray-3.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86685fa04067f7175f9718489ae755f6acde03593a1a9ca89305554af40e14fd", size = 332986, upload-time = "2025-11-02T21:39:44.656Z" },
+ { url = "https://files.pythonhosted.org/packages/53/25/4c08774d847f80a1166e4c704b4e0f1c417c0afe6306eae0bc5e70d35faa/bitarray-3.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56896ceeffe25946c4010320629e2d858ca763cd8ded273c81672a5edbcb1e0a", size = 360634, upload-time = "2025-11-02T21:39:45.798Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/8f/bf8ad26169ebd0b2746d5c7564db734453ca467f8aab87e9d43b0a794383/bitarray-3.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9858dcbc23ba7eaadcd319786b982278a1a2b2020720b19db43e309579ff76fb", size = 371992, upload-time = "2025-11-02T21:39:46.968Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/16/ce166754e7c9d10650e02914552fa637cf3b2591f7ed16632bbf6b783312/bitarray-3.8.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa7dec53c25f1949513457ef8b0ea1fb40e76c672cc4d2daa8ad3c8d6b73491a", size = 340315, upload-time = "2025-11-02T21:39:48.182Z" },
+ { url = "https://files.pythonhosted.org/packages/de/2a/fbba3a106ddd260e84b9a624f730257c32ba51a8a029565248dfedfdf6f2/bitarray-3.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15a2eff91f54d2b1f573cca8ca6fb58763ce8fea80e7899ab028f3987ef71cd5", size = 330473, upload-time = "2025-11-02T21:39:49.705Z" },
+ { url = "https://files.pythonhosted.org/packages/68/97/56cf3c70196e7307ad32318a9d6ed969dbdc6a4534bbe429112fa7dfe42e/bitarray-3.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b1572ee0eb1967e71787af636bb7d1eb9c6735d5337762c450650e7f51844594", size = 358129, upload-time = "2025-11-02T21:39:51.189Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/be/afd391a5c0896d3339613321b2f94af853f29afc8bd3fbc327431244c642/bitarray-3.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5bfac7f236ba1a4d402644bdce47fb9db02a7cf3214a1f637d3a88390f9e5428", size = 356005, upload-time = "2025-11-02T21:39:52.355Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/08/a8e1a371babba29bad3378bb3a2cdca2b012170711e7fe1f22031a6b7b95/bitarray-3.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0a55cf02d2cdd739b40ce10c09bbdd520e141217696add7a48b56e67bdfdfe6", size = 336862, upload-time = "2025-11-02T21:39:54.345Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/8a/6dc1d0fdc06991c8dc3b1fcfe1ae49fbaced42064cd1b5f24278e73fe05f/bitarray-3.8.0-cp313-cp313-win32.whl", hash = "sha256:a2ba92f59e30ce915e9e79af37649432e3a212ddddf416d4d686b1b4825bcdb2", size = 143018, upload-time = "2025-11-02T21:39:56.361Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/72/76e13f5cd23b8b9071747909663ce3b02da24a5e7e22c35146338625db35/bitarray-3.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c8f2a5d8006db5a555e06f9437e76bf52537d3dfd130cb8ae2b30866aca32c9", size = 149977, upload-time = "2025-11-02T21:39:57.718Z" },
+ { url = "https://files.pythonhosted.org/packages/01/37/60f336c32336cc3ec03b0c61076f16ea2f05d5371c8a56e802161d218b77/bitarray-3.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:50ddbe3a7b4b6ab96812f5a4d570f401a2cdb95642fd04c062f98939610bbeee", size = 146930, upload-time = "2025-11-02T21:39:59.308Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/b0/411327a6c7f6b2bead64bb06fe60b92e0344957ec1ab0645d5ccc25fdafe/bitarray-3.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8cbd4bfc933b33b85c43ef4c1f4d5e3e9d91975ea6368acf5fbac02bac06ea89", size = 148563, upload-time = "2025-11-02T21:40:01.006Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/bc/ff80d97c627d774f879da0ea93223adb1267feab7e07d5c17580ffe6d632/bitarray-3.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9d35d8f8a1c9ed4e2b08187b513f8a3c71958600129db3aa26d85ea3abfd1310", size = 145422, upload-time = "2025-11-02T21:40:02.535Z" },
+ { url = "https://files.pythonhosted.org/packages/66/e7/b4cb6c5689aacd0a32f3aa8a507155eaa33528c63de2f182b60843fbf700/bitarray-3.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99f55e14e7c56f4fafe1343480c32b110ef03836c21ff7c48bae7add6818f77c", size = 332852, upload-time = "2025-11-02T21:40:03.645Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/91/fbd1b047e3e2f4b65590f289c8151df1d203d75b005f5aae4e072fe77d76/bitarray-3.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dfbe2aa45b273f49e715c5345d94874cb65a28482bf231af408891c260601b8d", size = 360801, upload-time = "2025-11-02T21:40:04.827Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/4a/63064c593627bac8754fdafcb5343999c93ab2aeb27bcd9d270a010abea5/bitarray-3.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:64af877116edf051375b45f0bda648143176a017b13803ec7b3a3111dc05f4c5", size = 371408, upload-time = "2025-11-02T21:40:05.985Z" },
+ { url = "https://files.pythonhosted.org/packages/46/97/ddc07723767bdafd170f2ff6e173c940fa874192783ee464aa3c1dedf07d/bitarray-3.8.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cdfbb27f2c46bb5bbdcee147530cbc5ca8ab858d7693924e88e30ada21b2c5e2", size = 340033, upload-time = "2025-11-02T21:40:07.189Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/1e/e1ea9f1146fd4af032817069ff118918d73e5de519854ce3860e2ed560ff/bitarray-3.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4d73d4948dcc5591d880db8933004e01f1dd2296df9de815354d53469beb26fe", size = 330774, upload-time = "2025-11-02T21:40:08.496Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/9f/8242296c124a48d1eab471fd0838aeb7ea9c6fd720302d99ab7855d3e6d3/bitarray-3.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:28a85b056c0eb7f5d864c0ceef07034117e8ebfca756f50648c71950a568ba11", size = 358337, upload-time = "2025-11-02T21:40:10.035Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/6b/9095d75264c67d479f298c80802422464ce18c3cdd893252eeccf4997611/bitarray-3.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:79ec4498a545733ecace48d780d22407411b07403a2e08b9a4d7596c0b97ebd7", size = 355639, upload-time = "2025-11-02T21:40:11.485Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/af/c93c0ae5ef824136e90ac7ddf6cceccb1232f34240b2f55a922f874da9b4/bitarray-3.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:33af25c4ff7723363cb8404dfc2eefeab4110b654f6c98d26aba8a08c745d860", size = 336999, upload-time = "2025-11-02T21:40:12.709Z" },
+ { url = "https://files.pythonhosted.org/packages/81/0f/72c951f5997b2876355d5e671f78dd2362493254876675cf22dbd24389ae/bitarray-3.8.0-cp314-cp314-win32.whl", hash = "sha256:2c3bb96b6026643ce24677650889b09073f60b9860a71765f843c99f9ab38b25", size = 142169, upload-time = "2025-11-02T21:40:14.031Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/55/ef1b4de8107bf13823da8756c20e1fbc9452228b4e837f46f6d9ddba3eb3/bitarray-3.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:847c7f61964225fc489fe1d49eda7e0e0d253e98862c012cecf845f9ad45cdf4", size = 148737, upload-time = "2025-11-02T21:40:15.436Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/26/bc0784136775024ac56cc67c0d6f9aa77a7770de7f82c3a7c9be11c217cd/bitarray-3.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:a2cb35a6efaa0e3623d8272471371a12c7e07b51a33e5efce9b58f655d864b4e", size = 146083, upload-time = "2025-11-02T21:40:17.135Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/64/57984e64264bf43d93a1809e645972771566a2d0345f4896b041ce20b000/bitarray-3.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:15e8d0597cc6e8496de6f4dea2a6880c57e1251502a7072f5631108a1aa28521", size = 149455, upload-time = "2025-11-02T21:40:18.558Z" },
+ { url = "https://files.pythonhosted.org/packages/81/c0/0d5f2eaef1867f462f764bdb07d1e116c33a1bf052ea21889aefe4282f5b/bitarray-3.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8ffe660e963ae711cb9e2b8d8461c9b1ad6167823837fc17d59d5e539fb898fa", size = 146491, upload-time = "2025-11-02T21:40:19.665Z" },
+ { url = "https://files.pythonhosted.org/packages/65/c6/bc1261f7a8862c0c59220a484464739e52235fd1e2afcb24d7f7d3fb5702/bitarray-3.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4779f356083c62e29b4198d290b7b17a39a69702d150678b7efff0fdddf494a8", size = 339721, upload-time = "2025-11-02T21:40:21.277Z" },
+ { url = "https://files.pythonhosted.org/packages/81/d8/289ca55dd2939ea17b1108dc53bffc0fdc5160ba44f77502dfaae35d08c6/bitarray-3.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:025d133bf4ca8cf75f904eeb8ea946228d7c043231866143f31946a6f4dd0bf3", size = 367823, upload-time = "2025-11-02T21:40:22.463Z" },
+ { url = "https://files.pythonhosted.org/packages/91/a2/61e7461ca9ac0fcb70f327a2e84b006996d2a840898e69037a39c87c6d06/bitarray-3.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:451f9958850ea98440d542278368c8d1e1ea821e2494b204570ba34a340759df", size = 377341, upload-time = "2025-11-02T21:40:23.789Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/87/4a0c9c8bdb13916d443e04d8f8542eef9190f31425da3c17c3478c40173f/bitarray-3.8.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6d79f659965290af60d6acc8e2716341865fe74609a7ede2a33c2f86ad893b8f", size = 344985, upload-time = "2025-11-02T21:40:25.261Z" },
+ { url = "https://files.pythonhosted.org/packages/17/4c/ff9259b916efe53695b631772e5213699c738efc2471b5ffe273f4000994/bitarray-3.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fbf05678c2ae0064fb1b8de7e9e8f0fc30621b73c8477786dd0fb3868044a8c8", size = 336796, upload-time = "2025-11-02T21:40:26.942Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/4b/51b2468bbddbade5e2f3b8d5db08282c5b309e8687b0f02f75a8b5ff559c/bitarray-3.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:c396358023b876cff547ce87f4e8ff8a2280598873a137e8cc69e115262260b8", size = 365085, upload-time = "2025-11-02T21:40:28.224Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/79/53473bfc2e052c6dbb628cdc1b156be621c77aaeb715918358b01574be55/bitarray-3.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed3493a369fe849cce98542d7405c88030b355e4d2e113887cb7ecc86c205773", size = 361012, upload-time = "2025-11-02T21:40:29.635Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/b1/242bf2e44bfc69e73fa2b954b425d761a8e632f78ea31008f1c3cfad0854/bitarray-3.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c764fb167411d5afaef88138542a4bfa28bd5e5ded5e8e42df87cef965efd6e9", size = 340644, upload-time = "2025-11-02T21:40:31.089Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/01/12e5ecf30a5de28a32485f226cad4b8a546845f65f755ce0365057ab1e92/bitarray-3.8.0-cp314-cp314t-win32.whl", hash = "sha256:e12769d3adcc419e65860de946df8d2ed274932177ac1cdb05186e498aaa9149", size = 143630, upload-time = "2025-11-02T21:40:32.351Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/92/6b6ade587b08024a8a890b07724775d29da9cf7497be5c3cbe226185e463/bitarray-3.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0ca70ccf789446a6dfde40b482ec21d28067172cd1f8efd50d5548159fccad9e", size = 150250, upload-time = "2025-11-02T21:40:33.596Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/40/be3858ffed004e47e48a2cefecdbf9b950d41098b780f9dc3aa609a88351/bitarray-3.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2a3d1b05ffdd3e95687942ae7b13c63689f85d3f15c39b33329e3cb9ce6c015f", size = 147015, upload-time = "2025-11-02T21:40:35.064Z" },
+]
+
+[[package]]
+name = "black"
+version = "25.12.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "mypy-extensions" },
+ { name = "packaging" },
+ { name = "pathspec" },
+ { name = "platformdirs" },
+ { name = "pytokens" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c4/d9/07b458a3f1c525ac392b5edc6b191ff140b596f9d77092429417a54e249d/black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7", size = 659264, upload-time = "2025-12-08T01:40:52.501Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/37/d5/8d3145999d380e5d09bb00b0f7024bf0a8ccb5c07b5648e9295f02ec1d98/black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8", size = 1895720, upload-time = "2025-12-08T01:46:58.197Z" },
+ { url = "https://files.pythonhosted.org/packages/06/97/7acc85c4add41098f4f076b21e3e4e383ad6ed0a3da26b2c89627241fc11/black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a", size = 1727193, upload-time = "2025-12-08T01:52:26.674Z" },
+ { url = "https://files.pythonhosted.org/packages/24/f0/fdf0eb8ba907ddeb62255227d29d349e8256ef03558fbcadfbc26ecfe3b2/black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea", size = 1774506, upload-time = "2025-12-08T01:46:25.721Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/f5/9203a78efe00d13336786b133c6180a9303d46908a9aa72d1104ca214222/black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f", size = 1416085, upload-time = "2025-12-08T01:46:06.073Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/cc/7a6090e6b081c3316282c05c546e76affdce7bf7a3b7d2c3a2a69438bd01/black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da", size = 1226038, upload-time = "2025-12-08T01:45:29.388Z" },
+ { url = "https://files.pythonhosted.org/packages/60/ad/7ac0d0e1e0612788dbc48e62aef8a8e8feffac7eb3d787db4e43b8462fa8/black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a", size = 1877003, upload-time = "2025-12-08T01:43:29.967Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/dd/a237e9f565f3617a88b49284b59cbca2a4f56ebe68676c1aad0ce36a54a7/black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be", size = 1712639, upload-time = "2025-12-08T01:52:46.756Z" },
+ { url = "https://files.pythonhosted.org/packages/12/80/e187079df1ea4c12a0c63282ddd8b81d5107db6d642f7d7b75a6bcd6fc21/black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b", size = 1758143, upload-time = "2025-12-08T01:45:29.137Z" },
+ { url = "https://files.pythonhosted.org/packages/93/b5/3096ccee4f29dc2c3aac57274326c4d2d929a77e629f695f544e159bfae4/black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5", size = 1420698, upload-time = "2025-12-08T01:45:53.379Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/39/f81c0ffbc25ffbe61c7d0385bf277e62ffc3e52f5ee668d7369d9854fadf/black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655", size = 1229317, upload-time = "2025-12-08T01:46:35.606Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/bd/26083f805115db17fda9877b3c7321d08c647df39d0df4c4ca8f8450593e/black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a", size = 1924178, upload-time = "2025-12-08T01:49:51.048Z" },
+ { url = "https://files.pythonhosted.org/packages/89/6b/ea00d6651561e2bdd9231c4177f4f2ae19cc13a0b0574f47602a7519b6ca/black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783", size = 1742643, upload-time = "2025-12-08T01:49:59.09Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/f3/360fa4182e36e9875fabcf3a9717db9d27a8d11870f21cff97725c54f35b/black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59", size = 1800158, upload-time = "2025-12-08T01:44:27.301Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/08/2c64830cb6616278067e040acca21d4f79727b23077633953081c9445d61/black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892", size = 1426197, upload-time = "2025-12-08T01:45:51.198Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/60/a93f55fd9b9816b7432cf6842f0e3000fdd5b7869492a04b9011a133ee37/black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43", size = 1237266, upload-time = "2025-12-08T01:45:10.556Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/52/c551e36bc95495d2aa1a37d50566267aa47608c81a53f91daa809e03293f/black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5", size = 1923809, upload-time = "2025-12-08T01:46:55.126Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/f7/aac9b014140ee56d247e707af8db0aae2e9efc28d4a8aba92d0abd7ae9d1/black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f", size = 1742384, upload-time = "2025-12-08T01:49:37.022Z" },
+ { url = "https://files.pythonhosted.org/packages/74/98/38aaa018b2ab06a863974c12b14a6266badc192b20603a81b738c47e902e/black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf", size = 1798761, upload-time = "2025-12-08T01:46:05.386Z" },
+ { url = "https://files.pythonhosted.org/packages/16/3a/a8ac542125f61574a3f015b521ca83b47321ed19bb63fe6d7560f348bfe1/black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d", size = 1429180, upload-time = "2025-12-08T01:45:34.903Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/2d/bdc466a3db9145e946762d52cd55b1385509d9f9004fec1c97bdc8debbfb/black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce", size = 1239350, upload-time = "2025-12-08T01:46:09.458Z" },
+ { url = "https://files.pythonhosted.org/packages/35/46/1d8f2542210c502e2ae1060b2e09e47af6a5e5963cb78e22ec1a11170b28/black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5", size = 1917015, upload-time = "2025-12-08T01:53:27.987Z" },
+ { url = "https://files.pythonhosted.org/packages/41/37/68accadf977672beb8e2c64e080f568c74159c1aaa6414b4cd2aef2d7906/black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f", size = 1741830, upload-time = "2025-12-08T01:54:36.861Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/76/03608a9d8f0faad47a3af3a3c8c53af3367f6c0dd2d23a84710456c7ac56/black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f", size = 1791450, upload-time = "2025-12-08T01:44:52.581Z" },
+ { url = "https://files.pythonhosted.org/packages/06/99/b2a4bd7dfaea7964974f947e1c76d6886d65fe5d24f687df2d85406b2609/black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83", size = 1452042, upload-time = "2025-12-08T01:46:13.188Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/7c/d9825de75ae5dd7795d007681b752275ea85a1c5d83269b4b9c754c2aaab/black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b", size = 1267446, upload-time = "2025-12-08T01:46:14.497Z" },
+ { url = "https://files.pythonhosted.org/packages/68/11/21331aed19145a952ad28fca2756a1433ee9308079bd03bd898e903a2e53/black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828", size = 206191, upload-time = "2025-12-08T01:40:50.963Z" },
+]
+
+[[package]]
+name = "certifi"
+version = "2025.11.12"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.4.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" },
+ { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" },
+ { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" },
+ { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" },
+ { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
+ { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
+ { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
+ { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
+ { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
+ { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
+ { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
+ { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
+ { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
+ { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
+ { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
+ { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
+ { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
+ { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
+ { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
+ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
+ { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
+ { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
+ { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
+ { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
+ { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
+ { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
+ { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
+]
+
+[[package]]
+name = "ckzg"
+version = "2.1.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b1/e8/b262fff67d6bcaecd19c71d19ebea9184a1204e00368664e1544a2511bd8/ckzg-2.1.5.tar.gz", hash = "sha256:e48e092f9b89ebb6aaa195de2e2bb72ad2d4b35c87d3a15e4545f13c51fbbe30", size = 1123745, upload-time = "2025-09-30T19:09:13.391Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/47/c52e96b5c3476524c24a8ac99002590b0dc700618e8c9ed52bfcba1acda7/ckzg-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49ee4c830de89764bfd9e8188446f3020f14d32bd4486fcbc5a4a5afad775ac0", size = 116307, upload-time = "2025-09-30T19:07:35.486Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/71/a136af12f4354cd7533f52f0b5df75431824926b5cbeb5160684a1390ae9/ckzg-2.1.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3b4f0c6c2f1a629d4d64e900c65633595c63d208001d588c61b6c8bc1b189dec", size = 99814, upload-time = "2025-09-30T19:07:36.882Z" },
+ { url = "https://files.pythonhosted.org/packages/91/55/92ccf278a120de2f8433044bc591615165336ff297ae2c3ba723572c23be/ckzg-2.1.5-cp310-cp310-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:10c8bc524267a40fe7c4fabd4c23f131ea18fcabd6016cdc4ddcb95cc757faf5", size = 179522, upload-time = "2025-11-06T21:05:26.357Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/bd/381970acf6b2425923b1f5ce01194330f88997584dedfd7fdba699fd1109/ckzg-2.1.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ea589e60db460ee9ebb678f20e74cc9289e912ccad66693b3263459933aaffc", size = 165174, upload-time = "2025-11-06T21:05:28.723Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/9b/d5cb656245b239fe7d326670094ecbe16b123f15cbc095fcc9f795523505/ckzg-2.1.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97769b53f7d8c46e794d5c8aa609a4c00ec1fb050e69b6833b45dbb23a7b6501", size = 174752, upload-time = "2025-11-06T21:05:29.846Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/9d/97f403cb2c93abcb9f522a3b575cfd0ee9961398e23967e4ff5570844186/ckzg-2.1.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a45aaea4a42babea48bb27e387fb209f2aaaaaa16abea25a4a92a056b616f9af", size = 175697, upload-time = "2025-09-30T19:07:37.688Z" },
+ { url = "https://files.pythonhosted.org/packages/79/45/a5a414672a1daba079e43e4fb2b11d53a681b0934475f4eb32cb7babf633/ckzg-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:060562273057911c39a1491e9b76055c095c10cfff1704ed70011e38b53f83d8", size = 160993, upload-time = "2025-09-30T19:07:38.561Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/4b/54eb12939012294b47e5b112c1298f4fdbdbfded213926e5d404c4eae8dc/ckzg-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12a90277b17e1cb5c326c5c261dad2ebb14a7136e754593e3a0a92c94799fc1", size = 170357, upload-time = "2025-09-30T19:07:39.77Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/24/6b202537c55b713a794b27f23749e569c7e4f4b9e204226edb49d5e622e1/ckzg-2.1.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:084f284d842b0a51befb2b595bf45c9c623ee3713c12500ceee9dcd05b24d14d", size = 172736, upload-time = "2025-09-30T19:07:40.635Z" },
+ { url = "https://files.pythonhosted.org/packages/53/53/df9bd835bab4edc7c030f5fc97285109d917753146d06da0a49d4982764e/ckzg-2.1.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7d42760b353c5d4a0f0d70a3161c1db75e22f4529fad4cef2228be1b8cd2d579", size = 187984, upload-time = "2025-09-30T19:07:41.752Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/b6/2a319f43ceae927217949a266a9794a427aeaebcdd463fadcf3f7297ed6c/ckzg-2.1.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0c547c0c61d2087f70170898948cad4d0e4583a7e25b24fdf247a426066b47bc", size = 182390, upload-time = "2025-09-30T19:07:42.579Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/a5/48aaa7d84f061ec1ed0c6a20306304cc4186d3191b388c33e47548eda309/ckzg-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:2b7ef12896e2afff613f058e3bc8e3478ff626ae8a6f2d3200950304a536935f", size = 100964, upload-time = "2025-09-30T19:07:43.42Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/32/d82185ecd05a91d1a35229c587eac9c518b30693c4c983ffde37e1f5a1a2/ckzg-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cead4ba760a49eaa4d7a50a0483aad9727d6103fc00c408aef15f2cd8f8dec7b", size = 116304, upload-time = "2025-09-30T19:07:44.531Z" },
+ { url = "https://files.pythonhosted.org/packages/94/a7/d22f813e032a7380f758d437348d791e8609a5c8ef2bd3654201f85a0047/ckzg-2.1.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3156983ba598fa05f0136325125e75197e4cf24ded255aaa6ace068cede92932", size = 99811, upload-time = "2025-09-30T19:07:45.311Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/2d/96259b64512ab9876ec3d32527148179ce02d2b052974c80435316444f21/ckzg-2.1.5-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:cac601a9690f133dd9d8e85f7a96578496427d42cdea771e0e07785b1cbbe9dc", size = 180251, upload-time = "2025-11-06T21:05:31.241Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/b0/020bf03e7b276775aa5d8a90605dfa6b4f23f2ae515142aa788389e6b68f/ckzg-2.1.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05860f1477880376106a6934becdcb3a2c6330fc2386fed0d7e8f3b0ce5df81c", size = 165942, upload-time = "2025-11-06T21:05:32.634Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/56/01c8633366e61ab10c7fbc15b918a0ebec8108c711a250a13c6950e5e892/ckzg-2.1.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:92b18b0ec177b9e2b4238936a8bffcfdaee7626a58f8d0c7c2ac554b8a05c9b6", size = 175509, upload-time = "2025-11-06T21:05:33.847Z" },
+ { url = "https://files.pythonhosted.org/packages/41/62/82ee6852a629bd9a783fb7787bcc2ee6e8c00c26b4ceb821a17484081760/ckzg-2.1.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d05e2c9466b2a4214dc19da35ea4cae636e033f3434768b982d37317a0f9c520", size = 176458, upload-time = "2025-09-30T19:07:46.433Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/00/5c68bb77f12eab6ba464f3955263e515ae988ddfc4bc57023b54b60feda0/ckzg-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c754bbc253cfce8814d633f135be4891e6f83a50125f418fee01323ba306f59a", size = 161841, upload-time = "2025-09-30T19:07:47.261Z" },
+ { url = "https://files.pythonhosted.org/packages/42/b3/c75079d270a7895ba6b5a95f86674d5c95f2f7e0db03328f1b21dc9a90eb/ckzg-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2b766d4aed52c8c717322f2af935da0b916bf59fbba771adb822499b45e491", size = 171101, upload-time = "2025-09-30T19:07:48.471Z" },
+ { url = "https://files.pythonhosted.org/packages/63/bf/81c533231f3bdc0029a0ec8abef9c24b2eaf929a044a77c15b6da54b91dc/ckzg-2.1.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dd7a296475baa5f20b5e7972448d4cb2f44d00b920d680de756c90c512af1c3b", size = 173403, upload-time = "2025-09-30T19:07:49.288Z" },
+ { url = "https://files.pythonhosted.org/packages/58/6e/bbfd04b25185c6371020dd1f1c3eb0557e36878ad6cdd78a1ea2bb47d8df/ckzg-2.1.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:97d3e93b3d94031fbd376005d86bf9b2c230ecfb4a4f4ced3b06b4aeefae6c9f", size = 188738, upload-time = "2025-09-30T19:07:50.64Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/83/ab3cd495a2b37b72b4d886276b3669f101c4927b1d54b7e528aa537362ab/ckzg-2.1.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3d2d35ba937f002b72a9a168696d0073a8e5912fa7058e77a06c370b86586401", size = 183202, upload-time = "2025-09-30T19:07:51.544Z" },
+ { url = "https://files.pythonhosted.org/packages/19/37/4ad60c2879b5e6988337b776f580b7aacb40ae6c05ef264e9835bed35e80/ckzg-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:ce2047071353ee099d44aa6575974648663204eb9b42354bfa5ac6f9b8fb63e9", size = 100965, upload-time = "2025-09-30T19:07:52.437Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/9f/3ef8acd201e4d098af6bc368991ac1469a5390399abd1e78307fffb65218/ckzg-2.1.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:edead535bd9afef27b8650bba09659debd4f52638aee5ec1ab7d2c9d7e86953c", size = 116333, upload-time = "2025-09-30T19:07:53.223Z" },
+ { url = "https://files.pythonhosted.org/packages/25/c2/202947c143336185180216a4939296d824cbffca4e1438d0fe696daf1904/ckzg-2.1.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dc78622855de3d47767cdeecfdf58fd58911f43a0fa783524e414b7e75149020", size = 99822, upload-time = "2025-09-30T19:07:54.06Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/45/d720181bc2445340b9108a55c9e91a23a10e4eeb6c091588e550b0a28a54/ckzg-2.1.5-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:e5639064b0dd147b73f2ce2c2506844b0c625b232396ac852dc52eced04bd529", size = 180441, upload-time = "2025-11-06T21:05:34.937Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/91/467ff00f3ec3d97d14b9e31789904107a907dca7526eb003e218be8038d1/ckzg-2.1.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0864813902b96cde171e65334ce8d13c5ff5b6855f2e71a2272ae268fa07e8", size = 166199, upload-time = "2025-11-06T21:05:36.497Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/8b/1148f4edbd252386e59d8c73670caa3138991292656cf84bb584ebb0e113/ckzg-2.1.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6f13f673a24c01e681eb66aed8f8e4ce191f009dd2149f3e1b9ad0dd59b4cd", size = 175829, upload-time = "2025-11-06T21:05:37.971Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/20/ace67811fbabcfece937f8286cdd96f5668757b8944a74630b6454131545/ckzg-2.1.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:094add5f197a3d278924ec1480d258f3b8b0e9f8851ae409eec83a21a738bffe", size = 176595, upload-time = "2025-09-30T19:07:54.792Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/65/127fa59aae21688887249ec1caa92dabaced331de5cb4e0224216270c3d0/ckzg-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b4b05f798784400e8c4dedaf1a1d57bbbc54de790855855add876fff3c9f629", size = 162014, upload-time = "2025-09-30T19:07:55.776Z" },
+ { url = "https://files.pythonhosted.org/packages/35/de/dcaa260f6f5aca83eb9017ea0c691d3d37458e08e24dcad5efcd348d807e/ckzg-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64aef50a1cf599041b9af018bc885a3fad6a20bbaf443fc45f0457cb47914610", size = 171396, upload-time = "2025-09-30T19:07:56.583Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/72/f87db164d687759ae0666a2188c5f5d11a62cac9093464efbedc1f69f4e1/ckzg-2.1.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0171484eedc42b9417a79e33aff3f35d48915b01c54f42c829b891947ac06551", size = 173548, upload-time = "2025-09-30T19:07:58.555Z" },
+ { url = "https://files.pythonhosted.org/packages/03/ad/b5a88a445f27dbd39eece56edffbe986bf356003bded75f79ef59e2b37c9/ckzg-2.1.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2342b98acd7b6e6e33fbbc48ccec9093e1652461daf4353115adcd708498efcd", size = 188988, upload-time = "2025-09-30T19:07:59.496Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/57/42fbf29d39bd3f11a673a4e61af41b5485aa0ecf99473a0d4afc2528d24b/ckzg-2.1.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:cbce75c1e17fa60b5c33bae5069b8533cf5a4d028ef7d1f755b14a16f72307cf", size = 183513, upload-time = "2025-09-30T19:08:00.341Z" },
+ { url = "https://files.pythonhosted.org/packages/27/c0/ef4c9e9256088e5a425cedb80f26e2a0c853128571b027d8174caf97b2f6/ckzg-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:827be2aeffc8a10bfb39b8dad45def82164dfcde735818c4053f5064474ae1b4", size = 100992, upload-time = "2025-09-30T19:08:01.633Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/4b/089392b6f0015bb368b453f26330c643bf0087f77835df2328a1da2af401/ckzg-2.1.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d955f4e18bb9a9b3a6f55114052edd41650c29edd5f81e417c8f01abace8207", size = 116340, upload-time = "2025-09-30T19:08:02.478Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/45/4d8b70f69f0bc67e9262ec68200707d2d92a27e712cda2c163ebd4b4dcfa/ckzg-2.1.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c0961a685761196264aa49b1cf06e8a2b2add4d57987853d7dd7a7240dc5de7", size = 99822, upload-time = "2025-09-30T19:08:03.65Z" },
+ { url = "https://files.pythonhosted.org/packages/49/f0/1e03c6a491899264117a5a80670a26a569f9eeb67c723157891141d1646f/ckzg-2.1.5-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:026ef3bba0637032c21f6bdb8e92aefeae7c67003bf631a4ee80c515a36a9dbd", size = 180443, upload-time = "2025-11-06T21:05:39.2Z" },
+ { url = "https://files.pythonhosted.org/packages/60/f2/b85b5e5fee12d4ea13060066e9b50260f747a0a5db23634dc199e742894f/ckzg-2.1.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf031139a86e4ff00a717f9539331ef148ae9013b58848f2a7ac14596d812915", size = 166248, upload-time = "2025-11-06T21:05:40.384Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/41/07c5c7471d70d9cc49f2ce5013bb174529f2184611478d176c88c2fa048f/ckzg-2.1.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f51339d58541ae450c78a509b32822eec643595d8b96949fb1963fba802dc78b", size = 175870, upload-time = "2025-11-06T21:05:41.495Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/95/4193e4af65dc4839fa9fe07efad689fe726303b3ba62ee2f46c403458bec/ckzg-2.1.5-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:badb1c7dc6b932bed2c3f7695e1ce3e4bcc9601706136957408ac2bde5dd0892", size = 176586, upload-time = "2025-09-30T19:08:04.818Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/9e/850f48cb41685f5016028dbde8f7846ce9c56bfdc2e9e0f3df1a975263fe/ckzg-2.1.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58d92816b9babaee87bd9f23be10c07d5d07c709be184aa7ea08ddb2bcf2541c", size = 161970, upload-time = "2025-09-30T19:08:05.734Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/df/a9993dc124e95eb30059c108efd83a1504709cf069d3bee0745d450262a0/ckzg-2.1.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cf39f9abe8b3f1a71188fb601a8589672ee40eb0671fc36d8cdf4e78f00f43f", size = 171364, upload-time = "2025-09-30T19:08:06.979Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/03/78e8a723c1b832766e5698f7b39cc8dc27da95b62bc5c738a59564cb5f2c/ckzg-2.1.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:999df675674d8d31528fd9b9afd548e86decc86447f5555b451237e7953fd63f", size = 173571, upload-time = "2025-09-30T19:08:08.173Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/64/27f96201c6d78fbdb9a0812cf45dded974c4d03d876dac11d9c764ef858f/ckzg-2.1.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c39a1c7b32ac345cc44046076fd069ad6b7e6f7bef230ef9be414c712c4453b8", size = 189014, upload-time = "2025-09-30T19:08:09.045Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/6e/82177c4530265694f7ec151821c79351a07706dda4d8b23e8b37d0c122f0/ckzg-2.1.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4564765b0cc65929eca057241b9c030afac1dbae015f129cb60ca6abd6ff620", size = 183530, upload-time = "2025-09-30T19:08:09.867Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/41/1edfbd007b0398321defeedf6ad2d9f86a73f6a99d5ca4b4944bf6f2d757/ckzg-2.1.5-cp313-cp313-win_amd64.whl", hash = "sha256:55013b36514b8176197655b929bc53f020aa51a144331720dead2efc3793ed85", size = 100992, upload-time = "2025-09-30T19:08:10.719Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/07/6ac017fc1593ea8059de1271825eab1f55d0a2f2127e811d5597cc0f328e/ckzg-2.1.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a0cab7deaed093898a92d3644d4ca8621b63cb49296833e2d8b3edac456656d5", size = 116524, upload-time = "2025-11-06T21:05:42.614Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/57/c08133d854dad59d1052ad11796a1c6326c87363049feb8848ee291e68ba/ckzg-2.1.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:caedc9eba3d28584be9b6051585f20745f6abfec0d0657cce3dd45edb7f28586", size = 99833, upload-time = "2025-11-06T21:05:43.647Z" },
+ { url = "https://files.pythonhosted.org/packages/df/80/b07dc3a7581e202dd871a53d8ff65eb70beace3cd81f17e587c3bac64c42/ckzg-2.1.5-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:2f67e545d41ba960189b1011d078953311259674620c485e619c933494b88fd9", size = 180474, upload-time = "2025-11-06T21:05:44.734Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/38/eaa3d40cf5c886966cb32b987f45d6fe07fded3ec2a731b71ca320574849/ckzg-2.1.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6f65ff296033c259d0829093d2c55bb45651e001e0269b8b88d072fdc86ecc6", size = 166274, upload-time = "2025-11-06T21:05:45.882Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/74/a878da70ea299f75c0f279b01bfc46101893a1cc827ead5d5df661ff209a/ckzg-2.1.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d66d34ff33be94c8a1f0da86483cd5bfdc15842998f3654ed91b8fdbffa2a81", size = 175904, upload-time = "2025-11-06T21:05:47.039Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/6f/72029116643f22b70adeb622ead6137af5d504f74f064d08397e972648dc/ckzg-2.1.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:25cf954bae3e2b2db6fa5e811d9800f89199d3eb4fa906c96a1c03434d4893c9", size = 173641, upload-time = "2025-11-06T21:05:48.147Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/67/a618cb1a7b48a810d7dbeeec282ec4337d872111fbdaded2630c224e6566/ckzg-2.1.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:34d7128735e0bcfcac876bff47d0f85e674f1e24f99014e326ec266abed7a82c", size = 189020, upload-time = "2025-11-06T21:05:49.215Z" },
+ { url = "https://files.pythonhosted.org/packages/19/3b/417f0c9a8b40a2876c70384f19fe63289214a6f1480bc86e3a3beaf21b6b/ckzg-2.1.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1dec3efae8679f7b8e26263b8bb0d3061ef4c9c6fe395e55b71f8f0df90ca8a0", size = 183519, upload-time = "2025-11-06T21:05:50.542Z" },
+ { url = "https://files.pythonhosted.org/packages/81/77/5b1c3d31adf65040e52e77f13e38e89707a2ac46e0ca0ecf881a68833944/ckzg-2.1.5-cp314-cp314-win_amd64.whl", hash = "sha256:ce37c0ee0effe55d4ceed1735a2d85a3556a86238f3c89b7b7d1ca4ce4e92358", size = 104038, upload-time = "2025-11-06T21:05:51.677Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/fc/5ebcd1d75513e270440f4517a7423c496c0d025bf730da12c7c8693932c9/ckzg-2.1.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:db804d27f4b08e3aea440cdc6558af4ceb8256b18ea2b83681d80cc654a4085b", size = 116740, upload-time = "2025-11-06T21:05:52.767Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/2e/b661f589b8cdc586304c7a88cc58d48ca34a28200659e1222ffec8a58994/ckzg-2.1.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d472e3beeb95a110275b4d27e51d1c2b26ab99ddb91ac1c5587d710080c39c5e", size = 100101, upload-time = "2025-11-06T21:05:54.007Z" },
+ { url = "https://files.pythonhosted.org/packages/34/3f/88544854ca9623433aba919d85db5f2a3c190922eb7e96bf151b35273c79/ckzg-2.1.5-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:4b44a018124a79138fab8fde25221083574c181c324519be51eab09b1e43ae27", size = 183321, upload-time = "2025-11-06T21:05:55.085Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/11/b9dd3ea012bd215d2aff8e49953e8fe57e62c962eb1e2717663fab5bdc6a/ckzg-2.1.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6a91d7b444300cf8ecae4f55983726630530cdde15cab92023026230a30d094e", size = 169404, upload-time = "2025-11-06T21:05:56.212Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/cf/d695acc82fc7386b65833b2bcfe5b312070f9eb58ae7c5bdfcad7f8e460d/ckzg-2.1.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8674c64efbf2a12edf6d776061847bbe182997737e7690a69af932ce61a9c2a", size = 178676, upload-time = "2025-11-06T21:05:57.528Z" },
+ { url = "https://files.pythonhosted.org/packages/82/35/9319f1d8a8aa2ae9a7779bf6d49a46e6e2af481178eaabbca1ea9d8f9072/ckzg-2.1.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4290aa17c6402c98f16017fd6ee0bff8aeb5c97be5c3cee7c72aea1b7d176f3a", size = 176309, upload-time = "2025-11-06T21:05:59.047Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/24/e28206e43160f411d3ae53f2e557c1905af2928854f7ce4a1be1af893915/ckzg-2.1.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a0f82b8958ea97df12e29094f0a672cbe7532399724ea61b2399545991ed6017", size = 191777, upload-time = "2025-11-06T21:06:00.456Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/ae/51b4e2575d1b4ab76433c6ef56d4dfc1bad38c2f7ffb33353e271c4e4d05/ckzg-2.1.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:22300bf0d717a083c388de5cfafec08443c9938b3abde2e89f9d5d1fffde1c51", size = 186138, upload-time = "2025-11-06T21:06:01.684Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/6e/8ea848be3043b6bf9a7761492719a8c2d2c17a3da7b9551be7ec88a52c01/ckzg-2.1.5-cp314-cp314t-win_amd64.whl", hash = "sha256:aa8228206c3e3729fc117ca38e27588c079b0928a5ab628ee4d9fccaa2b8467d", size = 104191, upload-time = "2025-11-06T21:06:03.188Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7a/9f7534c99f79465ac751b6f9b9445f90f6a0d5ab990c630d888cc9792386/ckzg-2.1.5-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:65960cf6feaf1b281af76efdfedecc536f52e47ec3982c1a2c58c0d1b36a391b", size = 113098, upload-time = "2025-09-30T19:08:43.849Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/d6/8beb84f852cd8a30441d9dfce66679463cfbcb59386b474f6406f12ff979/ckzg-2.1.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f97d29d2ef0bfd4ad4ded126a514ced89c30ee1a30dc5d20d68918263b883c23", size = 95813, upload-time = "2025-09-30T19:08:44.767Z" },
+ { url = "https://files.pythonhosted.org/packages/40/30/4179c4933607e4b233e50676c3d8228234fa543467f801daa04dd8f08dca/ckzg-2.1.5-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3560a4dcd50f3b3a289c8b73657b239bbc6461eb9aa6ef5fe81a242f70591ff4", size = 126632, upload-time = "2025-09-30T19:08:45.55Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/4e/c535ed740c6c949c9382d3c22253d83adf8600612358434f365bc5178c77/ckzg-2.1.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13150a17d6aa76eb39d301440c02e5395540796d30e4d9ae30724ce191c50a28", size = 102843, upload-time = "2025-09-30T19:08:46.915Z" },
+ { url = "https://files.pythonhosted.org/packages/46/ff/efd63e7d9bf6651cbdd68cb28cae40bedeae3bdb8e32d4b7f73fe4ca48a2/ckzg-2.1.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:242e77af051028e4388df7c1ebc68897cf630cf80745f7b26ff0eb6e3ec7a78d", size = 111740, upload-time = "2025-09-30T19:08:47.916Z" },
+ { url = "https://files.pythonhosted.org/packages/22/71/18e05c16a2b7cb74c3ef4a96bee7a1d58b8b3be59db3f1b49a54f292b0b7/ckzg-2.1.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a77975d10c17f617d3a43d664d0f74eb342b2cb3deb9f20860e2e2aaa24643c1", size = 101017, upload-time = "2025-09-30T19:08:49.099Z" },
+ { url = "https://files.pythonhosted.org/packages/03/bd/4cc75266991d2420c3212614dc6e659490bcb8c1a03a638ff688cc775ff8/ckzg-2.1.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0ae260c705a82d9cf4b88eaa2e8f86263c23d99d4ec282f22838f27d24f9306c", size = 113098, upload-time = "2025-09-30T19:08:49.956Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/4c/fc5e6a3463b8d8bb36d21230f41fee13d5ba3fe3f594f1c20bb9bab1fab3/ckzg-2.1.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:7c21b0a4ad05a9e32e715118695d7a0912b4ee73198d63cc98de4d585597627e", size = 95811, upload-time = "2025-09-30T19:08:50.883Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/2e/3be3977c57a51f516f2897d543ddca7901659ae1705b5dc3dbf54e0b66f2/ckzg-2.1.5-pp311-pypy311_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0577aee9848d7a9cef750ff6f303f586caf33da986a762ca57ac0c57e59fb6d", size = 126633, upload-time = "2025-09-30T19:08:51.734Z" },
+ { url = "https://files.pythonhosted.org/packages/72/3b/8314ca493654bd035cc0a66308cec9a1089cf3bc33c2837e3e265345f3cc/ckzg-2.1.5-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e15faa1145b2408e17e3b2f0b159de325b0198615aa30268bb6cd8f4385ed745", size = 102844, upload-time = "2025-09-30T19:08:52.621Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/9b/1729ee420865a71e68f18880341b37ef980c2881674dc0b06460253ef25e/ckzg-2.1.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46d009dba9838630183610008a81bd80aafb389d45d8293d7a2fff7a5ea82266", size = 111739, upload-time = "2025-09-30T19:08:53.54Z" },
+ { url = "https://files.pythonhosted.org/packages/40/40/f259e2bf986d39717427bc12baa8189cd43f9675e81cd3bcab639e593614/ckzg-2.1.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:df66d2be54d91f74aded4ceb71e7b1f789e2636a3015f438904a22ec9de750f1", size = 101018, upload-time = "2025-09-30T19:08:54.391Z" },
+]
+
+[[package]]
+name = "click"
+version = "8.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
+]
+
+[[package]]
+name = "construct"
+version = "2.10.70"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/02/77/8c84b98eca70d245a2a956452f21d57930d22ab88cbeed9290ca630cf03f/construct-2.10.70.tar.gz", hash = "sha256:4d2472f9684731e58cc9c56c463be63baa1447d674e0d66aeb5627b22f512c29", size = 86337, upload-time = "2023-11-29T08:44:49.545Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b2/fb/08b3f4bf05da99aba8ffea52a558758def16e8516bc75ca94ff73587e7d3/construct-2.10.70-py3-none-any.whl", hash = "sha256:c80be81ef595a1a821ec69dc16099550ed22197615f4320b57cc9ce2a672cb30", size = 63020, upload-time = "2023-11-29T08:44:46.876Z" },
+]
+
+[[package]]
+name = "construct-typing"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "construct" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f6/ae/659fe4866d89ef5a3a65cddbdd7b35882f4feb72db383821965f2fcea934/construct_typing-0.7.0.tar.gz", hash = "sha256:71d110dedff39bd3b603c734077032a7065bc597a49db1f5b03a211d05dbac23", size = 45104, upload-time = "2025-10-27T19:30:29.614Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8c/0c/2db6f7e1ae9795e436c6a0dc0bc38b12b8c8a228cb63203e24190b755b3b/construct_typing-0.7.0-py3-none-any.whl", hash = "sha256:c92383c6e8e5d07ba25811c8d5163820458d821e73bb1006541f43f89788646c", size = 24350, upload-time = "2025-10-27T19:30:27.505Z" },
+]
+
+[[package]]
+name = "cytoolz"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "toolz" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bd/d4/16916f3dc20a3f5455b63c35dcb260b3716f59ce27a93586804e70e431d5/cytoolz-1.1.0.tar.gz", hash = "sha256:13a7bf254c3c0d28b12e2290b82aed0f0977a4c2a2bf84854fcdc7796a29f3b0", size = 642510, upload-time = "2025-10-19T00:44:56.174Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/7a/3244e6e3587be9abfee3b1c320e43a279831b3c3a31fe5d08c1ee6193e6b/cytoolz-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:72d7043a88ea5e61ba9d17ea0d1c1eff10f645d7edfcc4e56a31ef78be287644", size = 1307813, upload-time = "2025-10-19T00:39:34.198Z" },
+ { url = "https://files.pythonhosted.org/packages/32/7e/eaf504ca59addce323ef4d4ffedc2913d83c121ec19f6419bc402f7702dc/cytoolz-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d759e9ed421bacfeb456d47af8d734c057b9912b5f2441f95b27ca35e5efab07", size = 985777, upload-time = "2025-10-19T00:39:36.545Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/a1/ec95443f0cf4cd0dbc574fa26ac85a0442d35f3b601a90a0e3dda077f614/cytoolz-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fdb5be8fbcc0396141189022724155a4c1c93712ac4aef8c03829af0c2a816d7", size = 982865, upload-time = "2025-10-19T00:39:38.19Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/1b/8503604b0c0534977363fb77d371019395dfa031a216f9b1d8729d1280e4/cytoolz-1.1.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c8c0a513dc89bc05cc72893609118815bced5ef201f1a317b4cc3423b3a0e750", size = 2597969, upload-time = "2025-10-19T00:39:40.26Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/e5/30748da06417cb2d4bc58e380b0c11d8c6539f4e289dc1e4f4b4fc248d0e/cytoolz-1.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce94db4f8ebe842c30c0ece42ff5de977c47859088c2c363dede5a68f6906484", size = 2692230, upload-time = "2025-10-19T00:39:42.327Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/84/e06580b74deb97dfd3513e4e6b660c2dedc220c7653f5bd3e4f772f4d885/cytoolz-1.1.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b622d4f54e370c853ded94a668f94fe72c6d70e06ac102f17a2746661c27ab52", size = 2565243, upload-time = "2025-10-19T00:39:44.403Z" },
+ { url = "https://files.pythonhosted.org/packages/91/5e/79c0122a34c33afcb5aaee1fec35be24fe16cecefb9bb8890f2908feae56/cytoolz-1.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:375a65baa5a5b4ff6a0c5ff17e170cf23312e4c710755771ca966144c24216b5", size = 2868602, upload-time = "2025-10-19T00:39:46.051Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/84/404698ff02b32292db1e39cc4a2fbdabe15164b092cc364902984c3ce0f4/cytoolz-1.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c0d51bcdb3203a062a78f66bbe33db5e3123048e24a5f0e1402422d79df8ee2d", size = 2905121, upload-time = "2025-10-19T00:39:48.078Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/33/afad6593829ba73fc87b5ae64441e380fc937f79f24a1cda60d23cb99b8c/cytoolz-1.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1010869529bb05dc9802b6d776a34ca1b6d48b9deec70ad5e2918ae175be5c2f", size = 2684382, upload-time = "2025-10-19T00:39:49.766Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/86/7900013a82ca9c6cadbfb22bf50d0fbfc3b192915d2bdd9fab3f69a9afba/cytoolz-1.1.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:11a8f2e83295bdb33f35454d6bafcb7845b03b5881dcaed66ecbd726c7f16772", size = 2518183, upload-time = "2025-10-19T00:39:51.433Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/4b/acf9be2953fed6a6d795fb66de37c367915037a998a5b3d3b69476cf91fe/cytoolz-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0499c5e0a8e688ed367a2e51cc13792ae8f08226c15f7d168589fc44b9b9cada", size = 2609368, upload-time = "2025-10-19T00:39:53.458Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/ec/3e30455fd526f5cc37bd3dd2a0e2aafb803ae4d271e50ce53bfc30810053/cytoolz-1.1.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:87d44e6033d4c5e95a7d39ba59b8e105ba1c29b1ccd1d215f26477cc1d64be39", size = 2561458, upload-time = "2025-10-19T00:39:55.493Z" },
+ { url = "https://files.pythonhosted.org/packages/49/27/e5815c85bb18cdf95780f9596dcfd76dee910a4d635a1924648cb8a636c6/cytoolz-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a68cef396a7de237f7b97422a6a450dfb111722296ba217ba5b34551832f1f6e", size = 2578236, upload-time = "2025-10-19T00:39:57.512Z" },
+ { url = "https://files.pythonhosted.org/packages/17/db/588e266eff397670398ea335a809152e77b02ee92e0ec42091115b42f09b/cytoolz-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:06ad4c95b258141f138a93ebfdc1d76ac087afc1a82f1401100a1f44b44ba656", size = 2770523, upload-time = "2025-10-19T00:39:59.194Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/ad/82be0b999c7a0a0b362cedfc183eb090b872fd42937af2d6e97d58bc70f8/cytoolz-1.1.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ada59a4b3c59d4ac7162e0ed08667ffa78abf48e975c8a9f9d5b9bc50720f4fd", size = 2512909, upload-time = "2025-10-19T00:40:01.199Z" },
+ { url = "https://files.pythonhosted.org/packages/25/21/45f07ab0339a20c518bc9006100922babc397ab7ea5ef40a395db83b9cdd/cytoolz-1.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a8957bcaea1ba01327a9b219d2adb84144377684f51444253890dab500ca171f", size = 2755345, upload-time = "2025-10-19T00:40:03.322Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/a7/e530bf2b304206f79b36d793caba1ff9448348713a41bb1ad0197714a0f2/cytoolz-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6d8cdc299d67eb0f3b9ecdafeeb55eb3b7b7470e2d950ac34b05ed4c7a5572b8", size = 2617790, upload-time = "2025-10-19T00:40:05.03Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/77/7f53092121d7431589344c7d65c3d43c4111547aafabb21d3ca9032d126c/cytoolz-1.1.0-cp310-cp310-win32.whl", hash = "sha256:d8e08464c5cdea4f6df31e84b11ed6bfd79cedb99fbcbfdc15eb9361a6053c5a", size = 900209, upload-time = "2025-10-19T00:40:06.647Z" },
+ { url = "https://files.pythonhosted.org/packages/84/e4/902578658303b9bc76b1704d3ed85e6d307d311bd9fa0b919581bea56e62/cytoolz-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7e49922a7ed54262d41960bf3b835a7700327bf79cff1e9bfc73d79021132ff8", size = 944802, upload-time = "2025-10-19T00:40:08.983Z" },
+ { url = "https://files.pythonhosted.org/packages/71/9f/56a7003617b4eabd8ddfb470aacc240425cbe6ddeb756adfbbaadaa175f1/cytoolz-1.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:943a662d2e72ffc4438d43ab5a1de8d852237775a423236594a3b3e381b8032c", size = 904835, upload-time = "2025-10-19T00:40:11.024Z" },
+ { url = "https://files.pythonhosted.org/packages/69/82/edf1d0c32b6222f2c22e5618d6db855d44eb59f9b6f22436ff963c5d0a5c/cytoolz-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dba8e5a8c6e3c789d27b0eb5e7ce5ed7d032a7a9aae17ca4ba5147b871f6e327", size = 1314345, upload-time = "2025-10-19T00:40:13.273Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/b5/0e3c1edaa26c2bd9db90cba0ac62c85bbca84224c7ae1c2e0072c4ea64c5/cytoolz-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:44b31c05addb0889167a720123b3b497b28dd86f8a0aeaf3ae4ffa11e2c85d55", size = 989259, upload-time = "2025-10-19T00:40:15.196Z" },
+ { url = "https://files.pythonhosted.org/packages/09/aa/e2b2ee9fc684867e817640764ea5807f9d25aa1e7bdba02dd4b249aab0f7/cytoolz-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:653cb18c4fc5d8a8cfce2bce650aabcbe82957cd0536827367d10810566d5294", size = 986551, upload-time = "2025-10-19T00:40:16.831Z" },
+ { url = "https://files.pythonhosted.org/packages/39/9f/4e8ee41acf6674f10a9c2c9117b2f219429a5a0f09bba6135f34ca4f08a6/cytoolz-1.1.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:853a5b4806915020c890e1ce70cc056bbc1dd8bc44f2d74d555cccfd7aefba7d", size = 2688378, upload-time = "2025-10-19T00:40:18.552Z" },
+ { url = "https://files.pythonhosted.org/packages/78/94/ef006f3412bc22444d855a0fc9ecb81424237fb4e5c1a1f8f5fb79ac978f/cytoolz-1.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7b44e9de86bea013fe84fd8c399d6016bbb96c37c5290769e5c99460b9c53e5", size = 2798299, upload-time = "2025-10-19T00:40:20.191Z" },
+ { url = "https://files.pythonhosted.org/packages/df/aa/365953926ee8b4f2e07df7200c0d73632155908c8867af14b2d19cc9f1f7/cytoolz-1.1.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:098d628a801dc142e9740126be5624eb7aef1d732bc7a5719f60a2095547b485", size = 2639311, upload-time = "2025-10-19T00:40:22.289Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ee/62beaaee7df208f22590ad07ef8875519af49c52ca39d99460b14a00f15a/cytoolz-1.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:779ee4096ed7a82cffab89372ffc339631c285079dbf33dbe7aff1f6174985df", size = 2979532, upload-time = "2025-10-19T00:40:24.006Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/04/2211251e450bed111ada1194dc42c461da9aea441de62a01e4085ea6de9f/cytoolz-1.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f2ce18dd99533d077e9712f9faa852f389f560351b1efd2f2bdb193a95eddde2", size = 3018632, upload-time = "2025-10-19T00:40:26.175Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/a2/4a3400e4d07d3916172bf74fede08020d7b4df01595d8a97f1e9507af5ae/cytoolz-1.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac266a34437812cf841cecbfe19f355ab9c3dd1ef231afc60415d40ff12a76e4", size = 2788579, upload-time = "2025-10-19T00:40:27.878Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/82/bb88caa53a41f600e7763c517d50e2efbbe6427ea395716a92b83f44882a/cytoolz-1.1.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1920b9b9c13d60d0bb6cd14594b3bce0870022eccb430618c37156da5f2b7a55", size = 2593024, upload-time = "2025-10-19T00:40:29.601Z" },
+ { url = "https://files.pythonhosted.org/packages/09/a8/8b25e59570da16c7a0f173b8c6ec0aa6f3abd47fd385c007485acb459896/cytoolz-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47caa376dafd2bdc29f8a250acf59c810ec9105cd6f7680b9a9d070aae8490ec", size = 2715304, upload-time = "2025-10-19T00:40:31.151Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/56/faec7696f235521b926ffdf92c102f5b029f072d28e1020364e55b084820/cytoolz-1.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5ab2c97d8aaa522b038cca9187b1153347af22309e7c998b14750c6fdec7b1cb", size = 2654461, upload-time = "2025-10-19T00:40:32.884Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/82/f790ed167c04b8d2a33bed30770a9b7066fc4f573321d797190e5f05685f/cytoolz-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4bce006121b120e8b359244ee140bb0b1093908efc8b739db8dbaa3f8fb42139", size = 2672077, upload-time = "2025-10-19T00:40:34.543Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/b3/80b8183e7eee44f45bfa3cdd3ebdadf3dd43ffc686f96d442a6c4dded45d/cytoolz-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fc0f1e4e9bb384d26e73c6657bbc26abdae4ff66a95933c00f3d578be89181b", size = 2881589, upload-time = "2025-10-19T00:40:36.315Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/05/ac5ba5ddb88a3ba7ecea4bf192194a838af564d22ea7a4812cbb6bd106ce/cytoolz-1.1.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:dd3f894ff972da1994d06ac6157d74e40dda19eb31fe5e9b7863ca4278c3a167", size = 2589924, upload-time = "2025-10-19T00:40:38.317Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/cd/100483cae3849d24351c8333a815dc6adaf3f04912486e59386d86d9db9a/cytoolz-1.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0846f49cf8a4496bd42659040e68bd0484ce6af819709cae234938e039203ba0", size = 2868059, upload-time = "2025-10-19T00:40:40.025Z" },
+ { url = "https://files.pythonhosted.org/packages/34/6e/3a7c56b325772d39397fc3aafb4dc054273982097178b6c3917c6dad48de/cytoolz-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:16a3af394ade1973226d64bb2f9eb3336adbdea03ed5b134c1bbec5a3b20028e", size = 2721692, upload-time = "2025-10-19T00:40:41.621Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/ca/9fdaee32c3bc769dfb7e7991d9499136afccea67e423d097b8fb3c5acbc1/cytoolz-1.1.0-cp311-cp311-win32.whl", hash = "sha256:b786c9c8aeab76cc2f76011e986f7321a23a56d985b77d14f155d5e5514ea781", size = 899349, upload-time = "2025-10-19T00:40:43.183Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/04/2ab98edeea90311e4029e1643e43d2027b54da61453292d9ea51a103ee87/cytoolz-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:ebf06d1c5344fb22fee71bf664234733e55db72d74988f2ecb7294b05e4db30c", size = 945831, upload-time = "2025-10-19T00:40:44.693Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/8d/777d86ea6bcc68b0fc926b0ef8ab51819e2176b37aadea072aac949d5231/cytoolz-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:b63f5f025fac893393b186e132e3e242de8ee7265d0cd3f5bdd4dda93f6616c9", size = 904076, upload-time = "2025-10-19T00:40:46.678Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/ec/01426224f7acf60183d3921b25e1a8e71713d3d39cb464d64ac7aace6ea6/cytoolz-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:99f8e134c9be11649342853ec8c90837af4089fc8ff1e8f9a024a57d1fa08514", size = 1327800, upload-time = "2025-10-19T00:40:48.674Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/07/e07e8fedd332ac9626ad58bea31416dda19bfd14310731fa38b16a97e15f/cytoolz-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0a6f44cf9319c30feb9a50aa513d777ef51efec16f31c404409e7deb8063df64", size = 997118, upload-time = "2025-10-19T00:40:50.919Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/72/c0f766d63ed2f9ea8dc8e1628d385d99b41fb834ce17ac3669e3f91e115d/cytoolz-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:945580dc158c557172fca899a35a99a16fbcebf6db0c77cb6621084bc82189f9", size = 991169, upload-time = "2025-10-19T00:40:52.887Z" },
+ { url = "https://files.pythonhosted.org/packages/df/4b/1f757353d1bf33e56a7391ecc9bc49c1e529803b93a9d2f67fe5f92906fe/cytoolz-1.1.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:257905ec050d04f2f856854620d1e25556fd735064cebd81b460f54939b9f9d5", size = 2700680, upload-time = "2025-10-19T00:40:54.597Z" },
+ { url = "https://files.pythonhosted.org/packages/25/73/9b25bb7ed8d419b9d6ff2ae0b3d06694de79a3f98f5169a1293ff7ad3a3f/cytoolz-1.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82779049f352fb3ab5e8c993ab45edbb6e02efb1f17f0b50f4972c706cc51d76", size = 2824951, upload-time = "2025-10-19T00:40:56.137Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/93/9c787f7c909e75670fff467f2504725d06d8c3f51d6dfe22c55a08c8ccd4/cytoolz-1.1.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7d3e405e435320e08c5a1633afaf285a392e2d9cef35c925d91e2a31dfd7a688", size = 2679635, upload-time = "2025-10-19T00:40:57.799Z" },
+ { url = "https://files.pythonhosted.org/packages/50/aa/9ee92c302cccf7a41a7311b325b51ebeff25d36c1f82bdc1bbe3f58dc947/cytoolz-1.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:923df8f5591e0d20543060c29909c149ab1963a7267037b39eee03a83dbc50a8", size = 2938352, upload-time = "2025-10-19T00:40:59.49Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/a3/3b58c5c1692c3bacd65640d0d5c7267a7ebb76204f7507aec29de7063d2f/cytoolz-1.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:25db9e4862f22ea0ae2e56c8bec9fc9fd756b655ae13e8c7b5625d7ed1c582d4", size = 3022121, upload-time = "2025-10-19T00:41:01.209Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/93/c647bc3334355088c57351a536c2d4a83dd45f7de591fab383975e45bff9/cytoolz-1.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7a98deb11ccd8e5d9f9441ef2ff3352aab52226a2b7d04756caaa53cd612363", size = 2857656, upload-time = "2025-10-19T00:41:03.456Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/c2/43fea146bf4141deea959e19dcddf268c5ed759dec5c2ed4a6941d711933/cytoolz-1.1.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dce4ee9fc99104bc77efdea80f32ca5a650cd653bcc8a1d984a931153d3d9b58", size = 2551284, upload-time = "2025-10-19T00:41:05.347Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/df/cdc7a81ce5cfcde7ef523143d545635fc37e80ccacce140ae58483a21da3/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80d6da158f7d20c15819701bbda1c041f0944ede2f564f5c739b1bc80a9ffb8b", size = 2721673, upload-time = "2025-10-19T00:41:07.528Z" },
+ { url = "https://files.pythonhosted.org/packages/45/be/f8524bb9ad8812ad375e61238dcaa3177628234d1b908ad0b74e3657cafd/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:3b5c5a192abda123ad45ef716ec9082b4cf7d95e9ada8291c5c2cc5558be858b", size = 2722884, upload-time = "2025-10-19T00:41:09.698Z" },
+ { url = "https://files.pythonhosted.org/packages/23/e6/6bb8e4f9c267ad42d1ff77b6d2e4984665505afae50a216290e1d7311431/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5b399ce7d967b1cb6280250818b786be652aa8ddffd3c0bb5c48c6220d945ab5", size = 2685486, upload-time = "2025-10-19T00:41:11.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/dd/88619f9c8d2b682562c0c886bbb7c35720cb83fda2ac9a41bdd14073d9bd/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e7e29a1a03f00b4322196cfe8e2c38da9a6c8d573566052c586df83aacc5663c", size = 2839661, upload-time = "2025-10-19T00:41:13.053Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/8d/4478ebf471ee78dd496d254dc0f4ad729cd8e6ba8257de4f0a98a2838ef2/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5291b117d71652a817ec164e7011f18e6a51f8a352cc9a70ed5b976c51102fda", size = 2547095, upload-time = "2025-10-19T00:41:16.054Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/68/f1dea33367b0b3f64e199c230a14a6b6f243c189020effafd31e970ca527/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8caef62f846a9011676c51bda9189ae394cdd6bb17f2946ecaedc23243268320", size = 2870901, upload-time = "2025-10-19T00:41:17.727Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/9a/33591c09dfe799b8fb692cf2ad383e2c41ab6593cc960b00d1fc8a145655/cytoolz-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:de425c5a8e3be7bb3a195e19191d28d9eb3c2038046064a92edc4505033ec9cb", size = 2765422, upload-time = "2025-10-19T00:41:20.075Z" },
+ { url = "https://files.pythonhosted.org/packages/60/2b/a8aa233c9416df87f004e57ae4280bd5e1f389b4943d179f01020c6ec629/cytoolz-1.1.0-cp312-cp312-win32.whl", hash = "sha256:296440a870e8d1f2e1d1edf98f60f1532b9d3ab8dfbd4b25ec08cd76311e79e5", size = 901933, upload-time = "2025-10-19T00:41:21.646Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/33/4c9bdf8390dc01d2617c7f11930697157164a52259b6818ddfa2f94f89f4/cytoolz-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:07156987f224c6dac59aa18fb8bf91e1412f5463961862716a3381bf429c8699", size = 947989, upload-time = "2025-10-19T00:41:23.288Z" },
+ { url = "https://files.pythonhosted.org/packages/35/ac/6e2708835875f5acb52318462ed296bf94ed0cb8c7cb70e62fbd03f709e3/cytoolz-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:23e616b38f5b3160c7bb45b0f84a8f3deb4bd26b29fb2dfc716f241c738e27b8", size = 903913, upload-time = "2025-10-19T00:41:24.992Z" },
+ { url = "https://files.pythonhosted.org/packages/71/4a/b3ddb3ee44fe0045e95dd973746f93f033b6f92cce1fc3cbbe24b329943c/cytoolz-1.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:76c9b58555300be6dde87a41faf1f97966d79b9a678b7a526fcff75d28ef4945", size = 976728, upload-time = "2025-10-19T00:41:26.5Z" },
+ { url = "https://files.pythonhosted.org/packages/42/21/a3681434aa425875dd828bb515924b0f12c37a55c7d2bc5c0c5de3aeb0b4/cytoolz-1.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d1d638b10d3144795655e9395566ce35807df09219fd7cacd9e6acbdef67946a", size = 986057, upload-time = "2025-10-19T00:41:28.911Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/cb/efc1b29e211e0670a6953222afaac84dcbba5cb940b130c0e49858978040/cytoolz-1.1.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:26801c1a165e84786a99e03c9c9973356caaca002d66727b761fb1042878ef06", size = 992632, upload-time = "2025-10-19T00:41:30.612Z" },
+ { url = "https://files.pythonhosted.org/packages/be/b0/e50621d21e939338c97faab651f58ea7fa32101226a91de79ecfb89d71e1/cytoolz-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2a9a464542912d3272f6dccc5142df057c71c6a5cbd30439389a732df401afb7", size = 1317534, upload-time = "2025-10-19T00:41:32.625Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/6b/25aa9739b0235a5bc4c1ea293186bc6822a4c6607acfe1422423287e7400/cytoolz-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ed6104fa942aa5784bf54f339563de637557e3443b105760bc4de8f16a7fc79b", size = 992336, upload-time = "2025-10-19T00:41:34.073Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/53/5f4deb0ff958805309d135d899c764364c1e8a632ce4994bd7c45fb98df2/cytoolz-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56161f0ab60dc4159ec343509abaf809dc88e85c7e420e354442c62e3e7cbb77", size = 986118, upload-time = "2025-10-19T00:41:35.7Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/e3/f6255b76c8cc0debbe1c0779130777dc0434da6d9b28a90d9f76f8cb67cd/cytoolz-1.1.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:832bd36cc9123535f1945acf6921f8a2a15acc19cfe4065b1c9b985a28671886", size = 2679563, upload-time = "2025-10-19T00:41:37.926Z" },
+ { url = "https://files.pythonhosted.org/packages/59/8a/acc6e39a84e930522b965586ad3a36694f9bf247b23188ee0eb47b1c9ed1/cytoolz-1.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1842636b6e034f229bf084c2bcdcfd36c8437e752eefd2c74ce9e2f10415cb6e", size = 2813020, upload-time = "2025-10-19T00:41:39.935Z" },
+ { url = "https://files.pythonhosted.org/packages/db/f5/0083608286ad1716eda7c41f868e85ac549f6fd6b7646993109fa0bdfd98/cytoolz-1.1.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:823df012ab90d2f2a0f92fea453528539bf71ac1879e518524cd0c86aa6df7b9", size = 2669312, upload-time = "2025-10-19T00:41:41.55Z" },
+ { url = "https://files.pythonhosted.org/packages/47/a8/d16080b575520fe5da00cede1ece4e0a4180ec23f88dcdc6a2f5a90a7f7f/cytoolz-1.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f1fcf9e7e7b3487883ff3f815abc35b89dcc45c4cf81c72b7ee457aa72d197b", size = 2922147, upload-time = "2025-10-19T00:41:43.252Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/bc/716c9c1243701e58cad511eb3937fd550e645293c5ed1907639c5d66f194/cytoolz-1.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4cdb3fa1772116827f263f25b0cdd44c663b6701346a56411960534a06c082de", size = 2981602, upload-time = "2025-10-19T00:41:45.354Z" },
+ { url = "https://files.pythonhosted.org/packages/14/bc/571b232996846b27f4ac0c957dc8bf60261e9b4d0d01c8d955e82329544e/cytoolz-1.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1b5c95041741b81430454db65183e133976f45ac3c03454cfa8147952568529", size = 2830103, upload-time = "2025-10-19T00:41:47.959Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/55/c594afb46ecd78e4b7e1fb92c947ed041807875661ceda73baaf61baba4f/cytoolz-1.1.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b2079fd9f1a65f4c61e6278c8a6d4f85edf30c606df8d5b32f1add88cbbe2286", size = 2533802, upload-time = "2025-10-19T00:41:49.683Z" },
+ { url = "https://files.pythonhosted.org/packages/93/83/1edcf95832555a78fc43b975f3ebe8ceadcc9664dd47fd33747a14df5069/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a92a320d72bef1c7e2d4c6d875125cf57fc38be45feb3fac1bfa64ea401f54a4", size = 2706071, upload-time = "2025-10-19T00:41:51.386Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/df/035a408df87f25cfe3611557818b250126cd2281b2104cd88395de205583/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06d1c79aa51e6a92a90b0e456ebce2288f03dd6a76c7f582bfaa3eda7692e8a5", size = 2707575, upload-time = "2025-10-19T00:41:53.305Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/a4/ef78e13e16e93bf695a9331321d75fbc834a088d941f1c19e6b63314e257/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e1d7be25f6971e986a52b6d3a0da28e1941850985417c35528f6823aef2cfec5", size = 2660486, upload-time = "2025-10-19T00:41:55.542Z" },
+ { url = "https://files.pythonhosted.org/packages/30/7a/2c3d60682b26058d435416c4e90d4a94db854de5be944dfd069ed1be648a/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:964b248edc31efc50a65e9eaa0c845718503823439d2fa5f8d2c7e974c2b5409", size = 2819605, upload-time = "2025-10-19T00:41:58.257Z" },
+ { url = "https://files.pythonhosted.org/packages/45/92/19b722a1d83cc443fbc0c16e0dc376f8a451437890d3d9ee370358cf0709/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c9ff2b3c57c79b65cb5be14a18c6fd4a06d5036fb3f33e973a9f70e9ac13ca28", size = 2533559, upload-time = "2025-10-19T00:42:00.324Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/15/fa3b7891da51115204416f14192081d3dea0eaee091f123fdc1347de8dd1/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:22290b73086af600042d99f5ce52a43d4ad9872c382610413176e19fc1d4fd2d", size = 2839171, upload-time = "2025-10-19T00:42:01.881Z" },
+ { url = "https://files.pythonhosted.org/packages/46/40/d3519d5cd86eebebf1e8b7174ec32dfb6ecec67b48b0cfb92bf226659b5a/cytoolz-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a2ade74fccd080ea793382968913ee38d7a35c921df435bbf0a6aeecf0d17574", size = 2743379, upload-time = "2025-10-19T00:42:03.809Z" },
+ { url = "https://files.pythonhosted.org/packages/93/e2/a9e7511f0a13fdbefa5bf73cf8e4763878140de9453fd3e50d6ac57b6be7/cytoolz-1.1.0-cp313-cp313-win32.whl", hash = "sha256:db5dbcfda1c00e937426cbf9bdc63c24ebbc358c3263bfcbc1ab4a88dc52aa8e", size = 900844, upload-time = "2025-10-19T00:42:05.967Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/a4/fb7eb403c6a4c81e5a30363f34a71adcc8bf5292dc8ea32e2440aa5668f2/cytoolz-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9e2d3fe3b45c3eb7233746f7aca37789be3dceec3e07dcc406d3e045ea0f7bdc", size = 946461, upload-time = "2025-10-19T00:42:07.983Z" },
+ { url = "https://files.pythonhosted.org/packages/93/bb/1c8c33d353548d240bc6e8677ee8c3560ce5fa2f084e928facf7c35a6dcf/cytoolz-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:32c559f95ff44a9ebcbd934acaa1e6dc8f3e6ffce4762a79a88528064873d6d5", size = 902673, upload-time = "2025-10-19T00:42:09.982Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/ba/4a53acc60f59030fcaf48c7766e3c4c81bd997379425aa45b129396557b5/cytoolz-1.1.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9e2cd93b28f667c5870a070ab2b8bb4397470a85c4b204f2454b0ad001cd1ca3", size = 1372336, upload-time = "2025-10-19T00:42:12.104Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/90/f28fd8ad8319d8f5c8da69a2c29b8cf52a6d2c0161602d92b366d58926ab/cytoolz-1.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f494124e141a9361f31d79875fe7ea459a3be2b9dadd90480427c0c52a0943d4", size = 1011930, upload-time = "2025-10-19T00:42:14.231Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/95/4561c4e0ad1c944f7673d6d916405d68080f10552cfc5d69a1cf2475a9a1/cytoolz-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53a3262bf221f19437ed544bf8c0e1980c81ac8e2a53d87a9bc075dba943d36f", size = 1020610, upload-time = "2025-10-19T00:42:15.877Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/14/b2e1ffa4995ec36e1372e243411ff36325e4e6d7ffa34eb4098f5357d176/cytoolz-1.1.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:47663e57d3f3f124921f38055e86a1022d0844c444ede2e8f090d3bbf80deb65", size = 2917327, upload-time = "2025-10-19T00:42:17.706Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/29/7cab6c609b4514ac84cca2f7dca6c509977a8fc16d27c3a50e97f105fa6a/cytoolz-1.1.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a5a8755c4104ee4e3d5ba434c543b5f85fdee6a1f1df33d93f518294da793a60", size = 3108951, upload-time = "2025-10-19T00:42:19.363Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/71/1d1103b819458679277206ad07d78ca6b31c4bb88d6463fd193e19bfb270/cytoolz-1.1.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4d96ff3d381423af1b105295f97de86d1db51732c9566eb37378bab6670c5010", size = 2807149, upload-time = "2025-10-19T00:42:20.964Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/d4/3d83a05a21e7d2ed2b9e6daf489999c29934b005de9190272b8a2e3735d0/cytoolz-1.1.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0ec96b3d537cdf47d4e76ded199f7440715f4c71029b45445cff92c1248808c2", size = 3111608, upload-time = "2025-10-19T00:42:22.684Z" },
+ { url = "https://files.pythonhosted.org/packages/51/88/96f68354c3d4af68de41f0db4fe41a23b96a50a4a416636cea325490cfeb/cytoolz-1.1.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:208e2f2ef90a32b0acbff3303d90d89b13570a228d491d2e622a7883a3c68148", size = 3179373, upload-time = "2025-10-19T00:42:24.395Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/50/ed87a5cd8e6f27ffbb64c39e9730e18ec66c37631db2888ae711909f10c9/cytoolz-1.1.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d416a81bb0bd517558668e49d30a7475b5445f9bbafaab7dcf066f1e9adba36", size = 3003120, upload-time = "2025-10-19T00:42:26.18Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/a7/acde155b050d6eaa8e9c7845c98fc5fb28501568e78e83ebbf44f8855274/cytoolz-1.1.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f32e94c91ffe49af04835ee713ebd8e005c85ebe83e7e1fdcc00f27164c2d636", size = 2703225, upload-time = "2025-10-19T00:42:27.93Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/b6/9d518597c5bdea626b61101e8d2ff94124787a42259dafd9f5fc396f346a/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15d0c6405efc040499c46df44056a5c382f551a7624a41cf3e4c84a96b988a15", size = 2956033, upload-time = "2025-10-19T00:42:29.993Z" },
+ { url = "https://files.pythonhosted.org/packages/89/7a/93e5f860926165538c85e1c5e1670ad3424f158df810f8ccd269da652138/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:bf069c5381d757debae891401b88b3a346ba3a28ca45ba9251103b282463fad8", size = 2862950, upload-time = "2025-10-19T00:42:31.803Z" },
+ { url = "https://files.pythonhosted.org/packages/76/e6/99d6af00487bedc27597b54c9fcbfd5c833a69c6b7a9b9f0fff777bfc7aa/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d5cf15892e63411ec1bd67deff0e84317d974e6ab2cdfefdd4a7cea2989df66", size = 2861757, upload-time = "2025-10-19T00:42:33.625Z" },
+ { url = "https://files.pythonhosted.org/packages/71/ca/adfa1fb7949478135a37755cb8e88c20cd6b75c22a05f1128f05f3ab2c60/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3e3872c21170f8341656f8692f8939e8800dcee6549ad2474d4c817bdefd62cd", size = 2979049, upload-time = "2025-10-19T00:42:35.377Z" },
+ { url = "https://files.pythonhosted.org/packages/70/4c/7bf47a03a4497d500bc73d4204e2d907771a017fa4457741b2a1d7c09319/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:b9ddeff8e8fd65eb1fcefa61018100b2b627e759ea6ad275d2e2a93ffac147bf", size = 2699492, upload-time = "2025-10-19T00:42:37.133Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/e7/3d034b0e4817314f07aa465d5864e9b8df9d25cb260a53dd84583e491558/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:02feeeda93e1fa3b33414eb57c2b0aefd1db8f558dd33fdfcce664a0f86056e4", size = 2995646, upload-time = "2025-10-19T00:42:38.912Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/62/be357181c71648d9fe1d1ce91cd42c63457dcf3c158e144416fd51dced83/cytoolz-1.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d08154ad45349162b6c37f12d5d1b2e6eef338e657b85e1621e4e6a4a69d64cb", size = 2919481, upload-time = "2025-10-19T00:42:40.85Z" },
+ { url = "https://files.pythonhosted.org/packages/62/d5/bf5434fde726c4f80cb99912b2d8e0afa1587557e2a2d7e0315eb942f2de/cytoolz-1.1.0-cp313-cp313t-win32.whl", hash = "sha256:10ae4718a056948d73ca3e1bb9ab1f95f897ec1e362f829b9d37cc29ab566c60", size = 951595, upload-time = "2025-10-19T00:42:42.877Z" },
+ { url = "https://files.pythonhosted.org/packages/64/29/39c161e9204a9715321ddea698cbd0abc317e78522c7c642363c20589e71/cytoolz-1.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:1bb77bc6197e5cb19784b6a42bb0f8427e81737a630d9d7dda62ed31733f9e6c", size = 1004445, upload-time = "2025-10-19T00:42:44.855Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/5a/7cbff5e9a689f558cb0bdf277f9562b2ac51acf7cd15e055b8c3efb0e1ef/cytoolz-1.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:563dda652c6ff52d215704fbe6b491879b78d7bbbb3a9524ec8e763483cb459f", size = 926207, upload-time = "2025-10-19T00:42:46.456Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/e8/297a85ba700f437c01eba962428e6ab4572f6c3e68e8ff442ce5c9d3a496/cytoolz-1.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d542cee7c7882d2a914a33dec4d3600416fb336734df979473249d4c53d207a1", size = 980613, upload-time = "2025-10-19T00:42:47.988Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/d7/2b02c9d18e9cc263a0e22690f78080809f1eafe72f26b29ccc115d3bf5c8/cytoolz-1.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31922849b701b0f24bb62e56eb2488dcd3aa6ae3057694bd6b3b7c4c2bc27c2f", size = 990476, upload-time = "2025-10-19T00:42:49.653Z" },
+ { url = "https://files.pythonhosted.org/packages/89/26/b6b159d2929310fca0eff8a4989cd4b1ecbdf7c46fdff46c7a20fcae55c8/cytoolz-1.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e68308d32afd31943314735c1335e4ab5696110e96b405f6bdb8f2a8dc771a16", size = 992712, upload-time = "2025-10-19T00:42:51.306Z" },
+ { url = "https://files.pythonhosted.org/packages/42/a0/f7c572aa151ed466b0fce4a327c3cc916d3ef3c82e341be59ea4b9bee9e4/cytoolz-1.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fc4bb48b3b866e1867f7c6411a4229e5b44be3989060663713e10efc24c9bd5f", size = 1322596, upload-time = "2025-10-19T00:42:52.978Z" },
+ { url = "https://files.pythonhosted.org/packages/72/7c/a55d035e20b77b6725e85c8f1a418b3a4c23967288b8b0c2d1a40f158cbe/cytoolz-1.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:456f77207d1445025d7ef262b8370a05492dcb1490cb428b0f3bf1bd744a89b0", size = 992825, upload-time = "2025-10-19T00:42:55.026Z" },
+ { url = "https://files.pythonhosted.org/packages/03/af/39d2d3db322136e12e9336a1f13bab51eab88b386bfb11f91d3faff8ba34/cytoolz-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:174ebc71ebb20a9baeffce6ee07ee2cd913754325c93f99d767380d8317930f7", size = 990525, upload-time = "2025-10-19T00:42:56.666Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/bd/65d7a869d307f9b10ad45c2c1cbb40b81a8d0ed1138fa17fd904f5c83298/cytoolz-1.1.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8b3604fef602bcd53415055a4f68468339192fd17be39e687ae24f476d23d56e", size = 2672409, upload-time = "2025-10-19T00:42:58.81Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/fb/74dfd844bfd67e810bd36e8e3903a143035447245828e7fcd7c81351d775/cytoolz-1.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3604b959a01f64c366e7d10ec7634d5f5cfe10301e27a8f090f6eb3b2a628a18", size = 2808477, upload-time = "2025-10-19T00:43:00.577Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/1f/587686c43e31c19241ec317da66438d093523921ea7749bbc65558a30df9/cytoolz-1.1.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6db2127a3c1bc2f59f08010d2ae53a760771a9de2f67423ad8d400e9ba4276e8", size = 2636881, upload-time = "2025-10-19T00:43:02.24Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/6d/90468cd34f77cb38a11af52c4dc6199efcc97a486395a21bef72e9b7602e/cytoolz-1.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:56584745ac647993a016a21bc76399113b7595e312f8d0a1b140c9fcf9b58a27", size = 2937315, upload-time = "2025-10-19T00:43:03.954Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/50/7b92cd78c613b92e3509e6291d3fb7e0d72ebda999a8df806a96c40ca9ab/cytoolz-1.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db2c4c3a7f7bd7e03bb1a236a125c8feb86c75802f4ecda6ecfaf946610b2930", size = 2959988, upload-time = "2025-10-19T00:43:05.758Z" },
+ { url = "https://files.pythonhosted.org/packages/44/d5/34b5a28a8d9bb329f984b4c2259407ca3f501d1abeb01bacea07937d85d1/cytoolz-1.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:48cb8a692111a285d2b9acd16d185428176bfbffa8a7c274308525fccd01dd42", size = 2795116, upload-time = "2025-10-19T00:43:07.411Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/d9/5dd829e33273ec03bdc3c812e6c3281987ae2c5c91645582f6c331544a64/cytoolz-1.1.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d2f344ba5eb17dcf38ee37fdde726f69053f54927db8f8a1bed6ac61e5b1890d", size = 2535390, upload-time = "2025-10-19T00:43:09.104Z" },
+ { url = "https://files.pythonhosted.org/packages/87/1f/7f9c58068a8eec2183110df051bc6b69dd621143f84473eeb6dc1b32905a/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:abf76b1c1abd031f098f293b6d90ee08bdaa45f8b5678430e331d991b82684b1", size = 2704834, upload-time = "2025-10-19T00:43:10.942Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/90/667def5665333575d01a65fe3ec0ca31b897895f6e3bc1a42d6ea3659369/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ddf9a38a5b686091265ff45b53d142e44a538cd6c2e70610d3bc6be094219032", size = 2658441, upload-time = "2025-10-19T00:43:12.655Z" },
+ { url = "https://files.pythonhosted.org/packages/23/79/6615f9a14960bd29ac98b823777b6589357833f65cf1a11b5abc1587c120/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:946786755274f07bb2be0400f28adb31d7d85a7c7001873c0a8e24a503428fb3", size = 2654766, upload-time = "2025-10-19T00:43:14.325Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/99/be59c6e0ae02153ef10ae1ff0f380fb19d973c651b50cf829a731f6c9e79/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:d5b8f78b9fed79cf185ad4ddec099abeef45951bdcb416c5835ba05f0a1242c7", size = 2827649, upload-time = "2025-10-19T00:43:16.132Z" },
+ { url = "https://files.pythonhosted.org/packages/19/b7/854ddcf9f9618844108677c20d48f4611b5c636956adea0f0e85e027608f/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fccde6efefdbc02e676ccb352a2ccc8a8e929f59a1c6d3d60bb78e923a49ca44", size = 2533456, upload-time = "2025-10-19T00:43:17.764Z" },
+ { url = "https://files.pythonhosted.org/packages/45/66/bfe6fbb2bdcf03c8377c8c2f542576e15f3340c905a09d78a6cb3badd39a/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:717b7775313da5f51b0fbf50d865aa9c39cb241bd4cb605df3cf2246d6567397", size = 2826455, upload-time = "2025-10-19T00:43:19.561Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/0c/cce4047bd927e95f59e73319c02c9bc86bd3d76392e0eb9e41a1147a479c/cytoolz-1.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5158744a09d0e0e4a4f82225e3a3c4ebf38f9ae74467aaa905467270e52f2794", size = 2714897, upload-time = "2025-10-19T00:43:21.291Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/9a/061323bb289b565802bad14fb7ab59fcd8713105df142bcf4dd9ff64f8ac/cytoolz-1.1.0-cp314-cp314-win32.whl", hash = "sha256:1ed534bdbbf063b2bb28fca7d0f6723a3e5a72b086e7c7fe6d74ae8c3e4d00e2", size = 901490, upload-time = "2025-10-19T00:43:22.895Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/20/1f3a733d710d2a25d6f10b463bef55ada52fe6392a5d233c8d770191f48a/cytoolz-1.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:472c1c9a085f5ad973ec0ad7f0b9ba0969faea6f96c9e397f6293d386f3a25ec", size = 946730, upload-time = "2025-10-19T00:43:24.838Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/22/2d657db4a5d1c10a152061800f812caba9ef20d7bd2406f51a5fd800c180/cytoolz-1.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:a7ad7ca3386fa86bd301be3fa36e7f0acb024f412f665937955acfc8eb42deff", size = 905722, upload-time = "2025-10-19T00:43:26.439Z" },
+ { url = "https://files.pythonhosted.org/packages/19/97/b4a8c76796a9a8b9bc90c7992840fa1589a1af8e0426562dea4ce9b384a7/cytoolz-1.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:64b63ed4b71b1ba813300ad0f06b8aff19a12cf51116e0e4f1ed837cea4debcf", size = 1372606, upload-time = "2025-10-19T00:43:28.491Z" },
+ { url = "https://files.pythonhosted.org/packages/08/d4/a1bb1a32b454a2d650db8374ff3bf875ba0fc1c36e6446ec02a83b9140a1/cytoolz-1.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a60ba6f2ed9eb0003a737e1ee1e9fa2258e749da6477946008d4324efa25149f", size = 1012189, upload-time = "2025-10-19T00:43:30.177Z" },
+ { url = "https://files.pythonhosted.org/packages/21/4b/2f5cbbd81588918ee7dd70cffb66731608f578a9b72166aafa991071af7d/cytoolz-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1aa58e2434d732241f7f051e6f17657e969a89971025e24578b5cbc6f1346485", size = 1020624, upload-time = "2025-10-19T00:43:31.712Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/99/c4954dd86cd593cd776a038b36795a259b8b5c12cbab6363edf5f6d9c909/cytoolz-1.1.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6965af3fc7214645970e312deb9bd35a213a1eaabcfef4f39115e60bf2f76867", size = 2917016, upload-time = "2025-10-19T00:43:33.531Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/7c/f1f70a17e272b433232bc8a27df97e46b202d6cc07e3b0d63f7f41ba0f2d/cytoolz-1.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ddd2863f321d67527d3b67a93000a378ad6f967056f68c06467fe011278a6d0e", size = 3107634, upload-time = "2025-10-19T00:43:35.57Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/bd/c3226a57474b4aef1f90040510cba30d0decd3515fed48dc229b37c2f898/cytoolz-1.1.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4e6b428e9eb5126053c2ae0efa62512ff4b38ed3951f4d0888ca7005d63e56f5", size = 2806221, upload-time = "2025-10-19T00:43:37.707Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/47/2f7bfe4aaa1e07dc9828bea228ed744faf73b26aee0c1bdf3b5520bf1909/cytoolz-1.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d758e5ef311d2671e0ae8c214c52e44617cf1e58bef8f022b547b9802a5a7f30", size = 3107671, upload-time = "2025-10-19T00:43:39.401Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/12/6ff3b04fbd1369d0fcd5f8b5910ba6e427e33bf113754c4c35ec3f747924/cytoolz-1.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a95416eca473e6c1179b48d86adcf528b59c63ce78f4cb9934f2e413afa9b56b", size = 3176350, upload-time = "2025-10-19T00:43:41.148Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8c/6691d986b728e77b5d2872743ebcd962d37a2d0f7e9ad95a81b284fbf905/cytoolz-1.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36c8ede93525cf11e2cc787b7156e5cecd7340193ef800b816a16f1404a8dc6d", size = 3001173, upload-time = "2025-10-19T00:43:42.923Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/cb/f59d83a5058e1198db5a1f04e4a124c94d60390e4fa89b6d2e38ee8288a0/cytoolz-1.1.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c949755b6d8a649c5fbc888bc30915926f1b09fe42fea9f289e297c2f6ddd3", size = 2701374, upload-time = "2025-10-19T00:43:44.716Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/f0/1ae6d28df503b0bdae094879da2072b8ba13db5919cd3798918761578411/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e1b6d37545816905a76d9ed59fa4e332f929e879f062a39ea0f6f620405cdc27", size = 2953081, upload-time = "2025-10-19T00:43:47.103Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/06/d86fe811c6222dc32d3e08f5d88d2be598a6055b4d0590e7c1428d55c386/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:05332112d4087904842b36954cd1d3fc0e463a2f4a7ef9477bd241427c593c3b", size = 2862228, upload-time = "2025-10-19T00:43:49.353Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/32/978ef6f42623be44a0a03ae9de875ab54aa26c7e38c5c4cd505460b0927d/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:31538ca2fad2d688cbd962ccc3f1da847329e2258a52940f10a2ac0719e526be", size = 2861971, upload-time = "2025-10-19T00:43:51.028Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/f7/74c69497e756b752b359925d1feef68b91df024a4124a823740f675dacd3/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:747562aa70abf219ea16f07d50ac0157db856d447f7f498f592e097cbc77df0b", size = 2975304, upload-time = "2025-10-19T00:43:52.99Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/2b/3ce0e6889a6491f3418ad4d84ae407b8456b02169a5a1f87990dbba7433b/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:3dc15c48b20c0f467e15e341e102896c8422dccf8efc6322def5c1b02f074629", size = 2697371, upload-time = "2025-10-19T00:43:55.312Z" },
+ { url = "https://files.pythonhosted.org/packages/15/87/c616577f0891d97860643c845f7221e95240aa589586de727e28a5eb6e52/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3c03137ee6103ba92d5d6ad6a510e86fded69cd67050bd8a1843f15283be17ac", size = 2992436, upload-time = "2025-10-19T00:43:57.253Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/9f/490c81bffb3428ab1fa114051fbb5ba18aaa2e2fe4da5bf4170ca524e6b3/cytoolz-1.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:be8e298d88f88bd172b59912240558be3b7a04959375646e7fd4996401452941", size = 2917612, upload-time = "2025-10-19T00:43:59.423Z" },
+ { url = "https://files.pythonhosted.org/packages/66/35/0fec2769660ca6472bbf3317ab634675827bb706d193e3240aaf20eab961/cytoolz-1.1.0-cp314-cp314t-win32.whl", hash = "sha256:3d407140f5604a89578285d4aac7b18b8eafa055cf776e781aabb89c48738fad", size = 960842, upload-time = "2025-10-19T00:44:01.143Z" },
+ { url = "https://files.pythonhosted.org/packages/46/b4/b7ce3d3cd20337becfec978ecfa6d0ef64884d0cf32d44edfed8700914b9/cytoolz-1.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:56e5afb69eb6e1b3ffc34716ee5f92ffbdb5cb003b3a5ca4d4b0fe700e217162", size = 1020835, upload-time = "2025-10-19T00:44:03.246Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/1f/0498009aa563a9c5d04f520aadc6e1c0942434d089d0b2f51ea986470f55/cytoolz-1.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:27b19b4a286b3ff52040efa42dbe403730aebe5fdfd2def704eb285e2125c63e", size = 927963, upload-time = "2025-10-19T00:44:04.85Z" },
+ { url = "https://files.pythonhosted.org/packages/84/32/0522207170294cf691112a93c70a8ef942f60fa9ff8e793b63b1f09cedc0/cytoolz-1.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f32e93a55681d782fc6af939f6df36509d65122423cbc930be39b141064adff8", size = 922014, upload-time = "2025-10-19T00:44:44.911Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/49/9be2d24adaa18fa307ff14e3e43f02b2ae4b69c4ce51cee6889eb2114990/cytoolz-1.1.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5d9bc596751cbda8073e65be02ca11706f00029768fbbbc81e11a8c290bb41aa", size = 918134, upload-time = "2025-10-19T00:44:47.122Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/b3/6a76c3b94c6c87c72ea822e7e67405be6b649c2e37778eeac7c0c0c69de8/cytoolz-1.1.0-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9b16660d01c3931951fab49db422c627897c38c1a1f0393a97582004019a4887", size = 981970, upload-time = "2025-10-19T00:44:48.906Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/8a/606e4c7ed14aa6a86aee6ca84a2cb804754dc6c4905b8f94e09e49f1ce60/cytoolz-1.1.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b7de5718e2113d4efccea3f06055758cdbc17388ecc3341ba4d1d812837d7c1a", size = 978877, upload-time = "2025-10-19T00:44:50.819Z" },
+ { url = "https://files.pythonhosted.org/packages/97/ec/ad474dcb1f6c1ebfdda3c2ad2edbb1af122a0e79c9ff2cb901ffb5f59662/cytoolz-1.1.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a12a2a1a6bc44099491c05a12039efa08cc33a3d0f8c7b0566185e085e139283", size = 964279, upload-time = "2025-10-19T00:44:52.476Z" },
+ { url = "https://files.pythonhosted.org/packages/68/8c/d245fd416c69d27d51f14d5ad62acc4ee5971088ee31c40ffe1cc109af68/cytoolz-1.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:047defa7f5f9a32f82373dbc3957289562e8a3fa58ae02ec8e4dca4f43a33a21", size = 916630, upload-time = "2025-10-19T00:44:54.059Z" },
+]
+
+[[package]]
+name = "dnspython"
+version = "2.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
+]
+
+[[package]]
+name = "email-validator"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "dnspython" },
+ { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" },
+]
+
+[[package]]
+name = "eth-abi"
+version = "5.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "eth-typing" },
+ { name = "eth-utils" },
+ { name = "parsimonious" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/00/71/d9e1380bd77fd22f98b534699af564f189b56d539cc2b9dab908d4e4c242/eth_abi-5.2.0.tar.gz", hash = "sha256:178703fa98c07d8eecd5ae569e7e8d159e493ebb6eeb534a8fe973fbc4e40ef0", size = 49797, upload-time = "2025-01-14T16:29:34.629Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7a/b4/2f3982c4cbcbf5eeb6aec62df1533c0e63c653b3021ff338d44944405676/eth_abi-5.2.0-py3-none-any.whl", hash = "sha256:17abe47560ad753f18054f5b3089fcb588f3e3a092136a416b6c1502cb7e8877", size = 28511, upload-time = "2025-01-14T16:29:31.862Z" },
+]
+
+[[package]]
+name = "eth-account"
+version = "0.13.7"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "bitarray" },
+ { name = "ckzg" },
+ { name = "eth-abi" },
+ { name = "eth-keyfile" },
+ { name = "eth-keys" },
+ { name = "eth-rlp" },
+ { name = "eth-utils" },
+ { name = "hexbytes" },
+ { name = "pydantic" },
+ { name = "rlp" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/74/cf/20f76a29be97339c969fd765f1237154286a565a1d61be98e76bb7af946a/eth_account-0.13.7.tar.gz", hash = "sha256:5853ecbcbb22e65411176f121f5f24b8afeeaf13492359d254b16d8b18c77a46", size = 935998, upload-time = "2025-04-21T21:11:21.204Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/46/18/088fb250018cbe665bc2111974301b2d59f294a565aff7564c4df6878da2/eth_account-0.13.7-py3-none-any.whl", hash = "sha256:39727de8c94d004ff61d10da7587509c04d2dc7eac71e04830135300bdfc6d24", size = 587452, upload-time = "2025-04-21T21:11:18.346Z" },
+]
+
+[[package]]
+name = "eth-hash"
+version = "0.7.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ee/38/577b7bc9380ef9dff0f1dffefe0c9a1ded2385e7a06c306fd95afb6f9451/eth_hash-0.7.1.tar.gz", hash = "sha256:d2411a403a0b0a62e8247b4117932d900ffb4c8c64b15f92620547ca5ce46be5", size = 12227, upload-time = "2025-01-13T21:29:21.765Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/db/f8775490669d28aca24871c67dd56b3e72105cb3bcae9a4ec65dd70859b3/eth_hash-0.7.1-py3-none-any.whl", hash = "sha256:0fb1add2adf99ef28883fd6228eb447ef519ea72933535ad1a0b28c6f65f868a", size = 8028, upload-time = "2025-01-13T21:29:19.365Z" },
+]
+
+[package.optional-dependencies]
+pycryptodome = [
+ { name = "pycryptodome" },
+]
+
+[[package]]
+name = "eth-keyfile"
+version = "0.8.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "eth-keys" },
+ { name = "eth-utils" },
+ { name = "pycryptodome" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/35/66/dd823b1537befefbbff602e2ada88f1477c5b40ec3731e3d9bc676c5f716/eth_keyfile-0.8.1.tar.gz", hash = "sha256:9708bc31f386b52cca0969238ff35b1ac72bd7a7186f2a84b86110d3c973bec1", size = 12267, upload-time = "2024-04-23T20:28:53.862Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/fc/48a586175f847dd9e05e5b8994d2fe8336098781ec2e9836a2ad94280281/eth_keyfile-0.8.1-py3-none-any.whl", hash = "sha256:65387378b82fe7e86d7cb9f8d98e6d639142661b2f6f490629da09fddbef6d64", size = 7510, upload-time = "2024-04-23T20:28:51.063Z" },
+]
+
+[[package]]
+name = "eth-keys"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "eth-typing" },
+ { name = "eth-utils" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/58/11/1ed831c50bd74f57829aa06e58bd82a809c37e070ee501c953b9ac1f1552/eth_keys-0.7.0.tar.gz", hash = "sha256:79d24fd876201df67741de3e3fefb3f4dbcbb6ace66e47e6fe662851a4547814", size = 30166, upload-time = "2025-04-07T17:40:21.697Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4d/25/0ae00f2b0095e559d61ad3dc32171bd5a29dfd95ab04b4edd641f7c75f72/eth_keys-0.7.0-py3-none-any.whl", hash = "sha256:b0cdda8ffe8e5ba69c7c5ca33f153828edcace844f67aabd4542d7de38b159cf", size = 20656, upload-time = "2025-04-07T17:40:20.441Z" },
+]
+
+[[package]]
+name = "eth-rlp"
+version = "2.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "eth-utils" },
+ { name = "hexbytes" },
+ { name = "rlp" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7f/ea/ad39d001fa9fed07fad66edb00af701e29b48be0ed44a3bcf58cb3adf130/eth_rlp-2.2.0.tar.gz", hash = "sha256:5e4b2eb1b8213e303d6a232dfe35ab8c29e2d3051b86e8d359def80cd21db83d", size = 7720, upload-time = "2025-02-04T21:51:08.134Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/99/3b/57efe2bc2df0980680d57c01a36516cd3171d2319ceb30e675de19fc2cc5/eth_rlp-2.2.0-py3-none-any.whl", hash = "sha256:5692d595a741fbaef1203db6a2fedffbd2506d31455a6ad378c8449ee5985c47", size = 4446, upload-time = "2025-02-04T21:51:05.823Z" },
+]
+
+[[package]]
+name = "eth-typing"
+version = "5.2.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/60/54/62aa24b9cc708f06316167ee71c362779c8ed21fc8234a5cd94a8f53b623/eth_typing-5.2.1.tar.gz", hash = "sha256:7557300dbf02a93c70fa44af352b5c4a58f94e997a0fd6797fb7d1c29d9538ee", size = 21806, upload-time = "2025-04-14T20:39:28.217Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/30/72/c370bbe4c53da7bf998d3523f5a0f38867654923a82192df88d0705013d3/eth_typing-5.2.1-py3-none-any.whl", hash = "sha256:b0c2812ff978267563b80e9d701f487dd926f1d376d674f3b535cfe28b665d3d", size = 19163, upload-time = "2025-04-14T20:39:26.571Z" },
+]
+
+[[package]]
+name = "eth-utils"
+version = "5.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cytoolz", marker = "implementation_name == 'cpython'" },
+ { name = "eth-hash" },
+ { name = "eth-typing" },
+ { name = "pydantic" },
+ { name = "toolz", marker = "implementation_name == 'pypy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/e6/e1/ee3a8728227c3558853e63ff35bd4c449abdf5022a19601369400deacd39/eth_utils-5.3.1.tar.gz", hash = "sha256:c94e2d2abd024a9a42023b4ddc1c645814ff3d6a737b33d5cfd890ebf159c2d1", size = 123506, upload-time = "2025-08-27T16:37:17.378Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bf/4d/257cdc01ada430b8e84b9f2385c2553f33218f5b47da9adf0a616308d4b7/eth_utils-5.3.1-py3-none-any.whl", hash = "sha256:1f5476d8f29588d25b8ae4987e1ffdfae6d4c09026e476c4aad13b32dda3ead0", size = 102529, upload-time = "2025-08-27T16:37:15.449Z" },
+]
+
+[[package]]
+name = "exceptiongroup"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
+]
+
+[[package]]
+name = "fastapi"
+version = "0.128.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-doc" },
+ { name = "pydantic" },
+ { name = "starlette" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "email-validator" },
+ { name = "fastapi-cli", extra = ["standard"] },
+ { name = "httpx" },
+ { name = "jinja2" },
+ { name = "pydantic-extra-types" },
+ { name = "pydantic-settings" },
+ { name = "python-multipart" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+
+[[package]]
+name = "fastapi-cli"
+version = "0.0.20"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "rich-toolkit" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "typer" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d3/ca/d90fb3bfbcbd6e56c77afd9d114dd6ce8955d8bb90094399d1c70e659e40/fastapi_cli-0.0.20.tar.gz", hash = "sha256:d17c2634f7b96b6b560bc16b0035ed047d523c912011395f49f00a421692bc3a", size = 19786, upload-time = "2025-12-22T17:13:33.794Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/08/89/5c4eef60524d0fd704eb0706885b82cd5623a43396b94e4a5b17d3a3f516/fastapi_cli-0.0.20-py3-none-any.whl", hash = "sha256:e58b6a0038c0b1532b7a0af690656093dee666201b6b19d3c87175b358e9f783", size = 12390, upload-time = "2025-12-22T17:13:31.708Z" },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "fastapi-cloud-cli" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+
+[[package]]
+name = "fastapi-cloud-cli"
+version = "0.8.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "fastar" },
+ { name = "httpx" },
+ { name = "pydantic", extra = ["email"] },
+ { name = "rich-toolkit" },
+ { name = "rignore" },
+ { name = "sentry-sdk" },
+ { name = "typer" },
+ { name = "uvicorn", extra = ["standard"] },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/51/5d/3b33438de35521fab4968b232caa9a4bd568a5078f2b2dfb7bb8a4528603/fastapi_cloud_cli-0.8.0.tar.gz", hash = "sha256:cf07c502528bfd9e6b184776659f05d9212811d76bbec9fbb6bf34bed4c7456f", size = 30257, upload-time = "2025-12-23T12:08:33.904Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dd/8e/abb95ef59e91bb5adaa2d18fbf9ea70fd524010bb03f406a2dd2a4775ef9/fastapi_cloud_cli-0.8.0-py3-none-any.whl", hash = "sha256:e9f40bee671d985fd25d7a5409b56d4f103777bf8a0c6d746ea5fbf97a8186d9", size = 22306, upload-time = "2025-12-23T12:08:32.68Z" },
+]
+
+[[package]]
+name = "fastar"
+version = "0.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/69/e7/f89d54fb04104114dd0552836dc2b47914f416cc0e200b409dd04a33de5e/fastar-0.8.0.tar.gz", hash = "sha256:f4d4d68dbf1c4c2808f0e730fac5843493fc849f70fe3ad3af60dfbaf68b9a12", size = 68524, upload-time = "2025-11-26T02:36:00.72Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c3/e2/51d9ee443aabcd5aa581d45b18b6198ced364b5cd97e5504c5d782ceb82c/fastar-0.8.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c9f930cff014cf79d396d0541bd9f3a3f170c9b5e45d10d634d98f9ed08788c3", size = 708536, upload-time = "2025-11-26T02:34:35.236Z" },
+ { url = "https://files.pythonhosted.org/packages/07/2a/edfc6274768b8a3859a5ca4f8c29cb7f614d7f27d2378e2c88aa91cda54e/fastar-0.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07b70f712d20622346531a4b46bb332569bea621f61314c0b7e80903a16d14cf", size = 632235, upload-time = "2025-11-26T02:34:19.367Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/1e/3cfbaaec464caef196700ee2ffae1c03f94f7c5e2a85d0ec0ea9cdd1da81/fastar-0.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:330639db3bfba4c6d132421a2a4aeb81e7bea8ce9159cdb6e247fbc5fae97686", size = 871386, upload-time = "2025-11-26T02:33:47.613Z" },
+ { url = "https://files.pythonhosted.org/packages/82/50/224a674ad541054179e4e6e0b54bb6e162f04f698a2512b42a8085fc6b6f/fastar-0.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ea7ceb6231e48d7bb0d7dc13e946baa29c7f6873eaf4afb69725d6da349033", size = 764955, upload-time = "2025-11-26T02:32:44.279Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/5e/4608184aa57cb6a54f62c1eb3e5133ba8d461fc7f13193c0255effbec12a/fastar-0.8.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a90695a601a78bbca910fdf2efcdf3103c55d0de5a5c6e93556d707bf886250b", size = 765987, upload-time = "2025-11-26T02:32:59.701Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/53/6afd2b680dddfa10df9a16bbcf6cabfee0d92435d5c7e3f4cfe3b1712662/fastar-0.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d0bf655ff4c9320b0ca8a5b128063d5093c0c8c1645a2b5f7167143fd8531aa", size = 930900, upload-time = "2025-11-26T02:33:16.059Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/1e/b7a304bfcc1d06845cbfa4b464516f6fff9c8c6692f6ef80a3a86b04e199/fastar-0.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8df22cdd8d58e7689aa89b2e4a07e8e5fa4f88d2d9c2621f0e88a49be97ccea", size = 821523, upload-time = "2025-11-26T02:33:30.897Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/da/9ef8605c6d233cd6ca3a95f7f518ac22aa064903afe6afa57733bfb7c31b/fastar-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5e6ad722685128521c8fb44cf25bd38669650ba3a4b466b8903e5aa28e1a0", size = 821268, upload-time = "2025-11-26T02:34:04.003Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/22/ed37c78a6b4420de1677d82e79742787975c34847229c33dc376334c7283/fastar-0.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:31cd541231a2456e32104da891cf9962c3b40234d0465cbf9322a6bc8a1b05d5", size = 986286, upload-time = "2025-11-26T02:34:50.279Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/a6/366b15f432d85d4089e6e4b52a09cc2a2bcf4d7a1f0771e3d3194deccb1e/fastar-0.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:175db2a98d67ced106468e8987975484f8bbbd5ad99201da823b38bafb565ed5", size = 1041921, upload-time = "2025-11-26T02:35:07.292Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/45/45f8e6991e3ce9f8aeefdc8d4c200daada41097a36808643d1703464c3e2/fastar-0.8.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ada877ab1c65197d772ce1b1c2e244d4799680d8b3f136a4308360f3d8661b23", size = 1047302, upload-time = "2025-11-26T02:35:24.995Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/e2/a587796111a3cd4b78cd61ec3fc1252d8517d81f763f4164ed5680f84810/fastar-0.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:01084cb75f13ca6a8e80bd41584322523189f8e81b472053743d6e6c3062b5a6", size = 995141, upload-time = "2025-11-26T02:35:42.449Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c0/7a8ec86695b0b77168e220cf2af1aa30592f5ecdbd0ce6d641d29c4a8bae/fastar-0.8.0-cp310-cp310-win32.whl", hash = "sha256:ca639b9909805e44364ea13cca2682b487e74826e4ad75957115ec693228d6b6", size = 456544, upload-time = "2025-11-26T02:36:23.801Z" },
+ { url = "https://files.pythonhosted.org/packages/be/a9/8da4deb840121c59deabd939ce2dca3d6beec85576f3743d1144441938b5/fastar-0.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:fbc0f2ed0f4add7fb58034c576584d44d7eaaf93dee721dfb26dbed6e222dbac", size = 490701, upload-time = "2025-11-26T02:36:09.625Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/15/1c764530b81b266f6d27d78d49b6bef22a73b3300cd83a280bfd244908c5/fastar-0.8.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cd9c0d3ebf7a0a6f642f771cf41b79f7c98d40a3072a8abe1174fbd9bd615bd3", size = 708427, upload-time = "2025-11-26T02:34:36.502Z" },
+ { url = "https://files.pythonhosted.org/packages/41/fc/75d42c008516543219e4293e4d8ac55da57a5c63147484f10468bd1bc24e/fastar-0.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2875a077340fe4f8099bd3ed8fa90d9595e1ac3cd62ae19ab690d5bf550eeb35", size = 631740, upload-time = "2025-11-26T02:34:20.718Z" },
+ { url = "https://files.pythonhosted.org/packages/50/8d/9632984f7824ed2210157dcebd8e9821ef6d4f2b28510d0516db6625ff9b/fastar-0.8.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a999263d9f87184bf2801833b2ecf105e03c0dd91cac78685673b70da564fd64", size = 871628, upload-time = "2025-11-26T02:33:49.279Z" },
+ { url = "https://files.pythonhosted.org/packages/05/97/3eb6ea71b7544d45cd29cacb764ca23cde8ce0aed1a6a02251caa4c0a818/fastar-0.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c41111da56430f638cbfc498ebdcc7d30f63416e904b27b7695c29bd4889cb8", size = 765005, upload-time = "2025-11-26T02:32:45.833Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/45/3eb0ee945a0b5d5f9df7e7c25c037ce7fa441cd0b4d44f76d286e2f4396a/fastar-0.8.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3719541a12bb09ab1eae91d2c987a9b2b7d7149c52e7109ba6e15b74aabc49b1", size = 765587, upload-time = "2025-11-26T02:33:01.174Z" },
+ { url = "https://files.pythonhosted.org/packages/51/bb/7defd6ec0d9570b1987d8ebde52d07d97f3f26e10b592fb3e12738eba39a/fastar-0.8.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a9b0fff8079b18acdface7ef1b7f522fd9a589f65ca4a1a0dd7c92a0886c2a2", size = 931150, upload-time = "2025-11-26T02:33:17.374Z" },
+ { url = "https://files.pythonhosted.org/packages/28/54/62e51e684dab347c61878afbf09e177029c1a91eb1e39ef244e6b3ef9efa/fastar-0.8.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac073576c1931959191cb20df38bab21dd152f66c940aa3ca8b22e39f753b2f3", size = 821354, upload-time = "2025-11-26T02:33:32.083Z" },
+ { url = "https://files.pythonhosted.org/packages/53/a8/12708ea4d21e3cf9f485b2a67d44ce84d949a6eddcc9aa5b3d324585ab43/fastar-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:003b59a7c3e405b6a7bff8fab17d31e0ccbc7f06730a8f8ca1694eeea75f3c76", size = 821626, upload-time = "2025-11-26T02:34:05.685Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/c4/1b4d3347c7a759853f963410bf6baf42fe014d587c50c39c8e145f4bf1a0/fastar-0.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a7b96748425efd9fc155cd920d65088a1b0d754421962418ea73413d02ff515a", size = 986187, upload-time = "2025-11-26T02:34:52.047Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/59/2dbe0dc2570764475e60030403738faa261a9d3bff16b08629c378ab939a/fastar-0.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:90957a30e64418b02df5b4d525bea50403d98a4b1f29143ce5914ddfa7e54ee4", size = 1041536, upload-time = "2025-11-26T02:35:08.926Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/0f/639b295669c7ca6fbc2b4be2a7832aaeac1a5e06923f15a8a6d6daecbc7d/fastar-0.8.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f6e784a8015623fbb7ccca1af372fd82cb511b408ddd2348dc929fc6e415df73", size = 1047149, upload-time = "2025-11-26T02:35:26.597Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/e7/23e3a19e06d261d1894f98eca9458f98c090c505a0c712dafc0ff1fc2965/fastar-0.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a03eaf287bbc93064688a1220580ce261e7557c8898f687f4d0b281c85b28d3c", size = 994992, upload-time = "2025-11-26T02:35:44.009Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7a/3ea4726bae3ac9358d02107ae48f3e10ee186dbed554af79e00b7b498c44/fastar-0.8.0-cp311-cp311-win32.whl", hash = "sha256:661a47ed90762f419406c47e802f46af63a08254ba96abd1c8191e4ce967b665", size = 456449, upload-time = "2025-11-26T02:36:25.291Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/3c/0142bee993c431ee91cf5535e6e4b079ad491f620c215fcd79b7e5ffeb2b/fastar-0.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b48abd6056fef7bc3d414aafb453c5b07fdf06d2df5a2841d650288a3aa1e9d3", size = 490863, upload-time = "2025-11-26T02:36:11.114Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/18/d119944f6bdbf6e722e204e36db86390ea45684a1bf6be6e3aa42abd471f/fastar-0.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:50c18788b3c6ffb85e176dcb8548bb8e54616a0519dcdbbfba66f6bbc4316933", size = 462230, upload-time = "2025-11-26T02:36:01.917Z" },
+ { url = "https://files.pythonhosted.org/packages/58/f1/5b2ff898abac7f1a418284aad285e3a4f68d189c572ab2db0f6c9079dd16/fastar-0.8.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f10d2adfe40f47ff228f4efaa32d409d732ded98580e03ed37c9535b5fc923d", size = 706369, upload-time = "2025-11-26T02:34:37.783Z" },
+ { url = "https://files.pythonhosted.org/packages/23/60/8046a386dca39154f80c927cbbeeb4b1c1267a3271bffe61552eb9995757/fastar-0.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b930da9d598e3bc69513d131f397e6d6be4643926ef3de5d33d1e826631eb036", size = 629097, upload-time = "2025-11-26T02:34:21.888Z" },
+ { url = "https://files.pythonhosted.org/packages/22/7e/1ae005addc789924a9268da2394d3bb5c6f96836f7e37b7e3d23c2362675/fastar-0.8.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9d210da2de733ca801de83e931012349d209f38b92d9630ccaa94bd445bdc9b8", size = 868938, upload-time = "2025-11-26T02:33:51.119Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/77/290a892b073b84bf82e6b2259708dfe79c54f356e252c2dd40180b16fe07/fastar-0.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa02270721517078a5bd61a38719070ac2537a4aa6b6c48cf369cf2abc59174a", size = 765204, upload-time = "2025-11-26T02:32:47.02Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/00/c3155171b976003af3281f5258189f1935b15d1221bfc7467b478c631216/fastar-0.8.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:83c391e5b789a720e4d0029b9559f5d6dee3226693c5b39c0eab8eaece997e0f", size = 764717, upload-time = "2025-11-26T02:33:02.453Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/43/405b7ad76207b2c11b7b59335b70eac19e4a2653977f5588a1ac8fed54f4/fastar-0.8.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3258d7a78a72793cdd081545da61cabe85b1f37634a1d0b97ffee0ff11d105ef", size = 931502, upload-time = "2025-11-26T02:33:18.619Z" },
+ { url = "https://files.pythonhosted.org/packages/da/8a/a3dde6d37cc3da4453f2845cdf16675b5686b73b164f37e2cc579b057c2c/fastar-0.8.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6eab95dd985cdb6a50666cbeb9e4814676e59cfe52039c880b69d67cfd44767", size = 821454, upload-time = "2025-11-26T02:33:33.427Z" },
+ { url = "https://files.pythonhosted.org/packages/da/c1/904fe2468609c8990dce9fe654df3fbc7324a8d8e80d8240ae2c89757064/fastar-0.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:829b1854166141860887273c116c94e31357213fa8e9fe8baeb18bd6c38aa8d9", size = 821647, upload-time = "2025-11-26T02:34:07Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/73/a0642ab7a400bc07528091785e868ace598fde06fcd139b8f865ec1b6f3c/fastar-0.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1667eae13f9457a3c737f4376d68e8c3e548353538b28f7e4273a30cb3965cd", size = 986342, upload-time = "2025-11-26T02:34:53.371Z" },
+ { url = "https://files.pythonhosted.org/packages/af/af/60c1bfa6edab72366461a95f053d0f5f7ab1825fe65ca2ca367432cd8629/fastar-0.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b864a95229a7db0814cd9ef7987cb713fd43dce1b0d809dd17d9cd6f02fdde3e", size = 1040207, upload-time = "2025-11-26T02:35:10.65Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/a0/0d624290dec622e7fa084b6881f456809f68777d54a314f5dde932714506/fastar-0.8.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c05fbc5618ce17675a42576fa49858d79734627f0a0c74c0875ab45ee8de340c", size = 1045031, upload-time = "2025-11-26T02:35:28.108Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/74/cf663af53c4706ba88e6b4af44a6b0c3bd7d7ca09f079dc40647a8f06585/fastar-0.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7f41c51ee96f338662ee3c3df4840511ba3f9969606840f1b10b7cb633a3c716", size = 994877, upload-time = "2025-11-26T02:35:45.797Z" },
+ { url = "https://files.pythonhosted.org/packages/52/17/444c8be6e77206050e350da7c338102b6cab384be937fa0b1d6d1f9ede73/fastar-0.8.0-cp312-cp312-win32.whl", hash = "sha256:d949a1a2ea7968b734632c009df0571c94636a5e1622c87a6e2bf712a7334f47", size = 455996, upload-time = "2025-11-26T02:36:26.938Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/34/fc3b5e56d71a17b1904800003d9251716e8fd65f662e1b10a26881698a74/fastar-0.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:fc645994d5b927d769121094e8a649b09923b3c13a8b0b98696d8f853f23c532", size = 490429, upload-time = "2025-11-26T02:36:12.707Z" },
+ { url = "https://files.pythonhosted.org/packages/35/a8/5608cc837417107c594e2e7be850b9365bcb05e99645966a5d6a156285fe/fastar-0.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:d81ee82e8dc78a0adb81728383bd39611177d642a8fa2d601d4ad5ad59e5f3bd", size = 461297, upload-time = "2025-11-26T02:36:03.546Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/a5/79ecba3646e22d03eef1a66fb7fc156567213e2e4ab9faab3bbd4489e483/fastar-0.8.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:a3253a06845462ca2196024c7a18f5c0ba4de1532ab1c4bad23a40b332a06a6a", size = 706112, upload-time = "2025-11-26T02:34:39.237Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/03/4f883bce878218a8676c2d7ca09b50c856a5470bb3b7f63baf9521ea6995/fastar-0.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5cbeb3ebfa0980c68ff8b126295cc6b208ccd81b638aebc5a723d810a7a0e5d2", size = 628954, upload-time = "2025-11-26T02:34:23.705Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/f1/892e471f156b03d10ba48ace9384f5a896702a54506137462545f38e40b8/fastar-0.8.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1c0d5956b917daac77d333d48b3f0f3ff927b8039d5b32d8125462782369f761", size = 868685, upload-time = "2025-11-26T02:33:53.077Z" },
+ { url = "https://files.pythonhosted.org/packages/39/ba/e24915045852e30014ec6840446975c03f4234d1c9270394b51d3ad18394/fastar-0.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27b404db2b786b65912927ce7f3790964a4bcbde42cdd13091b82a89cd655e1c", size = 765044, upload-time = "2025-11-26T02:32:48.187Z" },
+ { url = "https://files.pythonhosted.org/packages/14/2c/1aa11ac21a99984864c2fca4994e094319ff3a2046e7a0343c39317bd5b9/fastar-0.8.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0902fc89dcf1e7f07b8563032a4159fe2b835e4c16942c76fd63451d0e5f76a3", size = 764322, upload-time = "2025-11-26T02:33:03.859Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/f0/4b91902af39fe2d3bae7c85c6d789586b9fbcf618d7fdb3d37323915906d/fastar-0.8.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:069347e2f0f7a8b99bbac8cd1bc0e06c7b4a31dc964fc60d84b95eab3d869dc1", size = 931016, upload-time = "2025-11-26T02:33:19.902Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/97/8fc43a5a9c0a2dc195730f6f7a0f367d171282cd8be2511d0e87c6d2dad0/fastar-0.8.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd135306f6bfe9a835918280e0eb440b70ab303e0187d90ab51ca86e143f70d", size = 821308, upload-time = "2025-11-26T02:33:34.664Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/e9/058615b63a7fd27965e8c5966f393ed0c169f7ff5012e1674f21684de3ba/fastar-0.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78d06d6897f43c27154b5f2d0eb930a43a81b7eec73f6f0b0114814d4a10ab38", size = 821171, upload-time = "2025-11-26T02:34:08.498Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/cf/69e16a17961570a755c37ffb5b5aa7610d2e77807625f537989da66f2a9d/fastar-0.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a922f8439231fa0c32b15e8d70ff6d415619b9d40492029dabbc14a0c53b5f18", size = 986227, upload-time = "2025-11-26T02:34:55.06Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/83/2100192372e59b56f4ace37d7d9cabda511afd71b5febad1643d1c334271/fastar-0.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a739abd51eb766384b4caff83050888e80cd75bbcfec61e6d1e64875f94e4a40", size = 1039395, upload-time = "2025-11-26T02:35:12.166Z" },
+ { url = "https://files.pythonhosted.org/packages/75/15/cdd03aca972f55872efbb7cf7540c3fa7b97a75d626303a3ea46932163dc/fastar-0.8.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5a65f419d808b23ac89d5cd1b13a2f340f15bc5d1d9af79f39fdb77bba48ff1b", size = 1044766, upload-time = "2025-11-26T02:35:29.62Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/29/945e69e4e2652329ace545999334ec31f1431fbae3abb0105587e11af2ae/fastar-0.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7bb2ae6c0cce58f0db1c9f20495e7557cca2c1ee9c69bbd90eafd54f139171c5", size = 994740, upload-time = "2025-11-26T02:35:47.887Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/5d/dbfe28f8cd1eb484bba0c62e5259b2cf6fea229d6ef43e05c06b5a78c034/fastar-0.8.0-cp313-cp313-win32.whl", hash = "sha256:b28753e0d18a643272597cb16d39f1053842aa43131ad3e260c03a2417d38401", size = 455990, upload-time = "2025-11-26T02:36:28.502Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/01/e965740bd36e60ef4c5aa2cbe42b6c4eb1dc3551009238a97c2e5e96bd23/fastar-0.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:620e5d737dce8321d49a5ebb7997f1fd0047cde3512082c27dc66d6ac8c1927a", size = 490227, upload-time = "2025-11-26T02:36:14.363Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/10/c99202719b83e5249f26902ae53a05aea67d840eeb242019322f20fc171c/fastar-0.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:c4c4bd08df563120cd33e854fe0a93b81579e8571b11f9b7da9e84c37da2d6b6", size = 461078, upload-time = "2025-11-26T02:36:04.94Z" },
+ { url = "https://files.pythonhosted.org/packages/96/4a/9573b87a0ef07580ed111e7230259aec31bb33ca3667963ebee77022ec61/fastar-0.8.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:50b36ce654ba44b0e13fae607ae17ee6e1597b69f71df1bee64bb8328d881dfc", size = 706041, upload-time = "2025-11-26T02:34:40.638Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/19/f95444a1d4f375333af49300aa75ee93afa3335c0e40fda528e460ed859c/fastar-0.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:63a892762683d7ab00df0227d5ea9677c62ff2cde9b875e666c0be569ed940f3", size = 628617, upload-time = "2025-11-26T02:34:24.893Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/c9/b51481b38b7e3f16ef2b9e233b1a3623386c939d745d6e41bbd389eaae30/fastar-0.8.0-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4ae6a145c1bff592644bde13f2115e0239f4b7babaf506d14e7d208483cf01a5", size = 869299, upload-time = "2025-11-26T02:33:54.274Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/02/3ba1267ee5ba7314e29c431cf82eaa68586f2c40cdfa08be3632b7d07619/fastar-0.8.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ae0ff7c0a1c7e1428404b81faee8aebef466bfd0be25bfe4dabf5d535c68741", size = 764667, upload-time = "2025-11-26T02:32:49.606Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/84/bf33530fd015b5d7c2cc69e0bce4a38d736754a6955487005aab1af6adcd/fastar-0.8.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dbfd87dbd217b45c898b2dbcd0169aae534b2c1c5cbe3119510881f6a5ac8ef5", size = 763993, upload-time = "2025-11-26T02:33:05.782Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e0/9564d24e7cea6321a8d921c6d2a457044a476ef197aa4708e179d3d97f0d/fastar-0.8.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5abd99fcba83ef28c8fe6ae2927edc79053db43a0457a962ed85c9bf150d37", size = 930153, upload-time = "2025-11-26T02:33:21.53Z" },
+ { url = "https://files.pythonhosted.org/packages/35/b1/6f57fcd8d6e192cfebf97e58eb27751640ad93784c857b79039e84387b51/fastar-0.8.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91d4c685620c3a9d6b5ae091dbabab4f98b20049b7ecc7976e19cc9016c0d5d6", size = 821177, upload-time = "2025-11-26T02:33:35.839Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/78/9e004ea9f3aa7466f5ddb6f9518780e1d2f0ed3ca55f093632982598bace/fastar-0.8.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f77c2f2cad76e9dc7b6701297adb1eba87d0485944b416fc2ccf5516c01219a3", size = 820652, upload-time = "2025-11-26T02:34:09.776Z" },
+ { url = "https://files.pythonhosted.org/packages/42/95/b604ed536544005c9f1aee7c4c74b00150db3d8d535cd8232dc20f947063/fastar-0.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e7f07c4a3dada7757a8fc430a5b4a29e6ef696d2212747213f57086ffd970316", size = 985961, upload-time = "2025-11-26T02:34:56.401Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7b/fa9d4d96a5d494bdb8699363bb9de8178c0c21a02e1d89cd6f913d127018/fastar-0.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:90c0c3fe55105c0aed8a83135dbdeb31e683455dbd326a1c48fa44c378b85616", size = 1039316, upload-time = "2025-11-26T02:35:13.807Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/f9/8462789243bc3f33e8401378ec6d54de4e20cfa60c96a0e15e3e9d1389bb/fastar-0.8.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fb9ee51e5bffe0dab3d3126d3a4fac8d8f7235cedcb4b8e74936087ce1c157f3", size = 1045028, upload-time = "2025-11-26T02:35:31.079Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/71/9abb128777e616127194b509e98fcda3db797d76288c1a8c23dd22afc14f/fastar-0.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e380b1e8d30317f52406c43b11e98d11e1d68723bbd031e18049ea3497b59a6d", size = 994677, upload-time = "2025-11-26T02:35:49.391Z" },
+ { url = "https://files.pythonhosted.org/packages/de/c1/b81b3f194853d7ad232a67a1d768f5f51a016f165cfb56cb31b31bbc6177/fastar-0.8.0-cp314-cp314-win32.whl", hash = "sha256:1c4ffc06e9c4a8ca498c07e094670d8d8c0d25b17ca6465b9774da44ea997ab1", size = 456687, upload-time = "2025-11-26T02:36:30.205Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/87/9e0cd4768a98181d56f0cdbab2363404cc15deb93f4aad3b99cd2761bbaa/fastar-0.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:5517a8ad4726267c57a3e0e2a44430b782e00b230bf51c55b5728e758bb3a692", size = 490578, upload-time = "2025-11-26T02:36:16.218Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/1e/580a76cf91847654f2ad6520e956e93218f778540975bc4190d363f709e2/fastar-0.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:58030551046ff4a8616931e52a36c83545ff05996db5beb6e0cd2b7e748aa309", size = 461473, upload-time = "2025-11-26T02:36:06.373Z" },
+ { url = "https://files.pythonhosted.org/packages/58/4c/bdb5c6efe934f68708529c8c9d4055ebef5c4be370621966438f658b29bd/fastar-0.8.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:1e7d29b6bfecb29db126a08baf3c04a5ab667f6cea2b7067d3e623a67729c4a6", size = 705570, upload-time = "2025-11-26T02:34:42.01Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/78/f01ac7e71d5a37621bd13598a26e948a12b85ca8042f7ee1a0a8c9f59cda/fastar-0.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05eb7b96940f9526b485f1d0b02393839f0f61cac4b1f60024984f8b326d2640", size = 627761, upload-time = "2025-11-26T02:34:26.152Z" },
+ { url = "https://files.pythonhosted.org/packages/06/45/6df0ecda86ea9d2e95053c1a655d153dee55fc121b6e13ea6d1e246a50b6/fastar-0.8.0-cp314-cp314t-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:619352d8ac011794e2345c462189dc02ba634750d23cd9d86a9267dd71b1f278", size = 869414, upload-time = "2025-11-26T02:33:55.618Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/72/486421f5a8c0c377cc82e7a50c8a8ea899a6ec2aa72bde8f09fb667a2dc8/fastar-0.8.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74ebfecef3fe6d7a90355fac1402fd30636988332a1d33f3e80019a10782bb24", size = 763863, upload-time = "2025-11-26T02:32:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/64/39f654dbb41a3867fb1f2c8081c014d8f1d32ea10585d84cacbef0b32995/fastar-0.8.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2975aca5a639e26a3ab0d23b4b0628d6dd6d521146c3c11486d782be621a35aa", size = 763065, upload-time = "2025-11-26T02:33:07.274Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/bd/c011a34fb3534c4c3301f7c87c4ffd7e47f6113c904c092ddc8a59a303ea/fastar-0.8.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afc438eaed8ff0dcdd9308268be5cb38c1db7e94c3ccca7c498ca13a4a4535a3", size = 930530, upload-time = "2025-11-26T02:33:23.117Z" },
+ { url = "https://files.pythonhosted.org/packages/55/9d/aa6e887a7033c571b1064429222bbe09adc9a3c1e04f3d1788ba5838ebd5/fastar-0.8.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ced0a5399cc0a84a858ef0a31ca2d0c24d3bbec4bcda506a9192d8119f3590a", size = 820572, upload-time = "2025-11-26T02:33:37.542Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/9c/7a3a2278a1052e1a5d98646de7c095a00cffd2492b3b84ce730e2f1cd93a/fastar-0.8.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec9b23da8c4c039da3fe2e358973c66976a0c8508aa06d6626b4403cb5666c19", size = 820649, upload-time = "2025-11-26T02:34:11.108Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/d38edc1f4438cd047e56137c26d94783ffade42e1b3bde620ccf17b771ef/fastar-0.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:dfba078fcd53478032fd0ceed56960ec6b7ff0511cfc013a8a3a4307e3a7bac4", size = 985653, upload-time = "2025-11-26T02:34:57.884Z" },
+ { url = "https://files.pythonhosted.org/packages/69/d9/2147d0c19757e165cd62d41cec3f7b38fad2ad68ab784978b5f81716c7ea/fastar-0.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:ade56c94c14be356d295fecb47a3fcd473dd43a8803ead2e2b5b9e58feb6dcfa", size = 1038140, upload-time = "2025-11-26T02:35:15.778Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/1d/ec4c717ffb8a308871e9602ec3197d957e238dc0227127ac573ec9bca952/fastar-0.8.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e48d938f9366db5e59441728f70b7f6c1ccfab7eff84f96f9b7e689b07786c52", size = 1045195, upload-time = "2025-11-26T02:35:32.865Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/9f/637334dc8c8f3bb391388b064ae13f0ad9402bc5a6c3e77b8887d0c31921/fastar-0.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:79c441dc1482ff51a54fb3f57ae6f7bb3d2cff88fa2cc5d196c519f8aab64a56", size = 994686, upload-time = "2025-11-26T02:35:51.392Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e2/dfa19a4b260b8ab3581b7484dcb80c09b25324f4daa6b6ae1c7640d1607a/fastar-0.8.0-cp314-cp314t-win32.whl", hash = "sha256:187f61dc739afe45ac8e47ed7fd1adc45d52eac110cf27d579155720507d6fbe", size = 455767, upload-time = "2025-11-26T02:36:34.758Z" },
+ { url = "https://files.pythonhosted.org/packages/51/47/df65c72afc1297797b255f90c4778b5d6f1f0f80282a134d5ab610310ed9/fastar-0.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:40e9d763cf8bf85ce2fa256e010aa795c0fe3d3bd1326d5c3084e6ce7857127e", size = 489971, upload-time = "2025-11-26T02:36:22.081Z" },
+ { url = "https://files.pythonhosted.org/packages/85/11/0aa8455af26f0ae89e42be67f3a874255ee5d7f0f026fc86e8d56f76b428/fastar-0.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:e59673307b6a08210987059a2bdea2614fe26e3335d0e5d1a3d95f49a05b1418", size = 460467, upload-time = "2025-11-26T02:36:07.978Z" },
+ { url = "https://files.pythonhosted.org/packages/25/9f/6eaa810c240236eff2edf736cd50a17c97dbab1693cda4f7bcea09d13418/fastar-0.8.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2127cf2e80ffd49744a160201e0e2f55198af6c028a7b3f750026e0b1f1caa4e", size = 710544, upload-time = "2025-11-26T02:34:46.195Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/a5/58ff9e49a1cd5fbfc8f1238226cbf83b905376a391a6622cdd396b2cfa29/fastar-0.8.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:ff85094f10003801339ac4fa9b20a3410c2d8f284d4cba2dc99de6e98c877812", size = 634020, upload-time = "2025-11-26T02:34:31.085Z" },
+ { url = "https://files.pythonhosted.org/packages/80/94/f839257c6600a83fbdb5a7fcc06319599086137b25ba38ca3d2c0fe14562/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3dbca235f0bd804cca6602fe055d3892bebf95fb802e6c6c7d872fb10f7abc6c", size = 871735, upload-time = "2025-11-26T02:34:00.088Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/79/4124c54260f7ee5cb7034bfe499eff2f8512b052d54be4671e59d4f25a4f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e54bfdee6c81a0005e147319e93d8797f442308032c92fa28d03ef8fda076", size = 766779, upload-time = "2025-11-26T02:32:55.109Z" },
+ { url = "https://files.pythonhosted.org/packages/36/b6/043b263c4126bf6557c942d099503989af9c5c7ee5cca9a04e00f754816f/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a78e5221b94a80800930b7fd0d0e797ae73aadf7044c05ed46cb9bdf870f022", size = 766755, upload-time = "2025-11-26T02:33:11.595Z" },
+ { url = "https://files.pythonhosted.org/packages/57/ff/29a5dc06f2940439ebf98661ecc98d48d3f22fed8d6a2d5dc985d1e8da24/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997092d31ff451de8d0568f6773f3517cb87dcd0bc76184edb65d7154390a6f8", size = 932732, upload-time = "2025-11-26T02:33:27.122Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/e8/2218830f422b37aad52c24b53cb84b5d88bd6fd6ad411bd6689b1a32500d/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:558e8fcf8fe574541df5db14a46cd98bfbed14a811b7014a54f2b714c0cfac42", size = 822571, upload-time = "2025-11-26T02:33:42.986Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/fd/ba6dfeff77cddfe58d85c490b1735c002b81c0d6f826916a8b6c4f8818bc/fastar-0.8.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d2a54f87e2908cc19e1a6ee249620174fbefc54a219aba1eaa6f31657683c3", size = 822440, upload-time = "2025-11-26T02:34:15.439Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/57/54d5740c84b35de0eb12975397ecc16785b5ad8bed2dbac38b8c8a7c1edd/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:ef94901537be277f9ec59db939eb817960496c6351afede5b102699b5098604d", size = 987424, upload-time = "2025-11-26T02:35:02.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/c7/18115927f16deb1ddffdbd4ae992e7e33064bc6defa2b92a147948f8bc0c/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:0afbb92f78bf29d5e9db76fb46cbabc429e49015cddf72ab9e761afbe88ac100", size = 1042675, upload-time = "2025-11-26T02:35:20.252Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/1a/ca884fc7973ec6d765e87af23a4dd25784fb0a36ac2df825f18c3630bbab/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fb59c7925e7710ad178d9e1a3e65edf295d9a042a0cdcb673b4040949eb8ad0a", size = 1047098, upload-time = "2025-11-26T02:35:37.643Z" },
+ { url = "https://files.pythonhosted.org/packages/44/ee/25cd645db749b206bb95e1512e57e75d56ccbbb8ec3536f52a7979deab6b/fastar-0.8.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e6c4d6329da568ec36b1347b0c09c4d27f9dfdeddf9f438ddb16799ecf170098", size = 997397, upload-time = "2025-11-26T02:35:56.215Z" },
+ { url = "https://files.pythonhosted.org/packages/98/6e/6c46aa7f8c8734e7f96ee5141acd3877667ce66f34eea10703aa7571d191/fastar-0.8.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:998e3fa4b555b63eb134e6758437ed739ad1652fdd2a61dfe1dacbfddc35fe66", size = 710662, upload-time = "2025-11-26T02:34:47.593Z" },
+ { url = "https://files.pythonhosted.org/packages/70/27/fd622442f2fbd4ff5459677987481ef1c60e077cb4e63a2ed4d8dce6f869/fastar-0.8.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5f83e60d845091f3a12bc37f412774264d161576eaf810ed8b43567eb934b7e5", size = 634049, upload-time = "2025-11-26T02:34:32.365Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/ee/aa4d08aea25b5419a7277132e738ab1cd775f26aebddce11413b07e2fdff/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:299672e1c74d8b73c61684fac9159cfc063d35f4b165996a88facb0e26862cb5", size = 872055, upload-time = "2025-11-26T02:34:01.377Z" },
+ { url = "https://files.pythonhosted.org/packages/92/9a/2bf2f77aade575e67997e0c759fd55cb1c66b7a5b437b1cd0e97d8b241bc/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3d3a27066b84d015deab5faee78565509bb33b137896443e4144cb1be1a5f90", size = 766787, upload-time = "2025-11-26T02:32:57.161Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/90/23a3f6c252f11b10c70f854bce09abc61f71b5a0e6a4b0eac2bcb9a2c583/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef0bcf4385bbdd3c1acecce2d9ea7dab7cc9b8ee0581bbccb7ab11908a7ce288", size = 766861, upload-time = "2025-11-26T02:33:12.824Z" },
+ { url = "https://files.pythonhosted.org/packages/76/bb/beeb9078380acd4484db5c957d066171695d9340e3526398eb230127b0c2/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f10ef62b6eda6cb6fd9ba8e1fe08a07d7b2bdcc8eaa00eb91566143b92ed7eee", size = 932667, upload-time = "2025-11-26T02:33:28.405Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/6d/b034cc637bd0ee638d5a85d08e941b0b8ffd44cf391fb751ba98233734f7/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c4f6c82a8ee98c17aa48585ee73b51c89c1b010e5c951af83e07c3436180e3fc", size = 822712, upload-time = "2025-11-26T02:33:44.27Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/2b/7d183c63f59227c4689792042d6647f2586a5e7273b55e81745063088d81/fastar-0.8.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6129067fcb86276635b5857010f4e9b9c7d5d15dd571bb03c6c1ed73c40fd92", size = 822659, upload-time = "2025-11-26T02:34:16.815Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/f9/716e0cd9de2427fdf766bc68176f76226cd01fffef3a56c5046fa863f5f0/fastar-0.8.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4cc9e77019e489f1ddac446b6a5b9dfb5c3d9abd142652c22a1d9415dbcc0e47", size = 987412, upload-time = "2025-11-26T02:35:04.259Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/b9/9a8c3fd59958c1c8027bc075af11722cdc62c4968bb277e841d131232289/fastar-0.8.0-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:382bfe82c026086487cb17fee12f4c1e2b4e67ce230f2e04487d3e7ddfd69031", size = 1042911, upload-time = "2025-11-26T02:35:21.857Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/2f/c3f30963b47022134b8a231c12845f4d7cfba520f59bbc1a82468aea77c7/fastar-0.8.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:908d2b9a1ff3d549cc304b32f95706a536da8f0bcb0bc0f9e4c1cce39b80e218", size = 1047464, upload-time = "2025-11-26T02:35:39.376Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8a/218ab6d9a2bab3b07718e6cd8405529600edc1e9c266320e8524c8f63251/fastar-0.8.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1aa7dbde2d2d73eb5b6203d0f74875cb66350f0f1b4325b4839fc8fbbf5d074e", size = 997309, upload-time = "2025-11-26T02:35:57.722Z" },
+]
+
+[[package]]
+name = "frozenlist"
+version = "1.8.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" },
+ { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" },
+ { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" },
+ { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" },
+ { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" },
+ { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" },
+ { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" },
+ { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" },
+ { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" },
+ { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" },
+ { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" },
+ { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" },
+ { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" },
+ { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" },
+ { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" },
+ { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" },
+ { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" },
+ { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" },
+ { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" },
+ { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" },
+ { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" },
+ { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" },
+ { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" },
+ { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" },
+ { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" },
+ { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" },
+ { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" },
+ { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" },
+ { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" },
+ { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" },
+ { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" },
+ { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" },
+ { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" },
+ { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
+]
+
+[[package]]
+name = "h11"
+version = "0.16.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
+]
+
+[[package]]
+name = "hexbytes"
+version = "1.3.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7f/87/adf4635b4b8c050283d74e6db9a81496063229c9263e6acc1903ab79fbec/hexbytes-1.3.1.tar.gz", hash = "sha256:a657eebebdfe27254336f98d8af6e2236f3f83aed164b87466b6cf6c5f5a4765", size = 8633, upload-time = "2025-05-14T16:45:17.5Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8d/e0/3b31492b1c89da3c5a846680517871455b30c54738486fc57ac79a5761bd/hexbytes-1.3.1-py3-none-any.whl", hash = "sha256:da01ff24a1a9a2b1881c4b85f0e9f9b0f51b526b379ffa23832ae7899d29c2c7", size = 5074, upload-time = "2025-05-14T16:45:16.179Z" },
+]
+
+[[package]]
+name = "httpcore"
+version = "1.0.9"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "h11" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
+]
+
+[[package]]
+name = "httptools"
+version = "0.7.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/46/120a669232c7bdedb9d52d4aeae7e6c7dfe151e99dc70802e2fc7a5e1993/httptools-0.7.1.tar.gz", hash = "sha256:abd72556974f8e7c74a259655924a717a2365b236c882c3f6f8a45fe94703ac9", size = 258961, upload-time = "2025-10-10T03:55:08.559Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/e5/c07e0bcf4ec8db8164e9f6738c048b2e66aabf30e7506f440c4cc6953f60/httptools-0.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:11d01b0ff1fe02c4c32d60af61a4d613b74fad069e47e06e9067758c01e9ac78", size = 204531, upload-time = "2025-10-10T03:54:20.887Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/4f/35e3a63f863a659f92ffd92bef131f3e81cf849af26e6435b49bd9f6f751/httptools-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84d86c1e5afdc479a6fdabf570be0d3eb791df0ae727e8dbc0259ed1249998d4", size = 109408, upload-time = "2025-10-10T03:54:22.455Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/71/b0a9193641d9e2471ac541d3b1b869538a5fb6419d52fd2669fa9c79e4b8/httptools-0.7.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c8c751014e13d88d2be5f5f14fc8b89612fcfa92a9cc480f2bc1598357a23a05", size = 440889, upload-time = "2025-10-10T03:54:23.753Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d9/2e34811397b76718750fea44658cb0205b84566e895192115252e008b152/httptools-0.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:654968cb6b6c77e37b832a9be3d3ecabb243bbe7a0b8f65fbc5b6b04c8fcabed", size = 440460, upload-time = "2025-10-10T03:54:25.313Z" },
+ { url = "https://files.pythonhosted.org/packages/01/3f/a04626ebeacc489866bb4d82362c0657b2262bef381d68310134be7f40bb/httptools-0.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b580968316348b474b020edf3988eecd5d6eec4634ee6561e72ae3a2a0e00a8a", size = 425267, upload-time = "2025-10-10T03:54:26.81Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/99/adcd4f66614db627b587627c8ad6f4c55f18881549bab10ecf180562e7b9/httptools-0.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d496e2f5245319da9d764296e86c5bb6fcf0cf7a8806d3d000717a889c8c0b7b", size = 424429, upload-time = "2025-10-10T03:54:28.174Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/72/ec8fc904a8fd30ba022dfa85f3bbc64c3c7cd75b669e24242c0658e22f3c/httptools-0.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cbf8317bfccf0fed3b5680c559d3459cccf1abe9039bfa159e62e391c7270568", size = 86173, upload-time = "2025-10-10T03:54:29.5Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/08/17e07e8d89ab8f343c134616d72eebfe03798835058e2ab579dcc8353c06/httptools-0.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:474d3b7ab469fefcca3697a10d11a32ee2b9573250206ba1e50d5980910da657", size = 206521, upload-time = "2025-10-10T03:54:31.002Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/06/c9c1b41ff52f16aee526fd10fbda99fa4787938aa776858ddc4a1ea825ec/httptools-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3c3b7366bb6c7b96bd72d0dbe7f7d5eead261361f013be5f6d9590465ea1c70", size = 110375, upload-time = "2025-10-10T03:54:31.941Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/cc/10935db22fda0ee34c76f047590ca0a8bd9de531406a3ccb10a90e12ea21/httptools-0.7.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:379b479408b8747f47f3b253326183d7c009a3936518cdb70db58cffd369d9df", size = 456621, upload-time = "2025-10-10T03:54:33.176Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/84/875382b10d271b0c11aa5d414b44f92f8dd53e9b658aec338a79164fa548/httptools-0.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cad6b591a682dcc6cf1397c3900527f9affef1e55a06c4547264796bbd17cf5e", size = 454954, upload-time = "2025-10-10T03:54:34.226Z" },
+ { url = "https://files.pythonhosted.org/packages/30/e1/44f89b280f7e46c0b1b2ccee5737d46b3bb13136383958f20b580a821ca0/httptools-0.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eb844698d11433d2139bbeeb56499102143beb582bd6c194e3ba69c22f25c274", size = 440175, upload-time = "2025-10-10T03:54:35.942Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/7e/b9287763159e700e335028bc1824359dc736fa9b829dacedace91a39b37e/httptools-0.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f65744d7a8bdb4bda5e1fa23e4ba16832860606fcc09d674d56e425e991539ec", size = 440310, upload-time = "2025-10-10T03:54:37.1Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/07/5b614f592868e07f5c94b1f301b5e14a21df4e8076215a3bccb830a687d8/httptools-0.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:135fbe974b3718eada677229312e97f3b31f8a9c8ffa3ae6f565bf808d5b6bcb", size = 86875, upload-time = "2025-10-10T03:54:38.421Z" },
+ { url = "https://files.pythonhosted.org/packages/53/7f/403e5d787dc4942316e515e949b0c8a013d84078a915910e9f391ba9b3ed/httptools-0.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:38e0c83a2ea9746ebbd643bdfb521b9aa4a91703e2cd705c20443405d2fd16a5", size = 206280, upload-time = "2025-10-10T03:54:39.274Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/0d/7f3fd28e2ce311ccc998c388dd1c53b18120fda3b70ebb022b135dc9839b/httptools-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f25bbaf1235e27704f1a7b86cd3304eabc04f569c828101d94a0e605ef7205a5", size = 110004, upload-time = "2025-10-10T03:54:40.403Z" },
+ { url = "https://files.pythonhosted.org/packages/84/a6/b3965e1e146ef5762870bbe76117876ceba51a201e18cc31f5703e454596/httptools-0.7.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2c15f37ef679ab9ecc06bfc4e6e8628c32a8e4b305459de7cf6785acd57e4d03", size = 517655, upload-time = "2025-10-10T03:54:41.347Z" },
+ { url = "https://files.pythonhosted.org/packages/11/7d/71fee6f1844e6fa378f2eddde6c3e41ce3a1fb4b2d81118dd544e3441ec0/httptools-0.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7fe6e96090df46b36ccfaf746f03034e5ab723162bc51b0a4cf58305324036f2", size = 511440, upload-time = "2025-10-10T03:54:42.452Z" },
+ { url = "https://files.pythonhosted.org/packages/22/a5/079d216712a4f3ffa24af4a0381b108aa9c45b7a5cc6eb141f81726b1823/httptools-0.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f72fdbae2dbc6e68b8239defb48e6a5937b12218e6ffc2c7846cc37befa84362", size = 495186, upload-time = "2025-10-10T03:54:43.937Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/9e/025ad7b65278745dee3bd0ebf9314934c4592560878308a6121f7f812084/httptools-0.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e99c7b90a29fd82fea9ef57943d501a16f3404d7b9ee81799d41639bdaae412c", size = 499192, upload-time = "2025-10-10T03:54:45.003Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/de/40a8f202b987d43afc4d54689600ff03ce65680ede2f31df348d7f368b8f/httptools-0.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:3e14f530fefa7499334a79b0cf7e7cd2992870eb893526fb097d51b4f2d0f321", size = 86694, upload-time = "2025-10-10T03:54:45.923Z" },
+ { url = "https://files.pythonhosted.org/packages/09/8f/c77b1fcbfd262d422f12da02feb0d218fa228d52485b77b953832105bb90/httptools-0.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6babce6cfa2a99545c60bfef8bee0cc0545413cb0018f617c8059a30ad985de3", size = 202889, upload-time = "2025-10-10T03:54:47.089Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/1a/22887f53602feaa066354867bc49a68fc295c2293433177ee90870a7d517/httptools-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:601b7628de7504077dd3dcb3791c6b8694bbd967148a6d1f01806509254fb1ca", size = 108180, upload-time = "2025-10-10T03:54:48.052Z" },
+ { url = "https://files.pythonhosted.org/packages/32/6a/6aaa91937f0010d288d3d124ca2946d48d60c3a5ee7ca62afe870e3ea011/httptools-0.7.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:04c6c0e6c5fb0739c5b8a9eb046d298650a0ff38cf42537fc372b28dc7e4472c", size = 478596, upload-time = "2025-10-10T03:54:48.919Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/70/023d7ce117993107be88d2cbca566a7c1323ccbaf0af7eabf2064fe356f6/httptools-0.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69d4f9705c405ae3ee83d6a12283dc9feba8cc6aaec671b412917e644ab4fa66", size = 473268, upload-time = "2025-10-10T03:54:49.993Z" },
+ { url = "https://files.pythonhosted.org/packages/32/4d/9dd616c38da088e3f436e9a616e1d0cc66544b8cdac405cc4e81c8679fc7/httptools-0.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44c8f4347d4b31269c8a9205d8a5ee2df5322b09bbbd30f8f862185bb6b05346", size = 455517, upload-time = "2025-10-10T03:54:51.066Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/3a/a6c595c310b7df958e739aae88724e24f9246a514d909547778d776799be/httptools-0.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:465275d76db4d554918aba40bf1cbebe324670f3dfc979eaffaa5d108e2ed650", size = 458337, upload-time = "2025-10-10T03:54:52.196Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/82/88e8d6d2c51edc1cc391b6e044c6c435b6aebe97b1abc33db1b0b24cd582/httptools-0.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:322d00c2068d125bd570f7bf78b2d367dad02b919d8581d7476d8b75b294e3e6", size = 85743, upload-time = "2025-10-10T03:54:53.448Z" },
+ { url = "https://files.pythonhosted.org/packages/34/50/9d095fcbb6de2d523e027a2f304d4551855c2f46e0b82befd718b8b20056/httptools-0.7.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c08fe65728b8d70b6923ce31e3956f859d5e1e8548e6f22ec520a962c6757270", size = 203619, upload-time = "2025-10-10T03:54:54.321Z" },
+ { url = "https://files.pythonhosted.org/packages/07/f0/89720dc5139ae54b03f861b5e2c55a37dba9a5da7d51e1e824a1f343627f/httptools-0.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7aea2e3c3953521c3c51106ee11487a910d45586e351202474d45472db7d72d3", size = 108714, upload-time = "2025-10-10T03:54:55.163Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/cb/eea88506f191fb552c11787c23f9a405f4c7b0c5799bf73f2249cd4f5228/httptools-0.7.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0e68b8582f4ea9166be62926077a3334064d422cf08ab87d8b74664f8e9058e1", size = 472909, upload-time = "2025-10-10T03:54:56.056Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/4a/a548bdfae6369c0d078bab5769f7b66f17f1bfaa6fa28f81d6be6959066b/httptools-0.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df091cf961a3be783d6aebae963cc9b71e00d57fa6f149025075217bc6a55a7b", size = 470831, upload-time = "2025-10-10T03:54:57.219Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/31/14df99e1c43bd132eec921c2e7e11cda7852f65619bc0fc5bdc2d0cb126c/httptools-0.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f084813239e1eb403ddacd06a30de3d3e09a9b76e7894dcda2b22f8a726e9c60", size = 452631, upload-time = "2025-10-10T03:54:58.219Z" },
+ { url = "https://files.pythonhosted.org/packages/22/d2/b7e131f7be8d854d48cb6d048113c30f9a46dca0c9a8b08fcb3fcd588cdc/httptools-0.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7347714368fb2b335e9063bc2b96f2f87a9ceffcd9758ac295f8bbcd3ffbc0ca", size = 452910, upload-time = "2025-10-10T03:54:59.366Z" },
+ { url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" },
+]
+
+[[package]]
+name = "httpx"
+version = "0.28.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "certifi" },
+ { name = "httpcore" },
+ { name = "idna" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
+]
+
+[[package]]
+name = "idna"
+version = "3.11"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
+]
+
+[[package]]
+name = "iniconfig"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
+]
+
+[[package]]
+name = "jinja2"
+version = "3.1.6"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markupsafe" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
+]
+
+[[package]]
+name = "jsonalias"
+version = "0.1.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ec/45/ee7e17002cb7f3264f755ff6a1a72c55d1830e07808d643167d2a2277c4f/jsonalias-0.1.1.tar.gz", hash = "sha256:64f04d935397d579fc94509e1fcb6212f2d081235d9d6395bd10baedf760a769", size = 1095, upload-time = "2022-10-28T22:57:56.224Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/41/ed/05aebce69f78c104feff2ffcdd5a6f9d668a208aba3a8bf56e3750809fd8/jsonalias-0.1.1-py3-none-any.whl", hash = "sha256:a56d2888e6397812c606156504e861e8ec00e188005af149f003c787db3d3f18", size = 1312, upload-time = "2022-10-28T22:57:54.763Z" },
+]
+
+[[package]]
+name = "librt"
+version = "0.7.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b5/8a/071f6628363d83e803d4783e0cd24fb9c5b798164300fcfaaa47c30659c0/librt-0.7.5.tar.gz", hash = "sha256:de4221a1181fa9c8c4b5f35506ed6f298948f44003d84d2a8b9885d7e01e6cfa", size = 145868, upload-time = "2025-12-25T03:53:16.039Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/88/f2/3248d8419db99ab80bb36266735d1241f766ad5fd993071211f789b618a5/librt-0.7.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81056e01bba1394f1d92904ec61a4078f66df785316275edbaf51d90da8c6e26", size = 54703, upload-time = "2025-12-25T03:51:48.394Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/30/7e179543dbcb1311f84b7e797658ad85cf2d4474c468f5dbafa13f2a98a5/librt-0.7.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7c72c8756eeb3aefb1b9e3dac7c37a4a25db63640cac0ab6fc18e91a0edf05a", size = 56660, upload-time = "2025-12-25T03:51:49.791Z" },
+ { url = "https://files.pythonhosted.org/packages/15/91/3ba03ac1ac1abd66757a134b3bd56d9674928b163d0e686ea065a2bbb92d/librt-0.7.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ddc4a16207f88f9597b397fc1f60781266d13b13de922ff61c206547a29e4bbd", size = 161026, upload-time = "2025-12-25T03:51:51.021Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/6e/b8365f547817d37b44c4be2ffa02630be995ef18be52d72698cecc3640c5/librt-0.7.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63055d3dda433ebb314c9f1819942f16a19203c454508fdb2d167613f7017169", size = 169530, upload-time = "2025-12-25T03:51:52.417Z" },
+ { url = "https://files.pythonhosted.org/packages/63/6a/8442eb0b6933c651a06e1888f863971f3391cc11338fdaa6ab969f7d1eac/librt-0.7.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f85f9b5db87b0f52e53c68ad2a0c5a53e00afa439bd54a1723742a2b1021276", size = 183272, upload-time = "2025-12-25T03:51:53.713Z" },
+ { url = "https://files.pythonhosted.org/packages/90/c4/b1166df6ef8e1f68d309f50bf69e8e750a5ea12fe7e2cf202c771ff359fc/librt-0.7.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c566a4672564c5d54d8ab65cdaae5a87ee14c1564c1a2ddc7a9f5811c750f023", size = 179040, upload-time = "2025-12-25T03:51:55.048Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/30/8f3fd9fd975b16c37832d6c248b976d2a0e33f155063781e064f249b37f1/librt-0.7.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fee15c2a190ef389f14928135c6fb2d25cd3fdb7887bfd9a7b444bbdc8c06b96", size = 173506, upload-time = "2025-12-25T03:51:56.407Z" },
+ { url = "https://files.pythonhosted.org/packages/75/71/c3d4d5658f9849bf8e07ffba99f892d49a0c9a4001323ed610db72aedc82/librt-0.7.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:584cb3e605ec45ba350962cec853e17be0a25a772f21f09f1e422f7044ae2a7d", size = 193573, upload-time = "2025-12-25T03:51:57.949Z" },
+ { url = "https://files.pythonhosted.org/packages/86/7c/c1c8a0116a2eed3d58c8946c589a8f9e1354b9b825cc92eba58bb15f6fb1/librt-0.7.5-cp310-cp310-win32.whl", hash = "sha256:9c08527055fbb03c641c15bbc5b79dd2942fb6a3bd8dabf141dd7e97eeea4904", size = 42603, upload-time = "2025-12-25T03:51:59.215Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/00/b52c77ca294247420020b829b70465c6e6f2b9d59ab21d8051aac20432da/librt-0.7.5-cp310-cp310-win_amd64.whl", hash = "sha256:dd810f2d39c526c42ea205e0addad5dc08ef853c625387806a29d07f9d150d9b", size = 48977, upload-time = "2025-12-25T03:52:00.519Z" },
+ { url = "https://files.pythonhosted.org/packages/11/89/42b3ccb702a7e5f7a4cf2afc8a0a8f8c5e7d4b4d3a7c3de6357673dddddb/librt-0.7.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f952e1a78c480edee8fb43aa2bf2e84dcd46c917d44f8065b883079d3893e8fc", size = 54705, upload-time = "2025-12-25T03:52:01.433Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/90/c16970b509c3c448c365041d326eeef5aeb2abaed81eb3187b26a3cd13f8/librt-0.7.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75965c1f4efb7234ff52a58b729d245a21e87e4b6a26a0ec08052f02b16274e4", size = 56667, upload-time = "2025-12-25T03:52:02.391Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/2f/da4bdf6c190503f4663fbb781dfae5564a2b1c3f39a2da8e1ac7536ac7bd/librt-0.7.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:732e0aa0385b59a1b2545159e781c792cc58ce9c134249233a7c7250a44684c4", size = 161705, upload-time = "2025-12-25T03:52:03.395Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/88/c5da8e1f5f22b23d56e1fbd87266799dcf32828d47bf69fabc6f9673c6eb/librt-0.7.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cdde31759bd8888f3ef0eebda80394a48961328a17c264dce8cc35f4b9cde35d", size = 171029, upload-time = "2025-12-25T03:52:04.798Z" },
+ { url = "https://files.pythonhosted.org/packages/38/8a/8dfc00a6f1febc094ed9a55a448fc0b3a591b5dfd83be6cfd76d0910b1f0/librt-0.7.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3146d52465b3b6397d25d513f428cb421c18df65b7378667bb5f1e3cc45805", size = 184704, upload-time = "2025-12-25T03:52:05.887Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/57/65dec835ff235f431801064a3b41268f2f5ee0d224dc3bbf46d911af5c1a/librt-0.7.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:29c8d2fae11d4379ea207ba7fc69d43237e42cf8a9f90ec6e05993687e6d648b", size = 180720, upload-time = "2025-12-25T03:52:06.925Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/27/92033d169bbcaa0d9a2dd476c179e5171ec22ed574b1b135a3c6104fb7d4/librt-0.7.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb41f04046b4f22b1e7ba5ef513402cd2e3477ec610e5f92d38fe2bba383d419", size = 174538, upload-time = "2025-12-25T03:52:08.075Z" },
+ { url = "https://files.pythonhosted.org/packages/44/5c/0127098743575d5340624d8d4ec508d4d5ff0877dcee6f55f54bf03e5ed0/librt-0.7.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8bb7883c1e94ceb87c2bf81385266f032da09cd040e804cc002f2c9d6b842e2f", size = 195240, upload-time = "2025-12-25T03:52:09.427Z" },
+ { url = "https://files.pythonhosted.org/packages/47/0f/be028c3e906a8ee6d29a42fd362e6d57d4143057f2bc0c454d489a0f898b/librt-0.7.5-cp311-cp311-win32.whl", hash = "sha256:84d4a6b9efd6124f728558a18e79e7cc5c5d4efc09b2b846c910de7e564f5bad", size = 42941, upload-time = "2025-12-25T03:52:10.527Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/3a/2f0ed57f4c3ae3c841780a95dfbea4cd811c6842d9ee66171ce1af606d25/librt-0.7.5-cp311-cp311-win_amd64.whl", hash = "sha256:ab4b0d3bee6f6ff7017e18e576ac7e41a06697d8dea4b8f3ab9e0c8e1300c409", size = 49244, upload-time = "2025-12-25T03:52:11.832Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/7c/d7932aedfa5a87771f9e2799e7185ec3a322f4a1f4aa87c234159b75c8c8/librt-0.7.5-cp311-cp311-win_arm64.whl", hash = "sha256:730be847daad773a3c898943cf67fb9845a3961d06fb79672ceb0a8cd8624cfa", size = 42614, upload-time = "2025-12-25T03:52:12.745Z" },
+ { url = "https://files.pythonhosted.org/packages/33/9d/cb0a296cee177c0fee7999ada1c1af7eee0e2191372058814a4ca6d2baf0/librt-0.7.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba1077c562a046208a2dc6366227b3eeae8f2c2ab4b41eaf4fd2fa28cece4203", size = 55689, upload-time = "2025-12-25T03:52:14.041Z" },
+ { url = "https://files.pythonhosted.org/packages/79/5c/d7de4d4228b74c5b81a3fbada157754bb29f0e1f8c38229c669a7f90422a/librt-0.7.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:654fdc971c76348a73af5240d8e2529265b9a7ba6321e38dd5bae7b0d4ab3abe", size = 57142, upload-time = "2025-12-25T03:52:15.336Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/b2/5da779184aae369b69f4ae84225f63741662a0fe422e91616c533895d7a4/librt-0.7.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6b7b58913d475911f6f33e8082f19dd9b120c4f4a5c911d07e395d67b81c6982", size = 165323, upload-time = "2025-12-25T03:52:16.384Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/40/6d5abc15ab6cc70e04c4d201bb28baffff4cfb46ab950b8e90935b162d58/librt-0.7.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8e0fd344bad57026a8f4ccfaf406486c2fc991838050c2fef156170edc3b775", size = 174218, upload-time = "2025-12-25T03:52:17.518Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/d0/5239a8507e6117a3cb59ce0095bdd258bd2a93d8d4b819a506da06d8d645/librt-0.7.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:46aa91813c267c3f60db75d56419b42c0c0b9748ec2c568a0e3588e543fb4233", size = 189007, upload-time = "2025-12-25T03:52:18.585Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/a4/8eed1166ffddbb01c25363e4c4e655f4bac298debe9e5a2dcfaf942438a1/librt-0.7.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ddc0ab9dbc5f9ceaf2bf7a367bf01f2697660e908f6534800e88f43590b271db", size = 183962, upload-time = "2025-12-25T03:52:19.723Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/83/260e60aab2f5ccba04579c5c46eb3b855e51196fde6e2bcf6742d89140a8/librt-0.7.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7a488908a470451338607650f1c064175094aedebf4a4fa37890682e30ce0b57", size = 177611, upload-time = "2025-12-25T03:52:21.18Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/36/6dcfed0df41e9695665462bab59af15b7ed2b9c668d85c7ebadd022cbb76/librt-0.7.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e47fc52602ffc374e69bf1b76536dc99f7f6dd876bd786c8213eaa3598be030a", size = 199273, upload-time = "2025-12-25T03:52:22.25Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/b7/157149c8cffae6bc4293a52e0267860cee2398cb270798d94f1c8a69b9ae/librt-0.7.5-cp312-cp312-win32.whl", hash = "sha256:cda8b025875946ffff5a9a7590bf9acde3eb02cb6200f06a2d3e691ef3d9955b", size = 43191, upload-time = "2025-12-25T03:52:23.643Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/91/197dfeb8d3bdeb0a5344d0d8b3077f183ba5e76c03f158126f6072730998/librt-0.7.5-cp312-cp312-win_amd64.whl", hash = "sha256:b591c094afd0ffda820e931148c9e48dc31a556dc5b2b9b3cc552fa710d858e4", size = 49462, upload-time = "2025-12-25T03:52:24.637Z" },
+ { url = "https://files.pythonhosted.org/packages/03/ea/052a79454cc52081dfaa9a1c4c10a529f7a6a6805b2fac5805fea5b25975/librt-0.7.5-cp312-cp312-win_arm64.whl", hash = "sha256:532ddc6a8a6ca341b1cd7f4d999043e4c71a212b26fe9fd2e7f1e8bb4e873544", size = 42830, upload-time = "2025-12-25T03:52:25.944Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/9a/8f61e16de0ff76590af893cfb5b1aa5fa8b13e5e54433d0809c7033f59ed/librt-0.7.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b1795c4b2789b458fa290059062c2f5a297ddb28c31e704d27e161386469691a", size = 55750, upload-time = "2025-12-25T03:52:26.975Z" },
+ { url = "https://files.pythonhosted.org/packages/05/7c/a8a883804851a066f301e0bad22b462260b965d5c9e7fe3c5de04e6f91f8/librt-0.7.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2fcbf2e135c11f721193aa5f42ba112bb1046afafbffd407cbc81d8d735c74d0", size = 57170, upload-time = "2025-12-25T03:52:27.948Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/5d/b3b47facf5945be294cf8a835b03589f70ee0e791522f99ec6782ed738b3/librt-0.7.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c039bbf79a9a2498404d1ae7e29a6c175e63678d7a54013a97397c40aee026c5", size = 165834, upload-time = "2025-12-25T03:52:29.09Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/b6/b26910cd0a4e43e5d02aacaaea0db0d2a52e87660dca08293067ee05601a/librt-0.7.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3919c9407faeeee35430ae135e3a78acd4ecaaaa73767529e2c15ca1d73ba325", size = 174820, upload-time = "2025-12-25T03:52:30.463Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/a3/81feddd345d4c869b7a693135a462ae275f964fcbbe793d01ea56a84c2ee/librt-0.7.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26b46620e1e0e45af510d9848ea0915e7040605dd2ae94ebefb6c962cbb6f7ec", size = 189609, upload-time = "2025-12-25T03:52:31.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/a9/31310796ef4157d1d37648bf4a3b84555319f14cee3e9bad7bdd7bfd9a35/librt-0.7.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9bbb8facc5375476d392990dd6a71f97e4cb42e2ac66f32e860f6e47299d5e89", size = 184589, upload-time = "2025-12-25T03:52:32.59Z" },
+ { url = "https://files.pythonhosted.org/packages/32/22/da3900544cb0ac6ab7a2857850158a0a093b86f92b264aa6c4a4f2355ff3/librt-0.7.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e9e9c988b5ffde7be02180f864cbd17c0b0c1231c235748912ab2afa05789c25", size = 178251, upload-time = "2025-12-25T03:52:33.745Z" },
+ { url = "https://files.pythonhosted.org/packages/db/77/78e02609846e78b9b8c8e361753b3dbac9a07e6d5b567fe518de9e074ab0/librt-0.7.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:edf6b465306215b19dbe6c3fb63cf374a8f3e1ad77f3b4c16544b83033bbb67b", size = 199852, upload-time = "2025-12-25T03:52:34.826Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/25/05706f6b346429c951582f1b3561f4d5e1418d0d7ba1a0c181237cd77b3b/librt-0.7.5-cp313-cp313-win32.whl", hash = "sha256:060bde69c3604f694bd8ae21a780fe8be46bb3dbb863642e8dfc75c931ca8eee", size = 43250, upload-time = "2025-12-25T03:52:35.905Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/59/c38677278ac0b9ae1afc611382ef6c9ea87f52ad257bd3d8d65f0eacdc6a/librt-0.7.5-cp313-cp313-win_amd64.whl", hash = "sha256:a82d5a0ee43aeae2116d7292c77cc8038f4841830ade8aa922e098933b468b9e", size = 49421, upload-time = "2025-12-25T03:52:36.895Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/47/1d71113df4a81de5fdfbd3d7244e05d3d67e89f25455c3380ca50b92741e/librt-0.7.5-cp313-cp313-win_arm64.whl", hash = "sha256:3c98a8d0ac9e2a7cb8ff8c53e5d6e8d82bfb2839abf144fdeaaa832f2a12aa45", size = 42827, upload-time = "2025-12-25T03:52:37.856Z" },
+ { url = "https://files.pythonhosted.org/packages/97/ae/8635b4efdc784220f1378be640d8b1a794332f7f6ea81bb4859bf9d18aa7/librt-0.7.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9937574e6d842f359b8585903d04f5b4ab62277a091a93e02058158074dc52f2", size = 55191, upload-time = "2025-12-25T03:52:38.839Z" },
+ { url = "https://files.pythonhosted.org/packages/52/11/ed7ef6955dc2032af37db9b0b31cd5486a138aa792e1bb9e64f0f4950e27/librt-0.7.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5cd3afd71e9bc146203b6c8141921e738364158d4aa7cdb9a874e2505163770f", size = 56894, upload-time = "2025-12-25T03:52:39.805Z" },
+ { url = "https://files.pythonhosted.org/packages/24/f1/02921d4a66a1b5dcd0493b89ce76e2762b98c459fe2ad04b67b2ea6fdd39/librt-0.7.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9cffa3ef0af29687455161cb446eff059bf27607f95163d6a37e27bcb37180f6", size = 163726, upload-time = "2025-12-25T03:52:40.79Z" },
+ { url = "https://files.pythonhosted.org/packages/65/87/27df46d2756fcb7a82fa7f6ca038a0c6064c3e93ba65b0b86fbf6a4f76a2/librt-0.7.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82f3f088482e2229387eadf8215c03f7726d56f69cce8c0c40f0795aebc9b361", size = 172470, upload-time = "2025-12-25T03:52:42.226Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/a9/e65a35e5d423639f4f3d8e17301ff13cc41c2ff97677fe9c361c26dbfbb7/librt-0.7.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7aa33153a5bb0bac783d2c57885889b1162823384e8313d47800a0e10d0070e", size = 186807, upload-time = "2025-12-25T03:52:43.688Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/b0/ac68aa582a996b1241773bd419823290c42a13dc9f494704a12a17ddd7b6/librt-0.7.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:265729b551a2dd329cc47b323a182fb7961af42abf21e913c9dd7d3331b2f3c2", size = 181810, upload-time = "2025-12-25T03:52:45.095Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/c1/03f6717677f20acd2d690813ec2bbe12a2de305f32c61479c53f7b9413bc/librt-0.7.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:168e04663e126416ba712114050f413ac306759a1791d87b7c11d4428ba75760", size = 175599, upload-time = "2025-12-25T03:52:46.177Z" },
+ { url = "https://files.pythonhosted.org/packages/01/d7/f976ff4c07c59b69bb5eec7e5886d43243075bbef834428124b073471c86/librt-0.7.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:553dc58987d1d853adda8aeadf4db8e29749f0b11877afcc429a9ad892818ae2", size = 196506, upload-time = "2025-12-25T03:52:47.327Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/74/004f068b8888e61b454568b5479f88018fceb14e511ac0609cccee7dd227/librt-0.7.5-cp314-cp314-win32.whl", hash = "sha256:263f4fae9eba277513357c871275b18d14de93fd49bf5e43dc60a97b81ad5eb8", size = 39747, upload-time = "2025-12-25T03:52:48.437Z" },
+ { url = "https://files.pythonhosted.org/packages/37/b1/ea3ec8fcf5f0a00df21f08972af77ad799604a306db58587308067d27af8/librt-0.7.5-cp314-cp314-win_amd64.whl", hash = "sha256:85f485b7471571e99fab4f44eeb327dc0e1f814ada575f3fa85e698417d8a54e", size = 45970, upload-time = "2025-12-25T03:52:49.389Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/30/5e3fb7ac4614a50fc67e6954926137d50ebc27f36419c9963a94f931f649/librt-0.7.5-cp314-cp314-win_arm64.whl", hash = "sha256:49c596cd18e90e58b7caa4d7ca7606049c1802125fcff96b8af73fa5c3870e4d", size = 39075, upload-time = "2025-12-25T03:52:50.395Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/7f/0af0a9306a06c2aabee3a790f5aa560c50ec0a486ab818a572dd3db6c851/librt-0.7.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:54d2aef0b0f5056f130981ad45081b278602ff3657fe16c88529f5058038e802", size = 57375, upload-time = "2025-12-25T03:52:51.439Z" },
+ { url = "https://files.pythonhosted.org/packages/57/1f/c85e510baf6572a3d6ef40c742eacedc02973ed2acdb5dba2658751d9af8/librt-0.7.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0b4791202296ad51ac09a3ff58eb49d9da8e3a4009167a6d76ac418a974e5fd4", size = 59234, upload-time = "2025-12-25T03:52:52.687Z" },
+ { url = "https://files.pythonhosted.org/packages/49/b1/bb6535e4250cd18b88d6b18257575a0239fa1609ebba925f55f51ae08e8e/librt-0.7.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e860909fea75baef941ee6436e0453612505883b9d0d87924d4fda27865b9a2", size = 183873, upload-time = "2025-12-25T03:52:53.705Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/49/ad4a138cca46cdaa7f0e15fa912ce3ccb4cc0d4090bfeb8ccc35766fa6d5/librt-0.7.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f02c4337bf271c4f06637f5ff254fad2238c0b8e32a3a480ebb2fc5e26f754a5", size = 194609, upload-time = "2025-12-25T03:52:54.884Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/2d/3b3cb933092d94bb2c1d3c9b503d8775f08d806588c19a91ee4d1495c2a8/librt-0.7.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7f51ffe59f4556243d3cc82d827bde74765f594fa3ceb80ec4de0c13ccd3416", size = 206777, upload-time = "2025-12-25T03:52:55.969Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/52/6e7611d3d1347812233dabc44abca4c8065ee97b83c9790d7ecc3f782bc8/librt-0.7.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0b7f080ba30601dfa3e3deed3160352273e1b9bc92e652f51103c3e9298f7899", size = 203208, upload-time = "2025-12-25T03:52:57.036Z" },
+ { url = "https://files.pythonhosted.org/packages/27/aa/466ae4654bd2d45903fbf180815d41e3ae8903e5a1861f319f73c960a843/librt-0.7.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:fb565b4219abc8ea2402e61c7ba648a62903831059ed3564fa1245cc245d58d7", size = 196698, upload-time = "2025-12-25T03:52:58.481Z" },
+ { url = "https://files.pythonhosted.org/packages/97/8f/424f7e4525bb26fe0d3e984d1c0810ced95e53be4fd867ad5916776e18a3/librt-0.7.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a3cfb15961e7333ea6ef033dc574af75153b5c230d5ad25fbcd55198f21e0cf", size = 217194, upload-time = "2025-12-25T03:52:59.575Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/33/13a4cb798a171b173f3c94db23adaf13a417130e1493933dc0df0d7fb439/librt-0.7.5-cp314-cp314t-win32.whl", hash = "sha256:118716de5ad6726332db1801bc90fa6d94194cd2e07c1a7822cebf12c496714d", size = 40282, upload-time = "2025-12-25T03:53:01.091Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/f1/62b136301796399d65dad73b580f4509bcbd347dff885a450bff08e80cb6/librt-0.7.5-cp314-cp314t-win_amd64.whl", hash = "sha256:3dd58f7ce20360c6ce0c04f7bd9081c7f9c19fc6129a3c705d0c5a35439f201d", size = 46764, upload-time = "2025-12-25T03:53:02.381Z" },
+ { url = "https://files.pythonhosted.org/packages/49/cb/940431d9410fda74f941f5cd7f0e5a22c63be7b0c10fa98b2b7022b48cb1/librt-0.7.5-cp314-cp314t-win_arm64.whl", hash = "sha256:08153ea537609d11f774d2bfe84af39d50d5c9ca3a4d061d946e0c9d8bce04a1", size = 39728, upload-time = "2025-12-25T03:53:03.306Z" },
+]
+
+[[package]]
+name = "markdown-it-py"
+version = "4.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "mdurl" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
+]
+
+[[package]]
+name = "markupsafe"
+version = "3.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e8/4b/3541d44f3937ba468b75da9eebcae497dcf67adb65caa16760b0a6807ebb/markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559", size = 11631, upload-time = "2025-09-27T18:36:05.558Z" },
+ { url = "https://files.pythonhosted.org/packages/98/1b/fbd8eed11021cabd9226c37342fa6ca4e8a98d8188a8d9b66740494960e4/markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419", size = 12057, upload-time = "2025-09-27T18:36:07.165Z" },
+ { url = "https://files.pythonhosted.org/packages/40/01/e560d658dc0bb8ab762670ece35281dec7b6c1b33f5fbc09ebb57a185519/markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695", size = 22050, upload-time = "2025-09-27T18:36:08.005Z" },
+ { url = "https://files.pythonhosted.org/packages/af/cd/ce6e848bbf2c32314c9b237839119c5a564a59725b53157c856e90937b7a/markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591", size = 20681, upload-time = "2025-09-27T18:36:08.881Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/2a/b5c12c809f1c3045c4d580b035a743d12fcde53cf685dbc44660826308da/markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c", size = 20705, upload-time = "2025-09-27T18:36:10.131Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/e3/9427a68c82728d0a88c50f890d0fc072a1484de2f3ac1ad0bfc1a7214fd5/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f", size = 21524, upload-time = "2025-09-27T18:36:11.324Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/36/23578f29e9e582a4d0278e009b38081dbe363c5e7165113fad546918a232/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6", size = 20282, upload-time = "2025-09-27T18:36:12.573Z" },
+ { url = "https://files.pythonhosted.org/packages/56/21/dca11354e756ebd03e036bd8ad58d6d7168c80ce1fe5e75218e4945cbab7/markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1", size = 20745, upload-time = "2025-09-27T18:36:13.504Z" },
+ { url = "https://files.pythonhosted.org/packages/87/99/faba9369a7ad6e4d10b6a5fbf71fa2a188fe4a593b15f0963b73859a1bbd/markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa", size = 14571, upload-time = "2025-09-27T18:36:14.779Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/25/55dc3ab959917602c96985cb1253efaa4ff42f71194bddeb61eb7278b8be/markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8", size = 15056, upload-time = "2025-09-27T18:36:16.125Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/9e/0a02226640c255d1da0b8d12e24ac2aa6734da68bff14c05dd53b94a0fc3/markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1", size = 13932, upload-time = "2025-09-27T18:36:17.311Z" },
+ { url = "https://files.pythonhosted.org/packages/08/db/fefacb2136439fc8dd20e797950e749aa1f4997ed584c62cfb8ef7c2be0e/markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad", size = 11631, upload-time = "2025-09-27T18:36:18.185Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/2e/5898933336b61975ce9dc04decbc0a7f2fee78c30353c5efba7f2d6ff27a/markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a", size = 12058, upload-time = "2025-09-27T18:36:19.444Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/09/adf2df3699d87d1d8184038df46a9c80d78c0148492323f4693df54e17bb/markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50", size = 24287, upload-time = "2025-09-27T18:36:20.768Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ac/0273f6fcb5f42e314c6d8cd99effae6a5354604d461b8d392b5ec9530a54/markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf", size = 22940, upload-time = "2025-09-27T18:36:22.249Z" },
+ { url = "https://files.pythonhosted.org/packages/19/ae/31c1be199ef767124c042c6c3e904da327a2f7f0cd63a0337e1eca2967a8/markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f", size = 21887, upload-time = "2025-09-27T18:36:23.535Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/76/7edcab99d5349a4532a459e1fe64f0b0467a3365056ae550d3bcf3f79e1e/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a", size = 23692, upload-time = "2025-09-27T18:36:24.823Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/28/6e74cdd26d7514849143d69f0bf2399f929c37dc2b31e6829fd2045b2765/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115", size = 21471, upload-time = "2025-09-27T18:36:25.95Z" },
+ { url = "https://files.pythonhosted.org/packages/62/7e/a145f36a5c2945673e590850a6f8014318d5577ed7e5920a4b3448e0865d/markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a", size = 22923, upload-time = "2025-09-27T18:36:27.109Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/62/d9c46a7f5c9adbeeeda52f5b8d802e1094e9717705a645efc71b0913a0a8/markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19", size = 14572, upload-time = "2025-09-27T18:36:28.045Z" },
+ { url = "https://files.pythonhosted.org/packages/83/8a/4414c03d3f891739326e1783338e48fb49781cc915b2e0ee052aa490d586/markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01", size = 15077, upload-time = "2025-09-27T18:36:29.025Z" },
+ { url = "https://files.pythonhosted.org/packages/35/73/893072b42e6862f319b5207adc9ae06070f095b358655f077f69a35601f0/markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c", size = 13876, upload-time = "2025-09-27T18:36:29.954Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" },
+ { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" },
+ { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
+ { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" },
+ { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" },
+ { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" },
+ { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" },
+ { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" },
+ { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" },
+ { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" },
+ { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" },
+ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" },
+ { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" },
+ { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" },
+ { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" },
+ { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" },
+ { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" },
+ { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" },
+ { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" },
+ { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" },
+ { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" },
+ { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" },
+ { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
+]
+
+[[package]]
+name = "mdurl"
+version = "0.1.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
+]
+
+[[package]]
+name = "multidict"
+version = "6.7.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", size = 101834, upload-time = "2025-10-06T14:52:30.657Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a9/63/7bdd4adc330abcca54c85728db2327130e49e52e8c3ce685cec44e0f2e9f/multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349", size = 77153, upload-time = "2025-10-06T14:48:26.409Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/bb/b6c35ff175ed1a3142222b78455ee31be71a8396ed3ab5280fbe3ebe4e85/multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e", size = 44993, upload-time = "2025-10-06T14:48:28.4Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/1f/064c77877c5fa6df6d346e68075c0f6998547afe952d6471b4c5f6a7345d/multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3", size = 44607, upload-time = "2025-10-06T14:48:29.581Z" },
+ { url = "https://files.pythonhosted.org/packages/04/7a/bf6aa92065dd47f287690000b3d7d332edfccb2277634cadf6a810463c6a/multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046", size = 241847, upload-time = "2025-10-06T14:48:32.107Z" },
+ { url = "https://files.pythonhosted.org/packages/94/39/297a8de920f76eda343e4ce05f3b489f0ab3f9504f2576dfb37b7c08ca08/multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32", size = 242616, upload-time = "2025-10-06T14:48:34.054Z" },
+ { url = "https://files.pythonhosted.org/packages/39/3a/d0eee2898cfd9d654aea6cb8c4addc2f9756e9a7e09391cfe55541f917f7/multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73", size = 222333, upload-time = "2025-10-06T14:48:35.9Z" },
+ { url = "https://files.pythonhosted.org/packages/05/48/3b328851193c7a4240815b71eea165b49248867bbb6153a0aee227a0bb47/multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc", size = 253239, upload-time = "2025-10-06T14:48:37.302Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/ca/0706a98c8d126a89245413225ca4a3fefc8435014de309cf8b30acb68841/multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62", size = 251618, upload-time = "2025-10-06T14:48:38.963Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/4f/9c7992f245554d8b173f6f0a048ad24b3e645d883f096857ec2c0822b8bd/multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84", size = 241655, upload-time = "2025-10-06T14:48:40.312Z" },
+ { url = "https://files.pythonhosted.org/packages/31/79/26a85991ae67efd1c0b1fc2e0c275b8a6aceeb155a68861f63f87a798f16/multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0", size = 239245, upload-time = "2025-10-06T14:48:41.848Z" },
+ { url = "https://files.pythonhosted.org/packages/14/1e/75fa96394478930b79d0302eaf9a6c69f34005a1a5251ac8b9c336486ec9/multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e", size = 233523, upload-time = "2025-10-06T14:48:43.749Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/5e/085544cb9f9c4ad2b5d97467c15f856df8d9bac410cffd5c43991a5d878b/multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4", size = 243129, upload-time = "2025-10-06T14:48:45.225Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/c3/e9d9e2f20c9474e7a8fcef28f863c5cbd29bb5adce6b70cebe8bdad0039d/multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648", size = 248999, upload-time = "2025-10-06T14:48:46.703Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/3f/df171b6efa3239ae33b97b887e42671cd1d94d460614bfb2c30ffdab3b95/multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111", size = 243711, upload-time = "2025-10-06T14:48:48.146Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/2f/9b5564888c4e14b9af64c54acf149263721a283aaf4aa0ae89b091d5d8c1/multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36", size = 237504, upload-time = "2025-10-06T14:48:49.447Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/3a/0bd6ca0f7d96d790542d591c8c3354c1e1b6bfd2024d4d92dc3d87485ec7/multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85", size = 41422, upload-time = "2025-10-06T14:48:50.789Z" },
+ { url = "https://files.pythonhosted.org/packages/00/35/f6a637ea2c75f0d3b7c7d41b1189189acff0d9deeb8b8f35536bb30f5e33/multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7", size = 46050, upload-time = "2025-10-06T14:48:51.938Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/b8/f7bf8329b39893d02d9d95cf610c75885d12fc0f402b1c894e1c8e01c916/multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0", size = 43153, upload-time = "2025-10-06T14:48:53.146Z" },
+ { url = "https://files.pythonhosted.org/packages/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", size = 76604, upload-time = "2025-10-06T14:48:54.277Z" },
+ { url = "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", size = 44715, upload-time = "2025-10-06T14:48:55.445Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", size = 44332, upload-time = "2025-10-06T14:48:56.706Z" },
+ { url = "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", size = 245212, upload-time = "2025-10-06T14:48:58.042Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", size = 246671, upload-time = "2025-10-06T14:49:00.004Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", size = 225491, upload-time = "2025-10-06T14:49:01.393Z" },
+ { url = "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", size = 257322, upload-time = "2025-10-06T14:49:02.745Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", size = 254694, upload-time = "2025-10-06T14:49:04.15Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", size = 246715, upload-time = "2025-10-06T14:49:05.967Z" },
+ { url = "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", size = 243189, upload-time = "2025-10-06T14:49:07.37Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", size = 237845, upload-time = "2025-10-06T14:49:08.759Z" },
+ { url = "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", size = 246374, upload-time = "2025-10-06T14:49:10.574Z" },
+ { url = "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", size = 253345, upload-time = "2025-10-06T14:49:12.331Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", size = 246940, upload-time = "2025-10-06T14:49:13.821Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", size = 242229, upload-time = "2025-10-06T14:49:15.603Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/a2/59b405d59fd39ec86d1142630e9049243015a5f5291ba49cadf3c090c541/multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff", size = 41308, upload-time = "2025-10-06T14:49:16.871Z" },
+ { url = "https://files.pythonhosted.org/packages/32/0f/13228f26f8b882c34da36efa776c3b7348455ec383bab4a66390e42963ae/multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81", size = 46037, upload-time = "2025-10-06T14:49:18.457Z" },
+ { url = "https://files.pythonhosted.org/packages/84/1f/68588e31b000535a3207fd3c909ebeec4fb36b52c442107499c18a896a2a/multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912", size = 43023, upload-time = "2025-10-06T14:49:19.648Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/9e/9f61ac18d9c8b475889f32ccfa91c9f59363480613fc807b6e3023d6f60b/multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184", size = 76877, upload-time = "2025-10-06T14:49:20.884Z" },
+ { url = "https://files.pythonhosted.org/packages/38/6f/614f09a04e6184f8824268fce4bc925e9849edfa654ddd59f0b64508c595/multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45", size = 45467, upload-time = "2025-10-06T14:49:22.054Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/93/c4f67a436dd026f2e780c433277fff72be79152894d9fc36f44569cab1a6/multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa", size = 43834, upload-time = "2025-10-06T14:49:23.566Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/f5/013798161ca665e4a422afbc5e2d9e4070142a9ff8905e482139cd09e4d0/multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7", size = 250545, upload-time = "2025-10-06T14:49:24.882Z" },
+ { url = "https://files.pythonhosted.org/packages/71/2f/91dbac13e0ba94669ea5119ba267c9a832f0cb65419aca75549fcf09a3dc/multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e", size = 258305, upload-time = "2025-10-06T14:49:26.778Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/b0/754038b26f6e04488b48ac621f779c341338d78503fb45403755af2df477/multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546", size = 242363, upload-time = "2025-10-06T14:49:28.562Z" },
+ { url = "https://files.pythonhosted.org/packages/87/15/9da40b9336a7c9fa606c4cf2ed80a649dffeb42b905d4f63a1d7eb17d746/multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4", size = 268375, upload-time = "2025-10-06T14:49:29.96Z" },
+ { url = "https://files.pythonhosted.org/packages/82/72/c53fcade0cc94dfaad583105fd92b3a783af2091eddcb41a6d5a52474000/multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1", size = 269346, upload-time = "2025-10-06T14:49:31.404Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/e2/9baffdae21a76f77ef8447f1a05a96ec4bc0a24dae08767abc0a2fe680b8/multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d", size = 256107, upload-time = "2025-10-06T14:49:32.974Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/06/3f06f611087dc60d65ef775f1fb5aca7c6d61c6db4990e7cda0cef9b1651/multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304", size = 253592, upload-time = "2025-10-06T14:49:34.52Z" },
+ { url = "https://files.pythonhosted.org/packages/20/24/54e804ec7945b6023b340c412ce9c3f81e91b3bf5fa5ce65558740141bee/multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12", size = 251024, upload-time = "2025-10-06T14:49:35.956Z" },
+ { url = "https://files.pythonhosted.org/packages/14/48/011cba467ea0b17ceb938315d219391d3e421dfd35928e5dbdc3f4ae76ef/multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62", size = 251484, upload-time = "2025-10-06T14:49:37.631Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/2f/919258b43bb35b99fa127435cfb2d91798eb3a943396631ef43e3720dcf4/multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0", size = 263579, upload-time = "2025-10-06T14:49:39.502Z" },
+ { url = "https://files.pythonhosted.org/packages/31/22/a0e884d86b5242b5a74cf08e876bdf299e413016b66e55511f7a804a366e/multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a", size = 259654, upload-time = "2025-10-06T14:49:41.32Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/e5/17e10e1b5c5f5a40f2fcbb45953c9b215f8a4098003915e46a93f5fcaa8f/multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8", size = 251511, upload-time = "2025-10-06T14:49:46.021Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/9a/201bb1e17e7af53139597069c375e7b0dcbd47594604f65c2d5359508566/multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4", size = 41895, upload-time = "2025-10-06T14:49:48.718Z" },
+ { url = "https://files.pythonhosted.org/packages/46/e2/348cd32faad84eaf1d20cce80e2bb0ef8d312c55bca1f7fa9865e7770aaf/multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b", size = 46073, upload-time = "2025-10-06T14:49:50.28Z" },
+ { url = "https://files.pythonhosted.org/packages/25/ec/aad2613c1910dce907480e0c3aa306905830f25df2e54ccc9dea450cb5aa/multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec", size = 43226, upload-time = "2025-10-06T14:49:52.304Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/86/33272a544eeb36d66e4d9a920602d1a2f57d4ebea4ef3cdfe5a912574c95/multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6", size = 76135, upload-time = "2025-10-06T14:49:54.26Z" },
+ { url = "https://files.pythonhosted.org/packages/91/1c/eb97db117a1ebe46d457a3d235a7b9d2e6dcab174f42d1b67663dd9e5371/multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159", size = 45117, upload-time = "2025-10-06T14:49:55.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/d8/6c3442322e41fb1dd4de8bd67bfd11cd72352ac131f6368315617de752f1/multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca", size = 43472, upload-time = "2025-10-06T14:49:57.048Z" },
+ { url = "https://files.pythonhosted.org/packages/75/3f/e2639e80325af0b6c6febdf8e57cc07043ff15f57fa1ef808f4ccb5ac4cd/multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8", size = 249342, upload-time = "2025-10-06T14:49:58.368Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/cc/84e0585f805cbeaa9cbdaa95f9a3d6aed745b9d25700623ac89a6ecff400/multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60", size = 257082, upload-time = "2025-10-06T14:49:59.89Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/9c/ac851c107c92289acbbf5cfb485694084690c1b17e555f44952c26ddc5bd/multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4", size = 240704, upload-time = "2025-10-06T14:50:01.485Z" },
+ { url = "https://files.pythonhosted.org/packages/50/cc/5f93e99427248c09da95b62d64b25748a5f5c98c7c2ab09825a1d6af0e15/multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f", size = 266355, upload-time = "2025-10-06T14:50:02.955Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/0c/2ec1d883ceb79c6f7f6d7ad90c919c898f5d1c6ea96d322751420211e072/multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf", size = 267259, upload-time = "2025-10-06T14:50:04.446Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/2d/f0b184fa88d6630aa267680bdb8623fb69cb0d024b8c6f0d23f9a0f406d3/multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32", size = 254903, upload-time = "2025-10-06T14:50:05.98Z" },
+ { url = "https://files.pythonhosted.org/packages/06/c9/11ea263ad0df7dfabcad404feb3c0dd40b131bc7f232d5537f2fb1356951/multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036", size = 252365, upload-time = "2025-10-06T14:50:07.511Z" },
+ { url = "https://files.pythonhosted.org/packages/41/88/d714b86ee2c17d6e09850c70c9d310abac3d808ab49dfa16b43aba9d53fd/multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec", size = 250062, upload-time = "2025-10-06T14:50:09.074Z" },
+ { url = "https://files.pythonhosted.org/packages/15/fe/ad407bb9e818c2b31383f6131ca19ea7e35ce93cf1310fce69f12e89de75/multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e", size = 249683, upload-time = "2025-10-06T14:50:10.714Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/a4/a89abdb0229e533fb925e7c6e5c40201c2873efebc9abaf14046a4536ee6/multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64", size = 261254, upload-time = "2025-10-06T14:50:12.28Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/aa/0e2b27bd88b40a4fb8dc53dd74eecac70edaa4c1dd0707eb2164da3675b3/multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd", size = 257967, upload-time = "2025-10-06T14:50:14.16Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/8e/0c67b7120d5d5f6d874ed85a085f9dc770a7f9d8813e80f44a9fec820bb7/multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288", size = 250085, upload-time = "2025-10-06T14:50:15.639Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/55/b73e1d624ea4b8fd4dd07a3bb70f6e4c7c6c5d9d640a41c6ffe5cdbd2a55/multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17", size = 41713, upload-time = "2025-10-06T14:50:17.066Z" },
+ { url = "https://files.pythonhosted.org/packages/32/31/75c59e7d3b4205075b4c183fa4ca398a2daf2303ddf616b04ae6ef55cffe/multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390", size = 45915, upload-time = "2025-10-06T14:50:18.264Z" },
+ { url = "https://files.pythonhosted.org/packages/31/2a/8987831e811f1184c22bc2e45844934385363ee61c0a2dcfa8f71b87e608/multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e", size = 43077, upload-time = "2025-10-06T14:50:19.853Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/68/7b3a5170a382a340147337b300b9eb25a9ddb573bcdfff19c0fa3f31ffba/multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00", size = 83114, upload-time = "2025-10-06T14:50:21.223Z" },
+ { url = "https://files.pythonhosted.org/packages/55/5c/3fa2d07c84df4e302060f555bbf539310980362236ad49f50eeb0a1c1eb9/multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb", size = 48442, upload-time = "2025-10-06T14:50:22.871Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/56/67212d33239797f9bd91962bb899d72bb0f4c35a8652dcdb8ed049bef878/multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b", size = 46885, upload-time = "2025-10-06T14:50:24.258Z" },
+ { url = "https://files.pythonhosted.org/packages/46/d1/908f896224290350721597a61a69cd19b89ad8ee0ae1f38b3f5cd12ea2ac/multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c", size = 242588, upload-time = "2025-10-06T14:50:25.716Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/67/8604288bbd68680eee0ab568fdcb56171d8b23a01bcd5cb0c8fedf6e5d99/multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1", size = 249966, upload-time = "2025-10-06T14:50:28.192Z" },
+ { url = "https://files.pythonhosted.org/packages/20/33/9228d76339f1ba51e3efef7da3ebd91964d3006217aae13211653193c3ff/multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b", size = 228618, upload-time = "2025-10-06T14:50:29.82Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/2d/25d9b566d10cab1c42b3b9e5b11ef79c9111eaf4463b8c257a3bd89e0ead/multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5", size = 257539, upload-time = "2025-10-06T14:50:31.731Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/b1/8d1a965e6637fc33de3c0d8f414485c2b7e4af00f42cab3d84e7b955c222/multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad", size = 256345, upload-time = "2025-10-06T14:50:33.26Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/0c/06b5a8adbdeedada6f4fb8d8f193d44a347223b11939b42953eeb6530b6b/multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c", size = 247934, upload-time = "2025-10-06T14:50:34.808Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/31/b2491b5fe167ca044c6eb4b8f2c9f3b8a00b24c432c365358eadac5d7625/multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5", size = 245243, upload-time = "2025-10-06T14:50:36.436Z" },
+ { url = "https://files.pythonhosted.org/packages/61/1a/982913957cb90406c8c94f53001abd9eafc271cb3e70ff6371590bec478e/multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10", size = 235878, upload-time = "2025-10-06T14:50:37.953Z" },
+ { url = "https://files.pythonhosted.org/packages/be/c0/21435d804c1a1cf7a2608593f4d19bca5bcbd7a81a70b253fdd1c12af9c0/multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754", size = 243452, upload-time = "2025-10-06T14:50:39.574Z" },
+ { url = "https://files.pythonhosted.org/packages/54/0a/4349d540d4a883863191be6eb9a928846d4ec0ea007d3dcd36323bb058ac/multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c", size = 252312, upload-time = "2025-10-06T14:50:41.612Z" },
+ { url = "https://files.pythonhosted.org/packages/26/64/d5416038dbda1488daf16b676e4dbfd9674dde10a0cc8f4fc2b502d8125d/multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762", size = 246935, upload-time = "2025-10-06T14:50:43.972Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/8c/8290c50d14e49f35e0bd4abc25e1bc7711149ca9588ab7d04f886cdf03d9/multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6", size = 243385, upload-time = "2025-10-06T14:50:45.648Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/a0/f83ae75e42d694b3fbad3e047670e511c138be747bc713cf1b10d5096416/multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d", size = 47777, upload-time = "2025-10-06T14:50:47.154Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/80/9b174a92814a3830b7357307a792300f42c9e94664b01dee8e457551fa66/multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6", size = 53104, upload-time = "2025-10-06T14:50:48.851Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/28/04baeaf0428d95bb7a7bea0e691ba2f31394338ba424fb0679a9ed0f4c09/multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792", size = 45503, upload-time = "2025-10-06T14:50:50.16Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/b1/3da6934455dd4b261d4c72f897e3a5728eba81db59959f3a639245891baa/multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842", size = 75128, upload-time = "2025-10-06T14:50:51.92Z" },
+ { url = "https://files.pythonhosted.org/packages/14/2c/f069cab5b51d175a1a2cb4ccdf7a2c2dabd58aa5bd933fa036a8d15e2404/multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b", size = 44410, upload-time = "2025-10-06T14:50:53.275Z" },
+ { url = "https://files.pythonhosted.org/packages/42/e2/64bb41266427af6642b6b128e8774ed84c11b80a90702c13ac0a86bb10cc/multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38", size = 43205, upload-time = "2025-10-06T14:50:54.911Z" },
+ { url = "https://files.pythonhosted.org/packages/02/68/6b086fef8a3f1a8541b9236c594f0c9245617c29841f2e0395d979485cde/multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128", size = 245084, upload-time = "2025-10-06T14:50:56.369Z" },
+ { url = "https://files.pythonhosted.org/packages/15/ee/f524093232007cd7a75c1d132df70f235cfd590a7c9eaccd7ff422ef4ae8/multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34", size = 252667, upload-time = "2025-10-06T14:50:57.991Z" },
+ { url = "https://files.pythonhosted.org/packages/02/a5/eeb3f43ab45878f1895118c3ef157a480db58ede3f248e29b5354139c2c9/multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99", size = 233590, upload-time = "2025-10-06T14:50:59.589Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/1e/76d02f8270b97269d7e3dbd45644b1785bda457b474315f8cf999525a193/multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202", size = 264112, upload-time = "2025-10-06T14:51:01.183Z" },
+ { url = "https://files.pythonhosted.org/packages/76/0b/c28a70ecb58963847c2a8efe334904cd254812b10e535aefb3bcce513918/multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1", size = 261194, upload-time = "2025-10-06T14:51:02.794Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/63/2ab26e4209773223159b83aa32721b4021ffb08102f8ac7d689c943fded1/multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3", size = 248510, upload-time = "2025-10-06T14:51:04.724Z" },
+ { url = "https://files.pythonhosted.org/packages/93/cd/06c1fa8282af1d1c46fd55c10a7930af652afdce43999501d4d68664170c/multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d", size = 248395, upload-time = "2025-10-06T14:51:06.306Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ac/82cb419dd6b04ccf9e7e61befc00c77614fc8134362488b553402ecd55ce/multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6", size = 239520, upload-time = "2025-10-06T14:51:08.091Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/f3/a0f9bf09493421bd8716a362e0cd1d244f5a6550f5beffdd6b47e885b331/multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7", size = 245479, upload-time = "2025-10-06T14:51:10.365Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/01/476d38fc73a212843f43c852b0eee266b6971f0e28329c2184a8df90c376/multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb", size = 258903, upload-time = "2025-10-06T14:51:12.466Z" },
+ { url = "https://files.pythonhosted.org/packages/49/6d/23faeb0868adba613b817d0e69c5f15531b24d462af8012c4f6de4fa8dc3/multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f", size = 252333, upload-time = "2025-10-06T14:51:14.48Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/cc/48d02ac22b30fa247f7dad82866e4b1015431092f4ba6ebc7e77596e0b18/multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f", size = 243411, upload-time = "2025-10-06T14:51:16.072Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/03/29a8bf5a18abf1fe34535c88adbdfa88c9fb869b5a3b120692c64abe8284/multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885", size = 40940, upload-time = "2025-10-06T14:51:17.544Z" },
+ { url = "https://files.pythonhosted.org/packages/82/16/7ed27b680791b939de138f906d5cf2b4657b0d45ca6f5dd6236fdddafb1a/multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c", size = 45087, upload-time = "2025-10-06T14:51:18.875Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/3c/e3e62eb35a1950292fe39315d3c89941e30a9d07d5d2df42965ab041da43/multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000", size = 42368, upload-time = "2025-10-06T14:51:20.225Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/40/cd499bd0dbc5f1136726db3153042a735fffd0d77268e2ee20d5f33c010f/multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63", size = 82326, upload-time = "2025-10-06T14:51:21.588Z" },
+ { url = "https://files.pythonhosted.org/packages/13/8a/18e031eca251c8df76daf0288e6790561806e439f5ce99a170b4af30676b/multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718", size = 48065, upload-time = "2025-10-06T14:51:22.93Z" },
+ { url = "https://files.pythonhosted.org/packages/40/71/5e6701277470a87d234e433fb0a3a7deaf3bcd92566e421e7ae9776319de/multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2", size = 46475, upload-time = "2025-10-06T14:51:24.352Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/6a/bab00cbab6d9cfb57afe1663318f72ec28289ea03fd4e8236bb78429893a/multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e", size = 239324, upload-time = "2025-10-06T14:51:25.822Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/5f/8de95f629fc22a7769ade8b41028e3e5a822c1f8904f618d175945a81ad3/multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064", size = 246877, upload-time = "2025-10-06T14:51:27.604Z" },
+ { url = "https://files.pythonhosted.org/packages/23/b4/38881a960458f25b89e9f4a4fdcb02ac101cfa710190db6e5528841e67de/multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e", size = 225824, upload-time = "2025-10-06T14:51:29.664Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/39/6566210c83f8a261575f18e7144736059f0c460b362e96e9cf797a24b8e7/multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd", size = 253558, upload-time = "2025-10-06T14:51:31.684Z" },
+ { url = "https://files.pythonhosted.org/packages/00/a3/67f18315100f64c269f46e6c0319fa87ba68f0f64f2b8e7fd7c72b913a0b/multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a", size = 252339, upload-time = "2025-10-06T14:51:33.699Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/2a/1cb77266afee2458d82f50da41beba02159b1d6b1f7973afc9a1cad1499b/multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96", size = 244895, upload-time = "2025-10-06T14:51:36.189Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/72/09fa7dd487f119b2eb9524946ddd36e2067c08510576d43ff68469563b3b/multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e", size = 241862, upload-time = "2025-10-06T14:51:41.291Z" },
+ { url = "https://files.pythonhosted.org/packages/65/92/bc1f8bd0853d8669300f732c801974dfc3702c3eeadae2f60cef54dc69d7/multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599", size = 232376, upload-time = "2025-10-06T14:51:43.55Z" },
+ { url = "https://files.pythonhosted.org/packages/09/86/ac39399e5cb9d0c2ac8ef6e10a768e4d3bc933ac808d49c41f9dc23337eb/multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394", size = 240272, upload-time = "2025-10-06T14:51:45.265Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/b6/fed5ac6b8563ec72df6cb1ea8dac6d17f0a4a1f65045f66b6d3bf1497c02/multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38", size = 248774, upload-time = "2025-10-06T14:51:46.836Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/8d/b954d8c0dc132b68f760aefd45870978deec6818897389dace00fcde32ff/multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9", size = 242731, upload-time = "2025-10-06T14:51:48.541Z" },
+ { url = "https://files.pythonhosted.org/packages/16/9d/a2dac7009125d3540c2f54e194829ea18ac53716c61b655d8ed300120b0f/multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0", size = 240193, upload-time = "2025-10-06T14:51:50.355Z" },
+ { url = "https://files.pythonhosted.org/packages/39/ca/c05f144128ea232ae2178b008d5011d4e2cea86e4ee8c85c2631b1b94802/multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13", size = 48023, upload-time = "2025-10-06T14:51:51.883Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8f/0a60e501584145588be1af5cc829265701ba3c35a64aec8e07cbb71d39bb/multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd", size = 53507, upload-time = "2025-10-06T14:51:53.672Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/ae/3148b988a9c6239903e786eac19c889fab607c31d6efa7fb2147e5680f23/multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827", size = 44804, upload-time = "2025-10-06T14:51:55.415Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", size = 12317, upload-time = "2025-10-06T14:52:29.272Z" },
+]
+
+[[package]]
+name = "mypy"
+version = "1.19.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "librt", marker = "platform_python_implementation != 'PyPy'" },
+ { name = "mypy-extensions" },
+ { name = "pathspec" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" },
+ { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" },
+ { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" },
+ { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" },
+ { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" },
+ { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" },
+ { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" },
+ { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" },
+ { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" },
+ { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" },
+ { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" },
+ { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" },
+ { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" },
+]
+
+[[package]]
+name = "mypy-extensions"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
+]
+
+[[package]]
+name = "nest-asyncio"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" },
+]
+
+[[package]]
+name = "packaging"
+version = "25.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
+]
+
+[[package]]
+name = "parsimonious"
+version = "0.10.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "regex" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/7b/91/abdc50c4ef06fdf8d047f60ee777ca9b2a7885e1a9cea81343fbecda52d7/parsimonious-0.10.0.tar.gz", hash = "sha256:8281600da180ec8ae35427a4ab4f7b82bfec1e3d1e52f80cb60ea82b9512501c", size = 52172, upload-time = "2022-09-03T17:01:17.004Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/aa/0f/c8b64d9b54ea631fcad4e9e3c8dbe8c11bb32a623be94f22974c88e71eaf/parsimonious-0.10.0-py3-none-any.whl", hash = "sha256:982ab435fabe86519b57f6b35610aa4e4e977e9f02a14353edf4bbc75369fc0f", size = 48427, upload-time = "2022-09-03T17:01:13.814Z" },
+]
+
+[[package]]
+name = "pathspec"
+version = "0.12.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
+]
+
+[[package]]
+name = "platformdirs"
+version = "4.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
+]
+
+[[package]]
+name = "pluggy"
+version = "1.6.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
+]
+
+[[package]]
+name = "propcache"
+version = "0.4.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" },
+ { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" },
+ { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" },
+ { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" },
+ { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" },
+ { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" },
+ { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" },
+ { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" },
+ { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" },
+ { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" },
+ { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" },
+ { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" },
+ { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" },
+ { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" },
+ { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" },
+ { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" },
+ { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" },
+ { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" },
+ { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" },
+ { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" },
+ { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" },
+ { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" },
+ { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" },
+ { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" },
+ { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" },
+ { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" },
+ { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" },
+ { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" },
+ { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" },
+ { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" },
+ { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" },
+ { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" },
+ { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" },
+ { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" },
+ { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" },
+ { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" },
+ { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" },
+ { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" },
+ { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" },
+ { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" },
+ { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" },
+ { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" },
+ { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" },
+]
+
+[[package]]
+name = "pycryptodome"
+version = "3.23.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/a6/8452177684d5e906854776276ddd34eca30d1b1e15aa1ee9cefc289a33f5/pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef", size = 4921276, upload-time = "2025-05-17T17:21:45.242Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/04/5d/bdb09489b63cd34a976cc9e2a8d938114f7a53a74d3dd4f125ffa49dce82/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4", size = 2495152, upload-time = "2025-05-17T17:20:20.833Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/ce/7840250ed4cc0039c433cd41715536f926d6e86ce84e904068eb3244b6a6/pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae", size = 1639348, upload-time = "2025-05-17T17:20:23.171Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/f0/991da24c55c1f688d6a3b5a11940567353f74590734ee4a64294834ae472/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477", size = 2184033, upload-time = "2025-05-17T17:20:25.424Z" },
+ { url = "https://files.pythonhosted.org/packages/54/16/0e11882deddf00f68b68dd4e8e442ddc30641f31afeb2bc25588124ac8de/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7", size = 2270142, upload-time = "2025-05-17T17:20:27.808Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/fc/4347fea23a3f95ffb931f383ff28b3f7b1fe868739182cb76718c0da86a1/pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446", size = 2309384, upload-time = "2025-05-17T17:20:30.765Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/d9/c5261780b69ce66d8cfab25d2797bd6e82ba0241804694cd48be41add5eb/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265", size = 2183237, upload-time = "2025-05-17T17:20:33.736Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/6f/3af2ffedd5cfa08c631f89452c6648c4d779e7772dfc388c77c920ca6bbf/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b", size = 2343898, upload-time = "2025-05-17T17:20:36.086Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/dc/9060d807039ee5de6e2f260f72f3d70ac213993a804f5e67e0a73a56dd2f/pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d", size = 2269197, upload-time = "2025-05-17T17:20:38.414Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/34/e6c8ca177cb29dcc4967fef73f5de445912f93bd0343c9c33c8e5bf8cde8/pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a", size = 1768600, upload-time = "2025-05-17T17:20:40.688Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/1d/89756b8d7ff623ad0160f4539da571d1f594d21ee6d68be130a6eccb39a4/pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625", size = 1799740, upload-time = "2025-05-17T17:20:42.413Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/61/35a64f0feaea9fd07f0d91209e7be91726eb48c0f1bfc6720647194071e4/pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39", size = 1703685, upload-time = "2025-05-17T17:20:44.388Z" },
+ { url = "https://files.pythonhosted.org/packages/db/6c/a1f71542c969912bb0e106f64f60a56cc1f0fabecf9396f45accbe63fa68/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27", size = 2495627, upload-time = "2025-05-17T17:20:47.139Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/4e/a066527e079fc5002390c8acdd3aca431e6ea0a50ffd7201551175b47323/pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843", size = 1640362, upload-time = "2025-05-17T17:20:50.392Z" },
+ { url = "https://files.pythonhosted.org/packages/50/52/adaf4c8c100a8c49d2bd058e5b551f73dfd8cb89eb4911e25a0c469b6b4e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490", size = 2182625, upload-time = "2025-05-17T17:20:52.866Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/e9/a09476d436d0ff1402ac3867d933c61805ec2326c6ea557aeeac3825604e/pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575", size = 2268954, upload-time = "2025-05-17T17:20:55.027Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c5/ffe6474e0c551d54cab931918127c46d70cab8f114e0c2b5a3c071c2f484/pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b", size = 2308534, upload-time = "2025-05-17T17:20:57.279Z" },
+ { url = "https://files.pythonhosted.org/packages/18/28/e199677fc15ecf43010f2463fde4c1a53015d1fe95fb03bca2890836603a/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a", size = 2181853, upload-time = "2025-05-17T17:20:59.322Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/ea/4fdb09f2165ce1365c9eaefef36625583371ee514db58dc9b65d3a255c4c/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f", size = 2342465, upload-time = "2025-05-17T17:21:03.83Z" },
+ { url = "https://files.pythonhosted.org/packages/22/82/6edc3fc42fe9284aead511394bac167693fb2b0e0395b28b8bedaa07ef04/pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa", size = 2267414, upload-time = "2025-05-17T17:21:06.72Z" },
+ { url = "https://files.pythonhosted.org/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886", size = 1768484, upload-time = "2025-05-17T17:21:08.535Z" },
+ { url = "https://files.pythonhosted.org/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2", size = 1799636, upload-time = "2025-05-17T17:21:10.393Z" },
+ { url = "https://files.pythonhosted.org/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c", size = 1703675, upload-time = "2025-05-17T17:21:13.146Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379", size = 1623886, upload-time = "2025-05-17T17:21:20.614Z" },
+ { url = "https://files.pythonhosted.org/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4", size = 1672151, upload-time = "2025-05-17T17:21:22.666Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630", size = 1664461, upload-time = "2025-05-17T17:21:25.225Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/92/608fbdad566ebe499297a86aae5f2a5263818ceeecd16733006f1600403c/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353", size = 1702440, upload-time = "2025-05-17T17:21:27.991Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/92/2eadd1341abd2989cce2e2740b4423608ee2014acb8110438244ee97d7ff/pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5", size = 1803005, upload-time = "2025-05-17T17:21:31.37Z" },
+]
+
+[[package]]
+name = "pydantic"
+version = "2.12.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "annotated-types" },
+ { name = "pydantic-core" },
+ { name = "typing-extensions" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" },
+]
+
+[package.optional-dependencies]
+email = [
+ { name = "email-validator" },
+]
+
+[[package]]
+name = "pydantic-core"
+version = "2.41.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" },
+ { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" },
+ { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" },
+ { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" },
+ { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" },
+ { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" },
+ { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" },
+ { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" },
+ { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" },
+ { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" },
+ { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" },
+ { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" },
+ { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" },
+ { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" },
+ { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" },
+ { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" },
+ { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" },
+ { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" },
+ { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" },
+ { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" },
+ { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" },
+ { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" },
+ { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" },
+ { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" },
+ { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" },
+ { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" },
+ { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" },
+ { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" },
+ { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" },
+ { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" },
+ { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" },
+ { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" },
+ { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" },
+ { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" },
+ { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" },
+ { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" },
+ { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" },
+ { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" },
+ { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" },
+ { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" },
+ { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" },
+ { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" },
+ { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
+]
+
+[[package]]
+name = "pydantic-extra-types"
+version = "2.11.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fd/35/2fee58b1316a73e025728583d3b1447218a97e621933fc776fb8c0f2ebdd/pydantic_extra_types-2.11.0.tar.gz", hash = "sha256:4e9991959d045b75feb775683437a97991d02c138e00b59176571db9ce634f0e", size = 157226, upload-time = "2025-12-31T16:18:27.944Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fe/17/fabd56da47096d240dd45ba627bead0333b0cf0ee8ada9bec579287dadf3/pydantic_extra_types-2.11.0-py3-none-any.whl", hash = "sha256:84b864d250a0fc62535b7ec591e36f2c5b4d1325fa0017eb8cda9aeb63b374a6", size = 74296, upload-time = "2025-12-31T16:18:26.38Z" },
+]
+
+[[package]]
+name = "pydantic-settings"
+version = "2.12.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pydantic" },
+ { name = "python-dotenv" },
+ { name = "typing-inspection" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" },
+]
+
+[[package]]
+name = "pygments"
+version = "2.19.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
+]
+
+[[package]]
+name = "pytest"
+version = "9.0.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
+ { name = "iniconfig" },
+ { name = "packaging" },
+ { name = "pluggy" },
+ { name = "pygments" },
+ { name = "tomli", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
+]
+
+[[package]]
+name = "pytest-asyncio"
+version = "1.3.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
+ { name = "pytest" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
+]
+
+[[package]]
+name = "python-dotenv"
+version = "1.2.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" },
+]
+
+[[package]]
+name = "python-multipart"
+version = "0.0.21"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/78/96/804520d0850c7db98e5ccb70282e29208723f0964e88ffd9d0da2f52ea09/python_multipart-0.0.21.tar.gz", hash = "sha256:7137ebd4d3bbf70ea1622998f902b97a29434a9e8dc40eb203bbcf7c2a2cba92", size = 37196, upload-time = "2025-12-17T09:24:22.446Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/aa/76/03af049af4dcee5d27442f71b6924f01f3efb5d2bd34f23fcd563f2cc5f5/python_multipart-0.0.21-py3-none-any.whl", hash = "sha256:cf7a6713e01c87aa35387f4774e812c4361150938d20d232800f75ffcf266090", size = 24541, upload-time = "2025-12-17T09:24:21.153Z" },
+]
+
+[[package]]
+name = "pytokens"
+version = "0.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" },
+]
+
+[[package]]
+name = "pyunormalize"
+version = "17.0.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/25/ab/b912c484cfb96ba4834efe050bbf10c9e157bd8189eb859aefba8712b136/pyunormalize-17.0.0.tar.gz", hash = "sha256:0949a3e56817e287febcaf1b0cc4b5adf0bb107628d379335938040947eec792", size = 53121, upload-time = "2025-09-28T20:53:06.141Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/92/80/61512483dc509e3ae8a42fb143479d1e406ce1d91f8f08d538a3dde39c6d/pyunormalize-17.0.0-py3-none-any.whl", hash = "sha256:f0d93b076f938db2b26d319d04f2b58505d1cd7a80b5b72badbe7d1aa4d2a31c", size = 51358, upload-time = "2025-09-28T20:53:04.876Z" },
+]
+
+[[package]]
+name = "pywin32"
+version = "311"
+source = { registry = "https://pypi.org/simple" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" },
+ { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" },
+ { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" },
+ { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" },
+ { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" },
+ { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" },
+]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f4/a0/39350dd17dd6d6c6507025c0e53aef67a9293a6d37d3511f23ea510d5800/pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b", size = 184227, upload-time = "2025-09-25T21:31:46.04Z" },
+ { url = "https://files.pythonhosted.org/packages/05/14/52d505b5c59ce73244f59c7a50ecf47093ce4765f116cdb98286a71eeca2/pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956", size = 174019, upload-time = "2025-09-25T21:31:47.706Z" },
+ { url = "https://files.pythonhosted.org/packages/43/f7/0e6a5ae5599c838c696adb4e6330a59f463265bfa1e116cfd1fbb0abaaae/pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8", size = 740646, upload-time = "2025-09-25T21:31:49.21Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/3a/61b9db1d28f00f8fd0ae760459a5c4bf1b941baf714e207b6eb0657d2578/pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198", size = 840793, upload-time = "2025-09-25T21:31:50.735Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1e/7acc4f0e74c4b3d9531e24739e0ab832a5edf40e64fbae1a9c01941cabd7/pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b", size = 770293, upload-time = "2025-09-25T21:31:51.828Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/ef/abd085f06853af0cd59fa5f913d61a8eab65d7639ff2a658d18a25d6a89d/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0", size = 732872, upload-time = "2025-09-25T21:31:53.282Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/15/2bc9c8faf6450a8b3c9fc5448ed869c599c0a74ba2669772b1f3a0040180/pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69", size = 758828, upload-time = "2025-09-25T21:31:54.807Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/00/531e92e88c00f4333ce359e50c19b8d1de9fe8d581b1534e35ccfbc5f393/pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e", size = 142415, upload-time = "2025-09-25T21:31:55.885Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/fa/926c003379b19fca39dd4634818b00dec6c62d87faf628d1394e137354d4/pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c", size = 158561, upload-time = "2025-09-25T21:31:57.406Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/16/a95b6757765b7b031c9374925bb718d55e0a9ba8a1b6a12d25962ea44347/pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e", size = 185826, upload-time = "2025-09-25T21:31:58.655Z" },
+ { url = "https://files.pythonhosted.org/packages/16/19/13de8e4377ed53079ee996e1ab0a9c33ec2faf808a4647b7b4c0d46dd239/pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824", size = 175577, upload-time = "2025-09-25T21:32:00.088Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/62/d2eb46264d4b157dae1275b573017abec435397aa59cbcdab6fc978a8af4/pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c", size = 775556, upload-time = "2025-09-25T21:32:01.31Z" },
+ { url = "https://files.pythonhosted.org/packages/10/cb/16c3f2cf3266edd25aaa00d6c4350381c8b012ed6f5276675b9eba8d9ff4/pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00", size = 882114, upload-time = "2025-09-25T21:32:03.376Z" },
+ { url = "https://files.pythonhosted.org/packages/71/60/917329f640924b18ff085ab889a11c763e0b573da888e8404ff486657602/pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d", size = 806638, upload-time = "2025-09-25T21:32:04.553Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/6f/529b0f316a9fd167281a6c3826b5583e6192dba792dd55e3203d3f8e655a/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a", size = 767463, upload-time = "2025-09-25T21:32:06.152Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/6a/b627b4e0c1dd03718543519ffb2f1deea4a1e6d42fbab8021936a4d22589/pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4", size = 794986, upload-time = "2025-09-25T21:32:07.367Z" },
+ { url = "https://files.pythonhosted.org/packages/45/91/47a6e1c42d9ee337c4839208f30d9f09caa9f720ec7582917b264defc875/pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b", size = 142543, upload-time = "2025-09-25T21:32:08.95Z" },
+ { url = "https://files.pythonhosted.org/packages/da/e3/ea007450a105ae919a72393cb06f122f288ef60bba2dc64b26e2646fa315/pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf", size = 158763, upload-time = "2025-09-25T21:32:09.96Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
+ { url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
+ { url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
+ { url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
+ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
+ { url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
+ { url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
+ { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
+ { url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
+ { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
+ { url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
+ { url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
+ { url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
+ { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
+ { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
+ { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
+ { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
+]
+
+[[package]]
+name = "regex"
+version = "2025.11.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/546676f25e573a4cf00fe8e119b78a37b6a8fe2dc95cda877b30889c9c45/regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01", size = 414669, upload-time = "2025-11-03T21:34:22.089Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8a/d6/d788d52da01280a30a3f6268aef2aa71043bff359c618fea4c5b536654d5/regex-2025.11.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2b441a4ae2c8049106e8b39973bfbddfb25a179dda2bdb99b0eeb60c40a6a3af", size = 488087, upload-time = "2025-11-03T21:30:47.317Z" },
+ { url = "https://files.pythonhosted.org/packages/69/39/abec3bd688ec9bbea3562de0fd764ff802976185f5ff22807bf0a2697992/regex-2025.11.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2fa2eed3f76677777345d2f81ee89f5de2f5745910e805f7af7386a920fa7313", size = 290544, upload-time = "2025-11-03T21:30:49.912Z" },
+ { url = "https://files.pythonhosted.org/packages/39/b3/9a231475d5653e60002508f41205c61684bb2ffbf2401351ae2186897fc4/regex-2025.11.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8b4a27eebd684319bdf473d39f1d79eed36bf2cd34bd4465cdb4618d82b3d56", size = 288408, upload-time = "2025-11-03T21:30:51.344Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/c5/1929a0491bd5ac2d1539a866768b88965fa8c405f3e16a8cef84313098d6/regex-2025.11.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cf77eac15bd264986c4a2c63353212c095b40f3affb2bc6b4ef80c4776c1a28", size = 781584, upload-time = "2025-11-03T21:30:52.596Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/fd/16aa16cf5d497ef727ec966f74164fbe75d6516d3d58ac9aa989bc9cdaad/regex-2025.11.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b7f9ee819f94c6abfa56ec7b1dbab586f41ebbdc0a57e6524bd5e7f487a878c7", size = 850733, upload-time = "2025-11-03T21:30:53.825Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/49/3294b988855a221cb6565189edf5dc43239957427df2d81d4a6b15244f64/regex-2025.11.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:838441333bc90b829406d4a03cb4b8bf7656231b84358628b0406d803931ef32", size = 898691, upload-time = "2025-11-03T21:30:55.575Z" },
+ { url = "https://files.pythonhosted.org/packages/14/62/b56d29e70b03666193369bdbdedfdc23946dbe9f81dd78ce262c74d988ab/regex-2025.11.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cfe6d3f0c9e3b7e8c0c694b24d25e677776f5ca26dce46fd6b0489f9c8339391", size = 791662, upload-time = "2025-11-03T21:30:57.262Z" },
+ { url = "https://files.pythonhosted.org/packages/15/fc/e4c31d061eced63fbf1ce9d853975f912c61a7d406ea14eda2dd355f48e7/regex-2025.11.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2ab815eb8a96379a27c3b6157fcb127c8f59c36f043c1678110cea492868f1d5", size = 782587, upload-time = "2025-11-03T21:30:58.788Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/bb/5e30c7394bcf63f0537121c23e796be67b55a8847c3956ae6068f4c70702/regex-2025.11.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:728a9d2d173a65b62bdc380b7932dd8e74ed4295279a8fe1021204ce210803e7", size = 774709, upload-time = "2025-11-03T21:31:00.081Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/c4/fce773710af81b0cb37cb4ff0947e75d5d17dee304b93d940b87a67fc2f4/regex-2025.11.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:509dc827f89c15c66a0c216331260d777dd6c81e9a4e4f830e662b0bb296c313", size = 845773, upload-time = "2025-11-03T21:31:01.583Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/5e/9466a7ec4b8ec282077095c6eb50a12a389d2e036581134d4919e8ca518c/regex-2025.11.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:849202cd789e5f3cf5dcc7822c34b502181b4824a65ff20ce82da5524e45e8e9", size = 836164, upload-time = "2025-11-03T21:31:03.244Z" },
+ { url = "https://files.pythonhosted.org/packages/95/18/82980a60e8ed1594eb3c89eb814fb276ef51b9af7caeab1340bfd8564af6/regex-2025.11.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b6f78f98741dcc89607c16b1e9426ee46ce4bf31ac5e6b0d40e81c89f3481ea5", size = 779832, upload-time = "2025-11-03T21:31:04.876Z" },
+ { url = "https://files.pythonhosted.org/packages/03/cc/90ab0fdbe6dce064a42015433f9152710139fb04a8b81b4fb57a1cb63ffa/regex-2025.11.3-cp310-cp310-win32.whl", hash = "sha256:149eb0bba95231fb4f6d37c8f760ec9fa6fabf65bab555e128dde5f2475193ec", size = 265802, upload-time = "2025-11-03T21:31:06.581Z" },
+ { url = "https://files.pythonhosted.org/packages/34/9d/e9e8493a85f3b1ddc4a5014465f5c2b78c3ea1cbf238dcfde78956378041/regex-2025.11.3-cp310-cp310-win_amd64.whl", hash = "sha256:ee3a83ce492074c35a74cc76cf8235d49e77b757193a5365ff86e3f2f93db9fd", size = 277722, upload-time = "2025-11-03T21:31:08.144Z" },
+ { url = "https://files.pythonhosted.org/packages/15/c4/b54b24f553966564506dbf873a3e080aef47b356a3b39b5d5aba992b50db/regex-2025.11.3-cp310-cp310-win_arm64.whl", hash = "sha256:38af559ad934a7b35147716655d4a2f79fcef2d695ddfe06a06ba40ae631fa7e", size = 270289, upload-time = "2025-11-03T21:31:10.267Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/90/4fb5056e5f03a7048abd2b11f598d464f0c167de4f2a51aa868c376b8c70/regex-2025.11.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eadade04221641516fa25139273505a1c19f9bf97589a05bc4cfcd8b4a618031", size = 488081, upload-time = "2025-11-03T21:31:11.946Z" },
+ { url = "https://files.pythonhosted.org/packages/85/23/63e481293fac8b069d84fba0299b6666df720d875110efd0338406b5d360/regex-2025.11.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feff9e54ec0dd3833d659257f5c3f5322a12eee58ffa360984b716f8b92983f4", size = 290554, upload-time = "2025-11-03T21:31:13.387Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9d/b101d0262ea293a0066b4522dfb722eb6a8785a8c3e084396a5f2c431a46/regex-2025.11.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3b30bc921d50365775c09a7ed446359e5c0179e9e2512beec4a60cbcef6ddd50", size = 288407, upload-time = "2025-11-03T21:31:14.809Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/64/79241c8209d5b7e00577ec9dca35cd493cc6be35b7d147eda367d6179f6d/regex-2025.11.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f99be08cfead2020c7ca6e396c13543baea32343b7a9a5780c462e323bd8872f", size = 793418, upload-time = "2025-11-03T21:31:16.556Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e2/23cd5d3573901ce8f9757c92ca4db4d09600b865919b6d3e7f69f03b1afd/regex-2025.11.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6dd329a1b61c0ee95ba95385fb0c07ea0d3fe1a21e1349fa2bec272636217118", size = 860448, upload-time = "2025-11-03T21:31:18.12Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/4c/aecf31beeaa416d0ae4ecb852148d38db35391aac19c687b5d56aedf3a8b/regex-2025.11.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c5238d32f3c5269d9e87be0cf096437b7622b6920f5eac4fd202468aaeb34d2", size = 907139, upload-time = "2025-11-03T21:31:20.753Z" },
+ { url = "https://files.pythonhosted.org/packages/61/22/b8cb00df7d2b5e0875f60628594d44dba283e951b1ae17c12f99e332cc0a/regex-2025.11.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10483eefbfb0adb18ee9474498c9a32fcf4e594fbca0543bb94c48bac6183e2e", size = 800439, upload-time = "2025-11-03T21:31:22.069Z" },
+ { url = "https://files.pythonhosted.org/packages/02/a8/c4b20330a5cdc7a8eb265f9ce593f389a6a88a0c5f280cf4d978f33966bc/regex-2025.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78c2d02bb6e1da0720eedc0bad578049cad3f71050ef8cd065ecc87691bed2b0", size = 782965, upload-time = "2025-11-03T21:31:23.598Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/4c/ae3e52988ae74af4b04d2af32fee4e8077f26e51b62ec2d12d246876bea2/regex-2025.11.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b49cd2aad93a1790ce9cffb18964f6d3a4b0b3dbdbd5de094b65296fce6e58", size = 854398, upload-time = "2025-11-03T21:31:25.008Z" },
+ { url = "https://files.pythonhosted.org/packages/06/d1/a8b9cf45874eda14b2e275157ce3b304c87e10fb38d9fc26a6e14eb18227/regex-2025.11.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:885b26aa3ee56433b630502dc3d36ba78d186a00cc535d3806e6bfd9ed3c70ab", size = 845897, upload-time = "2025-11-03T21:31:26.427Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/fe/1830eb0236be93d9b145e0bd8ab499f31602fe0999b1f19e99955aa8fe20/regex-2025.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ddd76a9f58e6a00f8772e72cff8ebcff78e022be95edf018766707c730593e1e", size = 788906, upload-time = "2025-11-03T21:31:28.078Z" },
+ { url = "https://files.pythonhosted.org/packages/66/47/dc2577c1f95f188c1e13e2e69d8825a5ac582ac709942f8a03af42ed6e93/regex-2025.11.3-cp311-cp311-win32.whl", hash = "sha256:3e816cc9aac1cd3cc9a4ec4d860f06d40f994b5c7b4d03b93345f44e08cc68bf", size = 265812, upload-time = "2025-11-03T21:31:29.72Z" },
+ { url = "https://files.pythonhosted.org/packages/50/1e/15f08b2f82a9bbb510621ec9042547b54d11e83cb620643ebb54e4eb7d71/regex-2025.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:087511f5c8b7dfbe3a03f5d5ad0c2a33861b1fc387f21f6f60825a44865a385a", size = 277737, upload-time = "2025-11-03T21:31:31.422Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/fc/6500eb39f5f76c5e47a398df82e6b535a5e345f839581012a418b16f9cc3/regex-2025.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:1ff0d190c7f68ae7769cd0313fe45820ba07ffebfddfaa89cc1eb70827ba0ddc", size = 270290, upload-time = "2025-11-03T21:31:33.041Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/74/18f04cb53e58e3fb107439699bd8375cf5a835eec81084e0bddbd122e4c2/regex-2025.11.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bc8ab71e2e31b16e40868a40a69007bc305e1109bd4658eb6cad007e0bf67c41", size = 489312, upload-time = "2025-11-03T21:31:34.343Z" },
+ { url = "https://files.pythonhosted.org/packages/78/3f/37fcdd0d2b1e78909108a876580485ea37c91e1acf66d3bb8e736348f441/regex-2025.11.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:22b29dda7e1f7062a52359fca6e58e548e28c6686f205e780b02ad8ef710de36", size = 291256, upload-time = "2025-11-03T21:31:35.675Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/26/0a575f58eb23b7ebd67a45fccbc02ac030b737b896b7e7a909ffe43ffd6a/regex-2025.11.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a91e4a29938bc1a082cc28fdea44be420bf2bebe2665343029723892eb073e1", size = 288921, upload-time = "2025-11-03T21:31:37.07Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/98/6a8dff667d1af907150432cf5abc05a17ccd32c72a3615410d5365ac167a/regex-2025.11.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b884f4226602ad40c5d55f52bf91a9df30f513864e0054bad40c0e9cf1afb7", size = 798568, upload-time = "2025-11-03T21:31:38.784Z" },
+ { url = "https://files.pythonhosted.org/packages/64/15/92c1db4fa4e12733dd5a526c2dd2b6edcbfe13257e135fc0f6c57f34c173/regex-2025.11.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e0b11b2b2433d1c39c7c7a30e3f3d0aeeea44c2a8d0bae28f6b95f639927a69", size = 864165, upload-time = "2025-11-03T21:31:40.559Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/e7/3ad7da8cdee1ce66c7cd37ab5ab05c463a86ffeb52b1a25fe7bd9293b36c/regex-2025.11.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87eb52a81ef58c7ba4d45c3ca74e12aa4b4e77816f72ca25258a85b3ea96cb48", size = 912182, upload-time = "2025-11-03T21:31:42.002Z" },
+ { url = "https://files.pythonhosted.org/packages/84/bd/9ce9f629fcb714ffc2c3faf62b6766ecb7a585e1e885eb699bcf130a5209/regex-2025.11.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a12ab1f5c29b4e93db518f5e3872116b7e9b1646c9f9f426f777b50d44a09e8c", size = 803501, upload-time = "2025-11-03T21:31:43.815Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/0f/8dc2e4349d8e877283e6edd6c12bdcebc20f03744e86f197ab6e4492bf08/regex-2025.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7521684c8c7c4f6e88e35ec89680ee1aa8358d3f09d27dfbdf62c446f5d4c695", size = 787842, upload-time = "2025-11-03T21:31:45.353Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/73/cff02702960bc185164d5619c0c62a2f598a6abff6695d391b096237d4ab/regex-2025.11.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7fe6e5440584e94cc4b3f5f4d98a25e29ca12dccf8873679a635638349831b98", size = 858519, upload-time = "2025-11-03T21:31:46.814Z" },
+ { url = "https://files.pythonhosted.org/packages/61/83/0e8d1ae71e15bc1dc36231c90b46ee35f9d52fab2e226b0e039e7ea9c10a/regex-2025.11.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8e026094aa12b43f4fd74576714e987803a315c76edb6b098b9809db5de58f74", size = 850611, upload-time = "2025-11-03T21:31:48.289Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/f5/70a5cdd781dcfaa12556f2955bf170cd603cb1c96a1827479f8faea2df97/regex-2025.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:435bbad13e57eb5606a68443af62bed3556de2f46deb9f7d4237bc2f1c9fb3a0", size = 789759, upload-time = "2025-11-03T21:31:49.759Z" },
+ { url = "https://files.pythonhosted.org/packages/59/9b/7c29be7903c318488983e7d97abcf8ebd3830e4c956c4c540005fcfb0462/regex-2025.11.3-cp312-cp312-win32.whl", hash = "sha256:3839967cf4dc4b985e1570fd8d91078f0c519f30491c60f9ac42a8db039be204", size = 266194, upload-time = "2025-11-03T21:31:51.53Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/67/3b92df89f179d7c367be654ab5626ae311cb28f7d5c237b6bb976cd5fbbb/regex-2025.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:e721d1b46e25c481dc5ded6f4b3f66c897c58d2e8cfdf77bbced84339108b0b9", size = 277069, upload-time = "2025-11-03T21:31:53.151Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/55/85ba4c066fe5094d35b249c3ce8df0ba623cfd35afb22d6764f23a52a1c5/regex-2025.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:64350685ff08b1d3a6fff33f45a9ca183dc1d58bbfe4981604e70ec9801bbc26", size = 270330, upload-time = "2025-11-03T21:31:54.514Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/a7/dda24ebd49da46a197436ad96378f17df30ceb40e52e859fc42cac45b850/regex-2025.11.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c1e448051717a334891f2b9a620fe36776ebf3dd8ec46a0b877c8ae69575feb4", size = 489081, upload-time = "2025-11-03T21:31:55.9Z" },
+ { url = "https://files.pythonhosted.org/packages/19/22/af2dc751aacf88089836aa088a1a11c4f21a04707eb1b0478e8e8fb32847/regex-2025.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b5aca4d5dfd7fbfbfbdaf44850fcc7709a01146a797536a8f84952e940cca76", size = 291123, upload-time = "2025-11-03T21:31:57.758Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/88/1a3ea5672f4b0a84802ee9891b86743438e7c04eb0b8f8c4e16a42375327/regex-2025.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:04d2765516395cf7dda331a244a3282c0f5ae96075f728629287dfa6f76ba70a", size = 288814, upload-time = "2025-11-03T21:32:01.12Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/8c/f5987895bf42b8ddeea1b315c9fedcfe07cadee28b9c98cf50d00adcb14d/regex-2025.11.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d9903ca42bfeec4cebedba8022a7c97ad2aab22e09573ce9976ba01b65e4361", size = 798592, upload-time = "2025-11-03T21:32:03.006Z" },
+ { url = "https://files.pythonhosted.org/packages/99/2a/6591ebeede78203fa77ee46a1c36649e02df9eaa77a033d1ccdf2fcd5d4e/regex-2025.11.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:639431bdc89d6429f6721625e8129413980ccd62e9d3f496be618a41d205f160", size = 864122, upload-time = "2025-11-03T21:32:04.553Z" },
+ { url = "https://files.pythonhosted.org/packages/94/d6/be32a87cf28cf8ed064ff281cfbd49aefd90242a83e4b08b5a86b38e8eb4/regex-2025.11.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f117efad42068f9715677c8523ed2be1518116d1c49b1dd17987716695181efe", size = 912272, upload-time = "2025-11-03T21:32:06.148Z" },
+ { url = "https://files.pythonhosted.org/packages/62/11/9bcef2d1445665b180ac7f230406ad80671f0fc2a6ffb93493b5dd8cd64c/regex-2025.11.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4aecb6f461316adf9f1f0f6a4a1a3d79e045f9b71ec76055a791affa3b285850", size = 803497, upload-time = "2025-11-03T21:32:08.162Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/a7/da0dc273d57f560399aa16d8a68ae7f9b57679476fc7ace46501d455fe84/regex-2025.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b3a5f320136873cc5561098dfab677eea139521cb9a9e8db98b7e64aef44cbc", size = 787892, upload-time = "2025-11-03T21:32:09.769Z" },
+ { url = "https://files.pythonhosted.org/packages/da/4b/732a0c5a9736a0b8d6d720d4945a2f1e6f38f87f48f3173559f53e8d5d82/regex-2025.11.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:75fa6f0056e7efb1f42a1c34e58be24072cb9e61a601340cc1196ae92326a4f9", size = 858462, upload-time = "2025-11-03T21:32:11.769Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/f5/a2a03df27dc4c2d0c769220f5110ba8c4084b0bfa9ab0f9b4fcfa3d2b0fc/regex-2025.11.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:dbe6095001465294f13f1adcd3311e50dd84e5a71525f20a10bd16689c61ce0b", size = 850528, upload-time = "2025-11-03T21:32:13.906Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/09/e1cd5bee3841c7f6eb37d95ca91cdee7100b8f88b81e41c2ef426910891a/regex-2025.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:454d9b4ae7881afbc25015b8627c16d88a597479b9dea82b8c6e7e2e07240dc7", size = 789866, upload-time = "2025-11-03T21:32:15.748Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/51/702f5ea74e2a9c13d855a6a85b7f80c30f9e72a95493260193c07f3f8d74/regex-2025.11.3-cp313-cp313-win32.whl", hash = "sha256:28ba4d69171fc6e9896337d4fc63a43660002b7da53fc15ac992abcf3410917c", size = 266189, upload-time = "2025-11-03T21:32:17.493Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/00/6e29bb314e271a743170e53649db0fdb8e8ff0b64b4f425f5602f4eb9014/regex-2025.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:bac4200befe50c670c405dc33af26dad5a3b6b255dd6c000d92fe4629f9ed6a5", size = 277054, upload-time = "2025-11-03T21:32:19.042Z" },
+ { url = "https://files.pythonhosted.org/packages/25/f1/b156ff9f2ec9ac441710764dda95e4edaf5f36aca48246d1eea3f1fd96ec/regex-2025.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:2292cd5a90dab247f9abe892ac584cb24f0f54680c73fcb4a7493c66c2bf2467", size = 270325, upload-time = "2025-11-03T21:32:21.338Z" },
+ { url = "https://files.pythonhosted.org/packages/20/28/fd0c63357caefe5680b8ea052131acbd7f456893b69cc2a90cc3e0dc90d4/regex-2025.11.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:1eb1ebf6822b756c723e09f5186473d93236c06c579d2cc0671a722d2ab14281", size = 491984, upload-time = "2025-11-03T21:32:23.466Z" },
+ { url = "https://files.pythonhosted.org/packages/df/ec/7014c15626ab46b902b3bcc4b28a7bae46d8f281fc7ea9c95e22fcaaa917/regex-2025.11.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1e00ec2970aab10dc5db34af535f21fcf32b4a31d99e34963419636e2f85ae39", size = 292673, upload-time = "2025-11-03T21:32:25.034Z" },
+ { url = "https://files.pythonhosted.org/packages/23/ab/3b952ff7239f20d05f1f99e9e20188513905f218c81d52fb5e78d2bf7634/regex-2025.11.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a4cb042b615245d5ff9b3794f56be4138b5adc35a4166014d31d1814744148c7", size = 291029, upload-time = "2025-11-03T21:32:26.528Z" },
+ { url = "https://files.pythonhosted.org/packages/21/7e/3dc2749fc684f455f162dcafb8a187b559e2614f3826877d3844a131f37b/regex-2025.11.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44f264d4bf02f3176467d90b294d59bf1db9fe53c141ff772f27a8b456b2a9ed", size = 807437, upload-time = "2025-11-03T21:32:28.363Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/0b/d529a85ab349c6a25d1ca783235b6e3eedf187247eab536797021f7126c6/regex-2025.11.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7be0277469bf3bd7a34a9c57c1b6a724532a0d235cd0dc4e7f4316f982c28b19", size = 873368, upload-time = "2025-11-03T21:32:30.4Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/18/2d868155f8c9e3e9d8f9e10c64e9a9f496bb8f7e037a88a8bed26b435af6/regex-2025.11.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d31e08426ff4b5b650f68839f5af51a92a5b51abd8554a60c2fbc7c71f25d0b", size = 914921, upload-time = "2025-11-03T21:32:32.123Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/71/9d72ff0f354fa783fe2ba913c8734c3b433b86406117a8db4ea2bf1c7a2f/regex-2025.11.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e43586ce5bd28f9f285a6e729466841368c4a0353f6fd08d4ce4630843d3648a", size = 812708, upload-time = "2025-11-03T21:32:34.305Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/19/ce4bf7f5575c97f82b6e804ffb5c4e940c62609ab2a0d9538d47a7fdf7d4/regex-2025.11.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0f9397d561a4c16829d4e6ff75202c1c08b68a3bdbfe29dbfcdb31c9830907c6", size = 795472, upload-time = "2025-11-03T21:32:36.364Z" },
+ { url = "https://files.pythonhosted.org/packages/03/86/fd1063a176ffb7b2315f9a1b08d17b18118b28d9df163132615b835a26ee/regex-2025.11.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:dd16e78eb18ffdb25ee33a0682d17912e8cc8a770e885aeee95020046128f1ce", size = 868341, upload-time = "2025-11-03T21:32:38.042Z" },
+ { url = "https://files.pythonhosted.org/packages/12/43/103fb2e9811205e7386366501bc866a164a0430c79dd59eac886a2822950/regex-2025.11.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:ffcca5b9efe948ba0661e9df0fa50d2bc4b097c70b9810212d6b62f05d83b2dd", size = 854666, upload-time = "2025-11-03T21:32:40.079Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/22/e392e53f3869b75804762c7c848bd2dd2abf2b70fb0e526f58724638bd35/regex-2025.11.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c56b4d162ca2b43318ac671c65bd4d563e841a694ac70e1a976ac38fcf4ca1d2", size = 799473, upload-time = "2025-11-03T21:32:42.148Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/f9/8bd6b656592f925b6845fcbb4d57603a3ac2fb2373344ffa1ed70aa6820a/regex-2025.11.3-cp313-cp313t-win32.whl", hash = "sha256:9ddc42e68114e161e51e272f667d640f97e84a2b9ef14b7477c53aac20c2d59a", size = 268792, upload-time = "2025-11-03T21:32:44.13Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/87/0e7d603467775ff65cd2aeabf1b5b50cc1c3708556a8b849a2fa4dd1542b/regex-2025.11.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7a7c7fdf755032ffdd72c77e3d8096bdcb0eb92e89e17571a196f03d88b11b3c", size = 280214, upload-time = "2025-11-03T21:32:45.853Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/d0/2afc6f8e94e2b64bfb738a7c2b6387ac1699f09f032d363ed9447fd2bb57/regex-2025.11.3-cp313-cp313t-win_arm64.whl", hash = "sha256:df9eb838c44f570283712e7cff14c16329a9f0fb19ca492d21d4b7528ee6821e", size = 271469, upload-time = "2025-11-03T21:32:48.026Z" },
+ { url = "https://files.pythonhosted.org/packages/31/e9/f6e13de7e0983837f7b6d238ad9458800a874bf37c264f7923e63409944c/regex-2025.11.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9697a52e57576c83139d7c6f213d64485d3df5bf84807c35fa409e6c970801c6", size = 489089, upload-time = "2025-11-03T21:32:50.027Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/5c/261f4a262f1fa65141c1b74b255988bd2fa020cc599e53b080667d591cfc/regex-2025.11.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e18bc3f73bd41243c9b38a6d9f2366cd0e0137a9aebe2d8ff76c5b67d4c0a3f4", size = 291059, upload-time = "2025-11-03T21:32:51.682Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/57/f14eeb7f072b0e9a5a090d1712741fd8f214ec193dba773cf5410108bb7d/regex-2025.11.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:61a08bcb0ec14ff4e0ed2044aad948d0659604f824cbd50b55e30b0ec6f09c73", size = 288900, upload-time = "2025-11-03T21:32:53.569Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/6b/1d650c45e99a9b327586739d926a1cd4e94666b1bd4af90428b36af66dc7/regex-2025.11.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9c30003b9347c24bcc210958c5d167b9e4f9be786cb380a7d32f14f9b84674f", size = 799010, upload-time = "2025-11-03T21:32:55.222Z" },
+ { url = "https://files.pythonhosted.org/packages/99/ee/d66dcbc6b628ce4e3f7f0cbbb84603aa2fc0ffc878babc857726b8aab2e9/regex-2025.11.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4e1e592789704459900728d88d41a46fe3969b82ab62945560a31732ffc19a6d", size = 864893, upload-time = "2025-11-03T21:32:57.239Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/2d/f238229f1caba7ac87a6c4153d79947fb0261415827ae0f77c304260c7d3/regex-2025.11.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6538241f45eb5a25aa575dbba1069ad786f68a4f2773a29a2bd3dd1f9de787be", size = 911522, upload-time = "2025-11-03T21:32:59.274Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/3d/22a4eaba214a917c80e04f6025d26143690f0419511e0116508e24b11c9b/regex-2025.11.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce22519c989bb72a7e6b36a199384c53db7722fe669ba891da75907fe3587db", size = 803272, upload-time = "2025-11-03T21:33:01.393Z" },
+ { url = "https://files.pythonhosted.org/packages/84/b1/03188f634a409353a84b5ef49754b97dbcc0c0f6fd6c8ede505a8960a0a4/regex-2025.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:66d559b21d3640203ab9075797a55165d79017520685fb407b9234d72ab63c62", size = 787958, upload-time = "2025-11-03T21:33:03.379Z" },
+ { url = "https://files.pythonhosted.org/packages/99/6a/27d072f7fbf6fadd59c64d210305e1ff865cc3b78b526fd147db768c553b/regex-2025.11.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:669dcfb2e38f9e8c69507bace46f4889e3abbfd9b0c29719202883c0a603598f", size = 859289, upload-time = "2025-11-03T21:33:05.374Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/70/1b3878f648e0b6abe023172dacb02157e685564853cc363d9961bcccde4e/regex-2025.11.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:32f74f35ff0f25a5021373ac61442edcb150731fbaa28286bbc8bb1582c89d02", size = 850026, upload-time = "2025-11-03T21:33:07.131Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/d5/68e25559b526b8baab8e66839304ede68ff6727237a47727d240006bd0ff/regex-2025.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e6c7a21dffba883234baefe91bc3388e629779582038f75d2a5be918e250f0ed", size = 789499, upload-time = "2025-11-03T21:33:09.141Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/df/43971264857140a350910d4e33df725e8c94dd9dee8d2e4729fa0d63d49e/regex-2025.11.3-cp314-cp314-win32.whl", hash = "sha256:795ea137b1d809eb6836b43748b12634291c0ed55ad50a7d72d21edf1cd565c4", size = 271604, upload-time = "2025-11-03T21:33:10.9Z" },
+ { url = "https://files.pythonhosted.org/packages/01/6f/9711b57dc6894a55faf80a4c1b5aa4f8649805cb9c7aef46f7d27e2b9206/regex-2025.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f95fbaa0ee1610ec0fc6b26668e9917a582ba80c52cc6d9ada15e30aa9ab9ad", size = 280320, upload-time = "2025-11-03T21:33:12.572Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/7e/f6eaa207d4377481f5e1775cdeb5a443b5a59b392d0065f3417d31d80f87/regex-2025.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:dfec44d532be4c07088c3de2876130ff0fbeeacaa89a137decbbb5f665855a0f", size = 273372, upload-time = "2025-11-03T21:33:14.219Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/06/49b198550ee0f5e4184271cee87ba4dfd9692c91ec55289e6282f0f86ccf/regex-2025.11.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ba0d8a5d7f04f73ee7d01d974d47c5834f8a1b0224390e4fe7c12a3a92a78ecc", size = 491985, upload-time = "2025-11-03T21:33:16.555Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/bf/abdafade008f0b1c9da10d934034cb670432d6cf6cbe38bbb53a1cfd6cf8/regex-2025.11.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:442d86cf1cfe4faabf97db7d901ef58347efd004934da045c745e7b5bd57ac49", size = 292669, upload-time = "2025-11-03T21:33:18.32Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/ef/0c357bb8edbd2ad8e273fcb9e1761bc37b8acbc6e1be050bebd6475f19c1/regex-2025.11.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fd0a5e563c756de210bb964789b5abe4f114dacae9104a47e1a649b910361536", size = 291030, upload-time = "2025-11-03T21:33:20.048Z" },
+ { url = "https://files.pythonhosted.org/packages/79/06/edbb67257596649b8fb088d6aeacbcb248ac195714b18a65e018bf4c0b50/regex-2025.11.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf3490bcbb985a1ae97b2ce9ad1c0f06a852d5b19dde9b07bdf25bf224248c95", size = 807674, upload-time = "2025-11-03T21:33:21.797Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/d9/ad4deccfce0ea336296bd087f1a191543bb99ee1c53093dcd4c64d951d00/regex-2025.11.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3809988f0a8b8c9dcc0f92478d6501fac7200b9ec56aecf0ec21f4a2ec4b6009", size = 873451, upload-time = "2025-11-03T21:33:23.741Z" },
+ { url = "https://files.pythonhosted.org/packages/13/75/a55a4724c56ef13e3e04acaab29df26582f6978c000ac9cd6810ad1f341f/regex-2025.11.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f4ff94e58e84aedb9c9fce66d4ef9f27a190285b451420f297c9a09f2b9abee9", size = 914980, upload-time = "2025-11-03T21:33:25.999Z" },
+ { url = "https://files.pythonhosted.org/packages/67/1e/a1657ee15bd9116f70d4a530c736983eed997b361e20ecd8f5ca3759d5c5/regex-2025.11.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eb542fd347ce61e1321b0a6b945d5701528dca0cd9759c2e3bb8bd57e47964d", size = 812852, upload-time = "2025-11-03T21:33:27.852Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/6f/f7516dde5506a588a561d296b2d0044839de06035bb486b326065b4c101e/regex-2025.11.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2d5919075a1f2e413c00b056ea0c2f065b3f5fe83c3d07d325ab92dce51d6", size = 795566, upload-time = "2025-11-03T21:33:32.364Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/dd/3d10b9e170cc16fb34cb2cef91513cf3df65f440b3366030631b2984a264/regex-2025.11.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3f8bf11a4827cc7ce5a53d4ef6cddd5ad25595d3c1435ef08f76825851343154", size = 868463, upload-time = "2025-11-03T21:33:34.459Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/8e/935e6beff1695aa9085ff83195daccd72acc82c81793df480f34569330de/regex-2025.11.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:22c12d837298651e5550ac1d964e4ff57c3f56965fc1812c90c9fb2028eaf267", size = 854694, upload-time = "2025-11-03T21:33:36.793Z" },
+ { url = "https://files.pythonhosted.org/packages/92/12/10650181a040978b2f5720a6a74d44f841371a3d984c2083fc1752e4acf6/regex-2025.11.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ba394a3dda9ad41c7c780f60f6e4a70988741415ae96f6d1bf6c239cf01379", size = 799691, upload-time = "2025-11-03T21:33:39.079Z" },
+ { url = "https://files.pythonhosted.org/packages/67/90/8f37138181c9a7690e7e4cb388debbd389342db3c7381d636d2875940752/regex-2025.11.3-cp314-cp314t-win32.whl", hash = "sha256:4bf146dca15cdd53224a1bf46d628bd7590e4a07fbb69e720d561aea43a32b38", size = 274583, upload-time = "2025-11-03T21:33:41.302Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/cd/867f5ec442d56beb56f5f854f40abcfc75e11d10b11fdb1869dd39c63aaf/regex-2025.11.3-cp314-cp314t-win_amd64.whl", hash = "sha256:adad1a1bcf1c9e76346e091d22d23ac54ef28e1365117d99521631078dfec9de", size = 284286, upload-time = "2025-11-03T21:33:43.324Z" },
+ { url = "https://files.pythonhosted.org/packages/20/31/32c0c4610cbc070362bf1d2e4ea86d1ea29014d400a6d6c2486fcfd57766/regex-2025.11.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c54f768482cef41e219720013cd05933b6f971d9562544d691c68699bf2b6801", size = 274741, upload-time = "2025-11-03T21:33:45.557Z" },
+]
+
+[[package]]
+name = "requests"
+version = "2.32.5"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "charset-normalizer" },
+ { name = "idna" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
+]
+
+[[package]]
+name = "rich"
+version = "14.2.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "markdown-it-py" },
+ { name = "pygments" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
+]
+
+[[package]]
+name = "rich-toolkit"
+version = "0.17.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "rich" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/97/09/3f9b8d9daaf235195c626f21e03604c05b987404ee3bcacee0c1f67f2a8e/rich_toolkit-0.17.1.tar.gz", hash = "sha256:5af54df8d1dd9c8530e462e1bdcaed625c9b49f5a55b035aa0ba1c17bdb87c9a", size = 187925, upload-time = "2025-12-17T10:49:22.583Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/7f/7b/15e55fa8a76d0d41bf34d965af78acdaf80a315907adb30de8b63c272694/rich_toolkit-0.17.1-py3-none-any.whl", hash = "sha256:96d24bb921ecd225ffce7c526a9149e74006410c05e6d405bd74ffd54d5631ed", size = 31412, upload-time = "2025-12-17T10:49:21.793Z" },
+]
+
+[[package]]
+name = "rignore"
+version = "0.7.6"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/e5/f5/8bed2310abe4ae04b67a38374a4d311dd85220f5d8da56f47ae9361be0b0/rignore-0.7.6.tar.gz", hash = "sha256:00d3546cd793c30cb17921ce674d2c8f3a4b00501cb0e3dd0e82217dbeba2671", size = 57140, upload-time = "2025-11-05T21:41:21.968Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/86/7a/b970cd0138b0ece72eb28f086e933f9ed75b795716ad3de5ab22994b3b54/rignore-0.7.6-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f3c74a7e5ee77aea669c95fdb3933f2a6c7549893700082e759128a29cf67e45", size = 884999, upload-time = "2025-11-05T20:42:38.373Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/05/23faca29616d8966ada63fb0e13c214107811fa9a0aba2275e4c7ca63bd5/rignore-0.7.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b7202404958f5fe3474bac91f65350f0b1dde1a5e05089f2946549b7e91e79ec", size = 824824, upload-time = "2025-11-05T20:42:22.1Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/2e/05a1e61f04cf2548524224f0b5f21ca19ea58f7273a863bac10846b8ff69/rignore-0.7.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bde7c5835fa3905bfb7e329a4f1d7eccb676de63da7a3f934ddd5c06df20597", size = 899121, upload-time = "2025-11-05T20:40:48.94Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/35/71518847e10bdbf359badad8800e4681757a01f4777b3c5e03dbde8a42d8/rignore-0.7.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:626c3d4ba03af266694d25101bc1d8d16eda49c5feb86cedfec31c614fceca7d", size = 873813, upload-time = "2025-11-05T20:41:04.71Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/c8/32ae405d3e7fd4d9f9b7838f2fcca0a5005bb87fa514b83f83fd81c0df22/rignore-0.7.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a43841e651e7a05a4274b9026cc408d1912e64016ede8cd4c145dae5d0635be", size = 1168019, upload-time = "2025-11-05T20:41:20.723Z" },
+ { url = "https://files.pythonhosted.org/packages/25/98/013c955982bc5b4719bf9a5bea58be317eea28aa12bfd004025e3cd7c000/rignore-0.7.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7978c498dbf7f74d30cdb8859fe612167d8247f0acd377ae85180e34490725da", size = 942822, upload-time = "2025-11-05T20:41:36.99Z" },
+ { url = "https://files.pythonhosted.org/packages/90/fb/9a3f3156c6ed30bcd597e63690353edac1fcffe9d382ad517722b56ac195/rignore-0.7.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d22f72ab695c07d2d96d2a645208daff17084441b5d58c07378c9dd6f9c4c87", size = 959820, upload-time = "2025-11-05T20:42:06.364Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/b2/93bf609633021e9658acaff24cfb055d8cdaf7f5855d10ebb35307900dda/rignore-0.7.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5bd8e1a91ed1a789b2cbe39eeea9204a6719d4f2cf443a9544b521a285a295f", size = 985050, upload-time = "2025-11-05T20:41:51.124Z" },
+ { url = "https://files.pythonhosted.org/packages/69/bc/ec2d040469bdfd7b743df10f2201c5d285009a4263d506edbf7a06a090bb/rignore-0.7.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fc03efad5789365018e94ac4079f851a999bc154d1551c45179f7fcf45322", size = 1079164, upload-time = "2025-11-05T21:40:10.368Z" },
+ { url = "https://files.pythonhosted.org/packages/df/26/4b635f4ea5baf4baa8ba8eee06163f6af6e76dfbe72deb57da34bb24b19d/rignore-0.7.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ce2617fe28c51367fd8abfd4eeea9e61664af63c17d4ea00353d8ef56dfb95fa", size = 1139028, upload-time = "2025-11-05T21:40:27.977Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/54/a3147ebd1e477b06eb24e2c2c56d951ae5faa9045b7b36d7892fec5080d9/rignore-0.7.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7c4ad2cee85068408e7819a38243043214e2c3047e9bd4c506f8de01c302709e", size = 1119024, upload-time = "2025-11-05T21:40:45.148Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/f4/27475db769a57cff18fe7e7267b36e6cdb5b1281caa185ba544171106cba/rignore-0.7.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:02cd240bfd59ecc3907766f4839cbba20530a2e470abca09eaa82225e4d946fb", size = 1128531, upload-time = "2025-11-05T21:41:02.734Z" },
+ { url = "https://files.pythonhosted.org/packages/97/32/6e782d3b352e4349fa0e90bf75b13cb7f11d8908b36d9e2b262224b65d9a/rignore-0.7.6-cp310-cp310-win32.whl", hash = "sha256:fe2bd8fa1ff555259df54c376abc73855cb02628a474a40d51b358c3a1ddc55b", size = 646817, upload-time = "2025-11-05T21:41:47.51Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/8a/53185c69abb3bb362e8a46b8089999f820bf15655629ff8395107633c8ab/rignore-0.7.6-cp310-cp310-win_amd64.whl", hash = "sha256:d80afd6071c78baf3765ec698841071b19e41c326f994cfa69b5a1df676f5d39", size = 727001, upload-time = "2025-11-05T21:41:32.778Z" },
+ { url = "https://files.pythonhosted.org/packages/25/41/b6e2be3069ef3b7f24e35d2911bd6deb83d20ed5642ad81d5a6d1c015473/rignore-0.7.6-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:40be8226e12d6653abbebaffaea2885f80374c1c8f76fe5ca9e0cadd120a272c", size = 885285, upload-time = "2025-11-05T20:42:39.763Z" },
+ { url = "https://files.pythonhosted.org/packages/52/66/ba7f561b6062402022887706a7f2b2c2e2e2a28f1e3839202b0a2f77e36d/rignore-0.7.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:182f4e5e4064d947c756819446a7d4cdede8e756b8c81cf9e509683fe38778d7", size = 823882, upload-time = "2025-11-05T20:42:23.488Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/81/4087453df35a90b07370647b19017029324950c1b9137d54bf1f33843f17/rignore-0.7.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16b63047648a916a87be1e51bb5c009063f1b8b6f5afe4f04f875525507e63dc", size = 899362, upload-time = "2025-11-05T20:40:51.111Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/c9/390a8fdfabb76d71416be773bd9f162977bd483084f68daf19da1dec88a6/rignore-0.7.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ba5524f5178deca4d7695e936604ebc742acb8958f9395776e1fcb8133f8257a", size = 873633, upload-time = "2025-11-05T20:41:06.193Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c9/79404fcb0faa76edfbc9df0901f8ef18568d1104919ebbbad6d608c888d1/rignore-0.7.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:62020dbb89a1dd4b84ab3d60547b3b2eb2723641d5fb198463643f71eaaed57d", size = 1167633, upload-time = "2025-11-05T20:41:22.491Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/8d/b3466d32d445d158a0aceb80919085baaae495b1f540fb942f91d93b5e5b/rignore-0.7.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b34acd532769d5a6f153a52a98dcb81615c949ab11697ce26b2eb776af2e174d", size = 941434, upload-time = "2025-11-05T20:41:38.151Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/40/9cd949761a7af5bc27022a939c91ff622d29c7a0b66d0c13a863097dde2d/rignore-0.7.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c5e53b752f9de44dff7b3be3c98455ce3bf88e69d6dc0cf4f213346c5e3416c", size = 959461, upload-time = "2025-11-05T20:42:08.476Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/87/1e1a145731f73bdb7835e11f80da06f79a00d68b370d9a847de979575e6d/rignore-0.7.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25b3536d13a5d6409ce85f23936f044576eeebf7b6db1d078051b288410fc049", size = 985323, upload-time = "2025-11-05T20:41:52.735Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/31/1ecff992fc3f59c4fcdcb6c07d5f6c1e6dfb55ccda19c083aca9d86fa1c6/rignore-0.7.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e01cad2b0b92f6b1993f29fc01f23f2d78caf4bf93b11096d28e9d578eb08ce", size = 1079173, upload-time = "2025-11-05T21:40:12.007Z" },
+ { url = "https://files.pythonhosted.org/packages/17/18/162eedadb4c2282fa4c521700dbf93c9b14b8842e8354f7d72b445b8d593/rignore-0.7.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5991e46ab9b4868334c9e372ab0892b0150f3f586ff2b1e314272caeb38aaedb", size = 1139012, upload-time = "2025-11-05T21:40:29.399Z" },
+ { url = "https://files.pythonhosted.org/packages/78/96/a9ca398a8af74bb143ad66c2a31303c894111977e28b0d0eab03867f1b43/rignore-0.7.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6c8ae562e5d1246cba5eaeb92a47b2a279e7637102828dde41dcbe291f529a3e", size = 1118827, upload-time = "2025-11-05T21:40:46.6Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/22/1c1a65047df864def9a047dbb40bc0b580b8289a4280e62779cd61ae21f2/rignore-0.7.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aaf938530dcc0b47c4cfa52807aa2e5bfd5ca6d57a621125fe293098692f6345", size = 1128182, upload-time = "2025-11-05T21:41:04.239Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/f4/1526eb01fdc2235aca1fd9d0189bee4021d009a8dcb0161540238c24166e/rignore-0.7.6-cp311-cp311-win32.whl", hash = "sha256:166ebce373105dd485ec213a6a2695986346e60c94ff3d84eb532a237b24a4d5", size = 646547, upload-time = "2025-11-05T21:41:49.439Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/c8/dda0983e1845706beb5826459781549a840fe5a7eb934abc523e8cd17814/rignore-0.7.6-cp311-cp311-win_amd64.whl", hash = "sha256:44f35ee844b1a8cea50d056e6a595190ce9d42d3cccf9f19d280ae5f3058973a", size = 727139, upload-time = "2025-11-05T21:41:34.367Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/47/eb1206b7bf65970d41190b879e1723fc6bbdb2d45e53565f28991a8d9d96/rignore-0.7.6-cp311-cp311-win_arm64.whl", hash = "sha256:14b58f3da4fa3d5c3fa865cab49821675371f5e979281c683e131ae29159a581", size = 657598, upload-time = "2025-11-05T21:41:23.758Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/0e/012556ef3047a2628842b44e753bb15f4dc46806780ff090f1e8fe4bf1eb/rignore-0.7.6-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:03e82348cb7234f8d9b2834f854400ddbbd04c0f8f35495119e66adbd37827a8", size = 883488, upload-time = "2025-11-05T20:42:41.359Z" },
+ { url = "https://files.pythonhosted.org/packages/93/b0/d4f1f3fe9eb3f8e382d45ce5b0547ea01c4b7e0b4b4eb87bcd66a1d2b888/rignore-0.7.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9e624f6be6116ea682e76c5feb71ea91255c67c86cb75befe774365b2931961", size = 820411, upload-time = "2025-11-05T20:42:24.782Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/c8/dea564b36dedac8de21c18e1851789545bc52a0c22ece9843444d5608a6a/rignore-0.7.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bda49950d405aa8d0ebe26af807c4e662dd281d926530f03f29690a2e07d649a", size = 897821, upload-time = "2025-11-05T20:40:52.613Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/2b/ee96db17ac1835e024c5d0742eefb7e46de60020385ac883dd3d1cde2c1f/rignore-0.7.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5fd5ab3840b8c16851d327ed06e9b8be6459702a53e5ab1fc4073b684b3789e", size = 873963, upload-time = "2025-11-05T20:41:07.49Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/8c/ad5a57bbb9d14d5c7e5960f712a8a0b902472ea3f4a2138cbf70d1777b75/rignore-0.7.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ced2a248352636a5c77504cb755dc02c2eef9a820a44d3f33061ce1bb8a7f2d2", size = 1169216, upload-time = "2025-11-05T20:41:23.73Z" },
+ { url = "https://files.pythonhosted.org/packages/80/e6/5b00bc2a6bc1701e6878fca798cf5d9125eb3113193e33078b6fc0d99123/rignore-0.7.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a04a3b73b75ddc12c9c9b21efcdaab33ca3832941d6f1d67bffd860941cd448a", size = 942942, upload-time = "2025-11-05T20:41:39.393Z" },
+ { url = "https://files.pythonhosted.org/packages/85/e5/7f99bd0cc9818a91d0e8b9acc65b792e35750e3bdccd15a7ee75e64efca4/rignore-0.7.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24321efac92140b7ec910ac7c53ab0f0c86a41133d2bb4b0e6a7c94967f44dd", size = 959787, upload-time = "2025-11-05T20:42:09.765Z" },
+ { url = "https://files.pythonhosted.org/packages/55/54/2ffea79a7c1eabcede1926347ebc2a81bc6b81f447d05b52af9af14948b9/rignore-0.7.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c7aa109d41e593785c55fdaa89ad80b10330affa9f9d3e3a51fa695f739b20", size = 984245, upload-time = "2025-11-05T20:41:54.062Z" },
+ { url = "https://files.pythonhosted.org/packages/41/f7/e80f55dfe0f35787fa482aa18689b9c8251e045076c35477deb0007b3277/rignore-0.7.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1734dc49d1e9501b07852ef44421f84d9f378da9fbeda729e77db71f49cac28b", size = 1078647, upload-time = "2025-11-05T21:40:13.463Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/cf/2c64f0b6725149f7c6e7e5a909d14354889b4beaadddaa5fff023ec71084/rignore-0.7.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5719ea14ea2b652c0c0894be5dfde954e1853a80dea27dd2fbaa749618d837f5", size = 1139186, upload-time = "2025-11-05T21:40:31.27Z" },
+ { url = "https://files.pythonhosted.org/packages/75/95/a86c84909ccc24af0d094b50d54697951e576c252a4d9f21b47b52af9598/rignore-0.7.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e23424fc7ce35726854f639cb7968151a792c0c3d9d082f7f67e0c362cfecca", size = 1117604, upload-time = "2025-11-05T21:40:48.07Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/5e/13b249613fd5d18d58662490ab910a9f0be758981d1797789913adb4e918/rignore-0.7.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3efdcf1dd84d45f3e2bd2f93303d9be103888f56dfa7c3349b5bf4f0657ec696", size = 1127725, upload-time = "2025-11-05T21:41:05.804Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/28/fa5dcd1e2e16982c359128664e3785f202d3eca9b22dd0b2f91c4b3d242f/rignore-0.7.6-cp312-cp312-win32.whl", hash = "sha256:ccca9d1a8b5234c76b71546fc3c134533b013f40495f394a65614a81f7387046", size = 646145, upload-time = "2025-11-05T21:41:51.096Z" },
+ { url = "https://files.pythonhosted.org/packages/26/87/69387fb5dd81a0f771936381431780b8cf66fcd2cfe9495e1aaf41548931/rignore-0.7.6-cp312-cp312-win_amd64.whl", hash = "sha256:c96a285e4a8bfec0652e0bfcf42b1aabcdda1e7625f5006d188e3b1c87fdb543", size = 726090, upload-time = "2025-11-05T21:41:36.485Z" },
+ { url = "https://files.pythonhosted.org/packages/24/5f/e8418108dcda8087fb198a6f81caadbcda9fd115d61154bf0df4d6d3619b/rignore-0.7.6-cp312-cp312-win_arm64.whl", hash = "sha256:a64a750e7a8277a323f01ca50b7784a764845f6cce2fe38831cb93f0508d0051", size = 656317, upload-time = "2025-11-05T21:41:25.305Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/8a/a4078f6e14932ac7edb171149c481de29969d96ddee3ece5dc4c26f9e0c3/rignore-0.7.6-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2bdab1d31ec9b4fb1331980ee49ea051c0d7f7bb6baa28b3125ef03cdc48fdaf", size = 883057, upload-time = "2025-11-05T20:42:42.741Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/8f/f8daacd177db4bf7c2223bab41e630c52711f8af9ed279be2058d2fe4982/rignore-0.7.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:90f0a00ce0c866c275bf888271f1dc0d2140f29b82fcf33cdbda1e1a6af01010", size = 820150, upload-time = "2025-11-05T20:42:26.545Z" },
+ { url = "https://files.pythonhosted.org/packages/36/31/b65b837e39c3f7064c426754714ac633b66b8c2290978af9d7f513e14aa9/rignore-0.7.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1ad295537041dc2ed4b540fb1a3906bd9ede6ccdad3fe79770cd89e04e3c73c", size = 897406, upload-time = "2025-11-05T20:40:53.854Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/58/1970ce006c427e202ac7c081435719a076c478f07b3a23f469227788dc23/rignore-0.7.6-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f782dbd3a65a5ac85adfff69e5c6b101285ef3f845c3a3cae56a54bebf9fe116", size = 874050, upload-time = "2025-11-05T20:41:08.922Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/00/eb45db9f90137329072a732273be0d383cb7d7f50ddc8e0bceea34c1dfdf/rignore-0.7.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65cece3b36e5b0826d946494734c0e6aaf5a0337e18ff55b071438efe13d559e", size = 1167835, upload-time = "2025-11-05T20:41:24.997Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/f1/6f1d72ddca41a64eed569680587a1236633587cc9f78136477ae69e2c88a/rignore-0.7.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7e4bb66c13cd7602dc8931822c02dfbbd5252015c750ac5d6152b186f0a8be0", size = 941945, upload-time = "2025-11-05T20:41:40.628Z" },
+ { url = "https://files.pythonhosted.org/packages/48/6f/2f178af1c1a276a065f563ec1e11e7a9e23d4996fd0465516afce4b5c636/rignore-0.7.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297e500c15766e196f68aaaa70e8b6db85fa23fdc075b880d8231fdfba738cd7", size = 959067, upload-time = "2025-11-05T20:42:11.09Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/db/423a81c4c1e173877c7f9b5767dcaf1ab50484a94f60a0b2ed78be3fa765/rignore-0.7.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a07084211a8d35e1a5b1d32b9661a5ed20669970b369df0cf77da3adea3405de", size = 984438, upload-time = "2025-11-05T20:41:55.443Z" },
+ { url = "https://files.pythonhosted.org/packages/31/eb/c4f92cc3f2825d501d3c46a244a671eb737fc1bcf7b05a3ecd34abb3e0d7/rignore-0.7.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:181eb2a975a22256a1441a9d2f15eb1292839ea3f05606620bd9e1938302cf79", size = 1078365, upload-time = "2025-11-05T21:40:15.148Z" },
+ { url = "https://files.pythonhosted.org/packages/26/09/99442f02794bd7441bfc8ed1c7319e890449b816a7493b2db0e30af39095/rignore-0.7.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:7bbcdc52b5bf9f054b34ce4af5269df5d863d9c2456243338bc193c28022bd7b", size = 1139066, upload-time = "2025-11-05T21:40:32.771Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/88/bcfc21e520bba975410e9419450f4b90a2ac8236b9a80fd8130e87d098af/rignore-0.7.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f2e027a6da21a7c8c0d87553c24ca5cc4364def18d146057862c23a96546238e", size = 1118036, upload-time = "2025-11-05T21:40:49.646Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/25/d37215e4562cda5c13312636393aea0bafe38d54d4e0517520a4cc0753ec/rignore-0.7.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee4a18b82cbbc648e4aac1510066682fe62beb5dc88e2c67c53a83954e541360", size = 1127550, upload-time = "2025-11-05T21:41:07.648Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/76/a264ab38bfa1620ec12a8ff1c07778da89e16d8c0f3450b0333020d3d6dc/rignore-0.7.6-cp313-cp313-win32.whl", hash = "sha256:a7d7148b6e5e95035d4390396895adc384d37ff4e06781a36fe573bba7c283e5", size = 646097, upload-time = "2025-11-05T21:41:53.201Z" },
+ { url = "https://files.pythonhosted.org/packages/62/44/3c31b8983c29ea8832b6082ddb1d07b90379c2d993bd20fce4487b71b4f4/rignore-0.7.6-cp313-cp313-win_amd64.whl", hash = "sha256:b037c4b15a64dced08fc12310ee844ec2284c4c5c1ca77bc37d0a04f7bff386e", size = 726170, upload-time = "2025-11-05T21:41:38.131Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/41/e26a075cab83debe41a42661262f606166157df84e0e02e2d904d134c0d8/rignore-0.7.6-cp313-cp313-win_arm64.whl", hash = "sha256:e47443de9b12fe569889bdbe020abe0e0b667516ee2ab435443f6d0869bd2804", size = 656184, upload-time = "2025-11-05T21:41:27.396Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/b9/1f5bd82b87e5550cd843ceb3768b4a8ef274eb63f29333cf2f29644b3d75/rignore-0.7.6-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:8e41be9fa8f2f47239ded8920cc283699a052ac4c371f77f5ac017ebeed75732", size = 882632, upload-time = "2025-11-05T20:42:44.063Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/6b/07714a3efe4a8048864e8a5b7db311ba51b921e15268b17defaebf56d3db/rignore-0.7.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6dc1e171e52cefa6c20e60c05394a71165663b48bca6c7666dee4f778f2a7d90", size = 820760, upload-time = "2025-11-05T20:42:27.885Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/0f/348c829ea2d8d596e856371b14b9092f8a5dfbb62674ec9b3f67e4939a9d/rignore-0.7.6-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ce2268837c3600f82ab8db58f5834009dc638ee17103582960da668963bebc5", size = 899044, upload-time = "2025-11-05T20:40:55.336Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/30/2e1841a19b4dd23878d73edd5d82e998a83d5ed9570a89675f140ca8b2ad/rignore-0.7.6-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:690a3e1b54bfe77e89c4bacb13f046e642f8baadafc61d68f5a726f324a76ab6", size = 874144, upload-time = "2025-11-05T20:41:10.195Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/bf/0ce9beb2e5f64c30e3580bef09f5829236889f01511a125f98b83169b993/rignore-0.7.6-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09d12ac7a0b6210c07bcd145007117ebd8abe99c8eeb383e9e4673910c2754b2", size = 1168062, upload-time = "2025-11-05T20:41:26.511Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/8b/571c178414eb4014969865317da8a02ce4cf5241a41676ef91a59aab24de/rignore-0.7.6-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a2b2b74a8c60203b08452479b90e5ce3dbe96a916214bc9eb2e5af0b6a9beb0", size = 942542, upload-time = "2025-11-05T20:41:41.838Z" },
+ { url = "https://files.pythonhosted.org/packages/19/62/7a3cf601d5a45137a7e2b89d10c05b5b86499190c4b7ca5c3c47d79ee519/rignore-0.7.6-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc5a531ef02131e44359419a366bfac57f773ea58f5278c2cdd915f7d10ea94", size = 958739, upload-time = "2025-11-05T20:42:12.463Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/1f/4261f6a0d7caf2058a5cde2f5045f565ab91aa7badc972b57d19ce58b14e/rignore-0.7.6-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7a1f77d9c4cd7e76229e252614d963442686bfe12c787a49f4fe481df49e7a9", size = 984138, upload-time = "2025-11-05T20:41:56.775Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/bf/628dfe19c75e8ce1f45f7c248f5148b17dfa89a817f8e3552ab74c3ae812/rignore-0.7.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ead81f728682ba72b5b1c3d5846b011d3e0174da978de87c61645f2ed36659a7", size = 1079299, upload-time = "2025-11-05T21:40:16.639Z" },
+ { url = "https://files.pythonhosted.org/packages/af/a5/be29c50f5c0c25c637ed32db8758fdf5b901a99e08b608971cda8afb293b/rignore-0.7.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:12ffd50f520c22ffdabed8cd8bfb567d9ac165b2b854d3e679f4bcaef11a9441", size = 1139618, upload-time = "2025-11-05T21:40:34.507Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/40/3c46cd7ce4fa05c20b525fd60f599165e820af66e66f2c371cd50644558f/rignore-0.7.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:e5a16890fbe3c894f8ca34b0fcacc2c200398d4d46ae654e03bc9b3dbf2a0a72", size = 1117626, upload-time = "2025-11-05T21:40:51.494Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/b9/aea926f263b8a29a23c75c2e0d8447965eb1879d3feb53cfcf84db67ed58/rignore-0.7.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3abab3bf99e8a77488ef6c7c9a799fac22224c28fe9f25cc21aa7cc2b72bfc0b", size = 1128144, upload-time = "2025-11-05T21:41:09.169Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/f6/0d6242f8d0df7f2ecbe91679fefc1f75e7cd2072cb4f497abaab3f0f8523/rignore-0.7.6-cp314-cp314-win32.whl", hash = "sha256:eeef421c1782953c4375aa32f06ecae470c1285c6381eee2a30d2e02a5633001", size = 646385, upload-time = "2025-11-05T21:41:55.105Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/38/c0dcd7b10064f084343d6af26fe9414e46e9619c5f3224b5272e8e5d9956/rignore-0.7.6-cp314-cp314-win_amd64.whl", hash = "sha256:6aeed503b3b3d5af939b21d72a82521701a4bd3b89cd761da1e7dc78621af304", size = 725738, upload-time = "2025-11-05T21:41:39.736Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/7a/290f868296c1ece914d565757ab363b04730a728b544beb567ceb3b2d96f/rignore-0.7.6-cp314-cp314-win_arm64.whl", hash = "sha256:104f215b60b3c984c386c3e747d6ab4376d5656478694e22c7bd2f788ddd8304", size = 656008, upload-time = "2025-11-05T21:41:29.028Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/d2/3c74e3cd81fe8ea08a8dcd2d755c09ac2e8ad8fe409508904557b58383d3/rignore-0.7.6-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bb24a5b947656dd94cb9e41c4bc8b23cec0c435b58be0d74a874f63c259549e8", size = 882835, upload-time = "2025-11-05T20:42:45.443Z" },
+ { url = "https://files.pythonhosted.org/packages/77/61/a772a34b6b63154877433ac2d048364815b24c2dd308f76b212c408101a2/rignore-0.7.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5b1e33c9501cefe24b70a1eafd9821acfd0ebf0b35c3a379430a14df089993e3", size = 820301, upload-time = "2025-11-05T20:42:29.226Z" },
+ { url = "https://files.pythonhosted.org/packages/71/30/054880b09c0b1b61d17eeb15279d8bf729c0ba52b36c3ada52fb827cbb3c/rignore-0.7.6-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bec3994665a44454df86deb762061e05cd4b61e3772f5b07d1882a8a0d2748d5", size = 897611, upload-time = "2025-11-05T20:40:56.475Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/40/b2d1c169f833d69931bf232600eaa3c7998ba4f9a402e43a822dad2ea9f2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26cba2edfe3cff1dfa72bddf65d316ddebf182f011f2f61538705d6dbaf54986", size = 873875, upload-time = "2025-11-05T20:41:11.561Z" },
+ { url = "https://files.pythonhosted.org/packages/55/59/ca5ae93d83a1a60e44b21d87deb48b177a8db1b85e82fc8a9abb24a8986d/rignore-0.7.6-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffa86694fec604c613696cb91e43892aa22e1fec5f9870e48f111c603e5ec4e9", size = 1167245, upload-time = "2025-11-05T20:41:28.29Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/52/cf3dce392ba2af806cba265aad6bcd9c48bb2a6cb5eee448d3319f6e505b/rignore-0.7.6-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48efe2ed95aa8104145004afb15cdfa02bea5cdde8b0344afeb0434f0d989aa2", size = 941750, upload-time = "2025-11-05T20:41:43.111Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/be/3f344c6218d779395e785091d05396dfd8b625f6aafbe502746fcd880af2/rignore-0.7.6-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dcae43eb44b7f2457fef7cc87f103f9a0013017a6f4e62182c565e924948f21", size = 958896, upload-time = "2025-11-05T20:42:13.784Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/34/d3fa71938aed7d00dcad87f0f9bcb02ad66c85d6ffc83ba31078ce53646a/rignore-0.7.6-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2cd649a7091c0dad2f11ef65630d30c698d505cbe8660dd395268e7c099cc99f", size = 983992, upload-time = "2025-11-05T20:41:58.022Z" },
+ { url = "https://files.pythonhosted.org/packages/24/a4/52a697158e9920705bdbd0748d59fa63e0f3233fb92e9df9a71afbead6ca/rignore-0.7.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42de84b0289d478d30ceb7ae59023f7b0527786a9a5b490830e080f0e4ea5aeb", size = 1078181, upload-time = "2025-11-05T21:40:18.151Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/65/aa76dbcdabf3787a6f0fd61b5cc8ed1e88580590556d6c0207960d2384bb/rignore-0.7.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:875a617e57b53b4acbc5a91de418233849711c02e29cc1f4f9febb2f928af013", size = 1139232, upload-time = "2025-11-05T21:40:35.966Z" },
+ { url = "https://files.pythonhosted.org/packages/08/44/31b31a49b3233c6842acc1c0731aa1e7fb322a7170612acf30327f700b44/rignore-0.7.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8703998902771e96e49968105207719f22926e4431b108450f3f430b4e268b7c", size = 1117349, upload-time = "2025-11-05T21:40:53.013Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/ae/1b199a2302c19c658cf74e5ee1427605234e8c91787cfba0015f2ace145b/rignore-0.7.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:602ef33f3e1b04c1e9a10a3c03f8bc3cef2d2383dcc250d309be42b49923cabc", size = 1127702, upload-time = "2025-11-05T21:41:10.881Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/d3/18210222b37e87e36357f7b300b7d98c6dd62b133771e71ae27acba83a4f/rignore-0.7.6-cp314-cp314t-win32.whl", hash = "sha256:c1d8f117f7da0a4a96a8daef3da75bc090e3792d30b8b12cfadc240c631353f9", size = 647033, upload-time = "2025-11-05T21:42:00.095Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/87/033eebfbee3ec7d92b3bb1717d8f68c88e6fc7de54537040f3b3a405726f/rignore-0.7.6-cp314-cp314t-win_amd64.whl", hash = "sha256:ca36e59408bec81de75d307c568c2d0d410fb880b1769be43611472c61e85c96", size = 725647, upload-time = "2025-11-05T21:41:44.449Z" },
+ { url = "https://files.pythonhosted.org/packages/79/62/b88e5879512c55b8ee979c666ee6902adc4ed05007226de266410ae27965/rignore-0.7.6-cp314-cp314t-win_arm64.whl", hash = "sha256:b83adabeb3e8cf662cabe1931b83e165b88c526fa6af6b3aa90429686e474896", size = 656035, upload-time = "2025-11-05T21:41:31.13Z" },
+ { url = "https://files.pythonhosted.org/packages/85/12/62d690b4644c330d7ac0f739b7f078190ab4308faa909a60842d0e4af5b2/rignore-0.7.6-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c3d3a523af1cd4ed2c0cba8d277a32d329b0c96ef9901fb7ca45c8cfaccf31a5", size = 887462, upload-time = "2025-11-05T20:42:50.804Z" },
+ { url = "https://files.pythonhosted.org/packages/05/bc/6528a0e97ed2bd7a7c329183367d1ffbc5b9762ae8348d88dae72cc9d1f5/rignore-0.7.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:990853566e65184a506e1e2af2d15045afad3ebaebb8859cb85b882081915110", size = 826918, upload-time = "2025-11-05T20:42:33.689Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/2c/7d7bad116e09a04e9e1688c6f891fa2d4fd33f11b69ac0bd92419ddebeae/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cab9ff2e436ce7240d7ee301c8ef806ed77c1fd6b8a8239ff65f9bbbcb5b8a3", size = 900922, upload-time = "2025-11-05T20:41:00.361Z" },
+ { url = "https://files.pythonhosted.org/packages/09/ba/e5ea89fbde8e37a90ce456e31c5e9d85512cef5ae38e0f4d2426eb776a19/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1a6671b2082c13bfd9a5cf4ce64670f832a6d41470556112c4ab0b6519b2fc4", size = 876987, upload-time = "2025-11-05T20:41:16.219Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/fb/93d14193f0ec0c3d35b763f0a000e9780f63b2031f3d3756442c2152622d/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2468729b4c5295c199d084ab88a40afcb7c8b974276805105239c07855bbacee", size = 1171110, upload-time = "2025-11-05T20:41:32.631Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/46/08436312ff96ffa29cfa4e1a987efc37e094531db46ba5e9fda9bb792afd/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:775710777fd71e5fdf54df69cdc249996a1d6f447a2b5bfb86dbf033fddd9cf9", size = 943339, upload-time = "2025-11-05T20:41:47.128Z" },
+ { url = "https://files.pythonhosted.org/packages/34/28/3b3c51328f505cfaf7e53f408f78a1e955d561135d02f9cb0341ea99f69a/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4565407f4a77f72cf9d91469e75d15d375f755f0a01236bb8aaa176278cc7085", size = 961680, upload-time = "2025-11-05T20:42:18.061Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/9e/cbff75c8676d4f4a90bd58a1581249d255c7305141b0868f0abc0324836b/rignore-0.7.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc44c33f8fb2d5c9da748de7a6e6653a78aa740655e7409895e94a247ffa97c8", size = 987045, upload-time = "2025-11-05T20:42:02.315Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/25/d802d1d369502a7ddb8816059e7c79d2d913e17df975b863418e0aca4d8a/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8f32478f05540513c11923e8838afab9efef0131d66dca7f67f0e1bbd118af6a", size = 1080310, upload-time = "2025-11-05T21:40:23.184Z" },
+ { url = "https://files.pythonhosted.org/packages/43/f0/250b785c2e473b1ab763eaf2be820934c2a5409a722e94b279dddac21c7d/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:1b63a3dd76225ea35b01dd6596aa90b275b5d0f71d6dc28fce6dd295d98614aa", size = 1140998, upload-time = "2025-11-05T21:40:40.603Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/d6/bb42fd2a8bba6aea327962656e20621fd495523259db40cfb4c5f760f05c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:fe6c41175c36554a4ef0994cd1b4dbd6d73156fca779066456b781707402048e", size = 1121178, upload-time = "2025-11-05T21:40:57.585Z" },
+ { url = "https://files.pythonhosted.org/packages/97/f4/aeb548374129dce3dc191a4bb598c944d9ed663f467b9af830315d86059c/rignore-0.7.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a0c6792406ae36f4e7664dc772da909451d46432ff8485774526232d4885063", size = 1130190, upload-time = "2025-11-05T21:41:16.403Z" },
+ { url = "https://files.pythonhosted.org/packages/82/78/a6250ff0c49a3cdb943910ada4116e708118e9b901c878cfae616c80a904/rignore-0.7.6-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a20b6fb61bcced9a83dfcca6599ad45182b06ba720cff7c8d891e5b78db5b65f", size = 886470, upload-time = "2025-11-05T20:42:52.314Z" },
+ { url = "https://files.pythonhosted.org/packages/35/af/c69c0c51b8f9f7914d95c4ea91c29a2ac067572048cae95dd6d2efdbe05d/rignore-0.7.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:392dcabfecbe176c9ebbcb40d85a5e86a5989559c4f988c2741da7daf1b5be25", size = 825976, upload-time = "2025-11-05T20:42:35.118Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/d2/1b264f56132264ea609d3213ab603d6a27016b19559a1a1ede1a66a03dcd/rignore-0.7.6-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22baa462abdc36fdd5a5e2dae423107723351b85ff093762f9261148b9d0a04a", size = 899739, upload-time = "2025-11-05T20:41:01.518Z" },
+ { url = "https://files.pythonhosted.org/packages/55/e4/b3c5dfdd8d8a10741dfe7199ef45d19a0e42d0c13aa377c83bd6caf65d90/rignore-0.7.6-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53fb28882d2538cb2d231972146c4927a9d9455e62b209f85d634408c4103538", size = 874843, upload-time = "2025-11-05T20:41:17.687Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/10/d6f3750233881a2a154cefc9a6a0a9b19da526b19f7f08221b552c6f827d/rignore-0.7.6-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87409f7eeb1103d6b77f3472a3a0d9a5953e3ae804a55080bdcb0120ee43995b", size = 1170348, upload-time = "2025-11-05T20:41:34.21Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/10/ad98ca05c9771c15af734cee18114a3c280914b6e34fde9ffea2e61e88aa/rignore-0.7.6-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:684014e42e4341ab3ea23a203551857fcc03a7f8ae96ca3aefb824663f55db32", size = 942315, upload-time = "2025-11-05T20:41:48.508Z" },
+ { url = "https://files.pythonhosted.org/packages/de/00/ab5c0f872acb60d534e687e629c17e0896c62da9b389c66d3aa16b817aa8/rignore-0.7.6-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77356ebb01ba13f8a425c3d30fcad40e57719c0e37670d022d560884a30e4767", size = 961047, upload-time = "2025-11-05T20:42:19.403Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/86/3030fdc363a8f0d1cd155b4c453d6db9bab47a24fcc64d03f61d9d78fe6a/rignore-0.7.6-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6cbd8a48abbd3747a6c830393cd578782fab5d43f4deea48c5f5e344b8fed2b0", size = 986090, upload-time = "2025-11-05T20:42:03.581Z" },
+ { url = "https://files.pythonhosted.org/packages/33/b8/133aa4002cee0ebbb39362f94e4898eec7fbd09cec9fcbce1cd65b355b7f/rignore-0.7.6-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2673225dcec7f90497e79438c35e34638d0d0391ccea3cbb79bfb9adc0dc5bd7", size = 1079656, upload-time = "2025-11-05T21:40:24.89Z" },
+ { url = "https://files.pythonhosted.org/packages/67/56/36d5d34210e5e7dfcd134eed8335b19e80ae940ee758f493e4f2b344dd70/rignore-0.7.6-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:c081f17290d8a2b96052b79207622aa635686ea39d502b976836384ede3d303c", size = 1139789, upload-time = "2025-11-05T21:40:42.119Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/5b/bb4f9420802bf73678033a4a55ab1bede36ce2e9b41fec5f966d83d932b3/rignore-0.7.6-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:57e8327aacc27f921968cb2a174f9e47b084ce9a7dd0122c8132d22358f6bd79", size = 1120308, upload-time = "2025-11-05T21:40:59.402Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/8b/a1299085b28a2f6135e30370b126e3c5055b61908622f2488ade67641479/rignore-0.7.6-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:d8955b57e42f2a5434670d5aa7b75eaf6e74602ccd8955dddf7045379cd762fb", size = 1129444, upload-time = "2025-11-05T21:41:17.906Z" },
+]
+
+[[package]]
+name = "rlp"
+version = "4.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "eth-utils" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1b/2d/439b0728a92964a04d9c88ea1ca9ebb128893fbbd5834faa31f987f2fd4c/rlp-4.1.0.tar.gz", hash = "sha256:be07564270a96f3e225e2c107db263de96b5bc1f27722d2855bd3459a08e95a9", size = 33429, upload-time = "2025-02-04T22:05:59.089Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/99/fb/e4c0ced9893b84ac95b7181d69a9786ce5879aeb3bbbcbba80a164f85d6a/rlp-4.1.0-py3-none-any.whl", hash = "sha256:8eca394c579bad34ee0b937aecb96a57052ff3716e19c7a578883e767bc5da6f", size = 19973, upload-time = "2025-02-04T22:05:57.05Z" },
+]
+
+[[package]]
+name = "ruff"
+version = "0.14.10"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/57/08/52232a877978dd8f9cf2aeddce3e611b40a63287dfca29b6b8da791f5e8d/ruff-0.14.10.tar.gz", hash = "sha256:9a2e830f075d1a42cd28420d7809ace390832a490ed0966fe373ba288e77aaf4", size = 5859763, upload-time = "2025-12-18T19:28:57.98Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/60/01/933704d69f3f05ee16ef11406b78881733c186fe14b6a46b05cfcaf6d3b2/ruff-0.14.10-py3-none-linux_armv6l.whl", hash = "sha256:7a3ce585f2ade3e1f29ec1b92df13e3da262178df8c8bdf876f48fa0e8316c49", size = 13527080, upload-time = "2025-12-18T19:29:25.642Z" },
+ { url = "https://files.pythonhosted.org/packages/df/58/a0349197a7dfa603ffb7f5b0470391efa79ddc327c1e29c4851e85b09cc5/ruff-0.14.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:674f9be9372907f7257c51f1d4fc902cb7cf014b9980152b802794317941f08f", size = 13797320, upload-time = "2025-12-18T19:29:02.571Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/82/36be59f00a6082e38c23536df4e71cdbc6af8d7c707eade97fcad5c98235/ruff-0.14.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d85713d522348837ef9df8efca33ccb8bd6fcfc86a2cde3ccb4bc9d28a18003d", size = 12918434, upload-time = "2025-12-18T19:28:51.202Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/00/45c62a7f7e34da92a25804f813ebe05c88aa9e0c25e5cb5a7d23dd7450e3/ruff-0.14.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6987ebe0501ae4f4308d7d24e2d0fe3d7a98430f5adfd0f1fead050a740a3a77", size = 13371961, upload-time = "2025-12-18T19:29:04.991Z" },
+ { url = "https://files.pythonhosted.org/packages/40/31/a5906d60f0405f7e57045a70f2d57084a93ca7425f22e1d66904769d1628/ruff-0.14.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16a01dfb7b9e4eee556fbfd5392806b1b8550c9b4a9f6acd3dbe6812b193c70a", size = 13275629, upload-time = "2025-12-18T19:29:21.381Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/60/61c0087df21894cf9d928dc04bcd4fb10e8b2e8dca7b1a276ba2155b2002/ruff-0.14.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7165d31a925b7a294465fa81be8c12a0e9b60fb02bf177e79067c867e71f8b1f", size = 14029234, upload-time = "2025-12-18T19:29:00.132Z" },
+ { url = "https://files.pythonhosted.org/packages/44/84/77d911bee3b92348b6e5dab5a0c898d87084ea03ac5dc708f46d88407def/ruff-0.14.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c561695675b972effb0c0a45db233f2c816ff3da8dcfbe7dfc7eed625f218935", size = 15449890, upload-time = "2025-12-18T19:28:53.573Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/36/480206eaefa24a7ec321582dda580443a8f0671fdbf6b1c80e9c3e93a16a/ruff-0.14.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4bb98fcbbc61725968893682fd4df8966a34611239c9fd07a1f6a07e7103d08e", size = 15123172, upload-time = "2025-12-18T19:29:23.453Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/38/68e414156015ba80cef5473d57919d27dfb62ec804b96180bafdeaf0e090/ruff-0.14.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f24b47993a9d8cb858429e97bdf8544c78029f09b520af615c1d261bf827001d", size = 14460260, upload-time = "2025-12-18T19:29:27.808Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/19/9e050c0dca8aba824d67cc0db69fb459c28d8cd3f6855b1405b3f29cc91d/ruff-0.14.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59aabd2e2c4fd614d2862e7939c34a532c04f1084476d6833dddef4afab87e9f", size = 14229978, upload-time = "2025-12-18T19:29:11.32Z" },
+ { url = "https://files.pythonhosted.org/packages/51/eb/e8dd1dd6e05b9e695aa9dd420f4577debdd0f87a5ff2fedda33c09e9be8c/ruff-0.14.10-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:213db2b2e44be8625002dbea33bb9c60c66ea2c07c084a00d55732689d697a7f", size = 14338036, upload-time = "2025-12-18T19:29:09.184Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/12/f3e3a505db7c19303b70af370d137795fcfec136d670d5de5391e295c134/ruff-0.14.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b914c40ab64865a17a9a5b67911d14df72346a634527240039eb3bd650e5979d", size = 13264051, upload-time = "2025-12-18T19:29:13.431Z" },
+ { url = "https://files.pythonhosted.org/packages/08/64/8c3a47eaccfef8ac20e0484e68e0772013eb85802f8a9f7603ca751eb166/ruff-0.14.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1484983559f026788e3a5c07c81ef7d1e97c1c78ed03041a18f75df104c45405", size = 13283998, upload-time = "2025-12-18T19:29:06.994Z" },
+ { url = "https://files.pythonhosted.org/packages/12/84/534a5506f4074e5cc0529e5cd96cfc01bb480e460c7edf5af70d2bcae55e/ruff-0.14.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c70427132db492d25f982fffc8d6c7535cc2fd2c83fc8888f05caaa248521e60", size = 13601891, upload-time = "2025-12-18T19:28:55.811Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/1e/14c916087d8598917dbad9b2921d340f7884824ad6e9c55de948a93b106d/ruff-0.14.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5bcf45b681e9f1ee6445d317ce1fa9d6cba9a6049542d1c3d5b5958986be8830", size = 14336660, upload-time = "2025-12-18T19:29:16.531Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/1c/d7b67ab43f30013b47c12b42d1acd354c195351a3f7a1d67f59e54227ede/ruff-0.14.10-py3-none-win32.whl", hash = "sha256:104c49fc7ab73f3f3a758039adea978869a918f31b73280db175b43a2d9b51d6", size = 13196187, upload-time = "2025-12-18T19:29:19.006Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/9c/896c862e13886fae2af961bef3e6312db9ebc6adc2b156fe95e615dee8c1/ruff-0.14.10-py3-none-win_amd64.whl", hash = "sha256:466297bd73638c6bdf06485683e812db1c00c7ac96d4ddd0294a338c62fdc154", size = 14661283, upload-time = "2025-12-18T19:29:30.16Z" },
+ { url = "https://files.pythonhosted.org/packages/74/31/b0e29d572670dca3674eeee78e418f20bdf97fa8aa9ea71380885e175ca0/ruff-0.14.10-py3-none-win_arm64.whl", hash = "sha256:e51d046cf6dda98a4633b8a8a771451107413b0f07183b2bef03f075599e44e6", size = 13729839, upload-time = "2025-12-18T19:28:48.636Z" },
+]
+
+[[package]]
+name = "sentry-sdk"
+version = "2.48.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/40/f0/0e9dc590513d5e742d7799e2038df3a05167cba084c6ca4f3cdd75b55164/sentry_sdk-2.48.0.tar.gz", hash = "sha256:5213190977ff7fdff8a58b722fb807f8d5524a80488626ebeda1b5676c0c1473", size = 384828, upload-time = "2025-12-16T14:55:41.722Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4d/19/8d77f9992e5cbfcaa9133c3bf63b4fbbb051248802e1e803fed5c552fbb2/sentry_sdk-2.48.0-py2.py3-none-any.whl", hash = "sha256:6b12ac256769d41825d9b7518444e57fa35b5642df4c7c5e322af4d2c8721172", size = 414555, upload-time = "2025-12-16T14:55:40.152Z" },
+]
+
+[[package]]
+name = "shellingham"
+version = "1.5.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
+]
+
+[[package]]
+name = "solana"
+version = "0.36.11"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "construct-typing" },
+ { name = "httpx" },
+ { name = "solders" },
+ { name = "typing-extensions" },
+ { name = "websockets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/5e/66/b8cd6e4d95bfe46798942ace31935e7799005a4e2180869dc7bac6b75be9/solana-0.36.11.tar.gz", hash = "sha256:2fdcf483674f4b88fe6510524bf3234a5837d19fe1815aa5a285f2739d28b3a3", size = 54516, upload-time = "2026-01-03T02:11:52.243Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8b/8d/807eebf0560759ad90464060e0d1d87ff5409beb6ed56104c553a83a976a/solana-0.36.11-py3-none-any.whl", hash = "sha256:1d659decc67a40ee1e9b5ded373a076b87cf3b4bd0645e120d16d9348c2025ba", size = 64786, upload-time = "2026-01-03T02:11:50.811Z" },
+]
+
+[[package]]
+name = "solders"
+version = "0.27.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "jsonalias" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/66/25/80a81bb3dc4c70329dd0016edbdfbf2e8d8300a98ab9cd1a6ea0266bda7c/solders-0.27.1.tar.gz", hash = "sha256:7d8a24ad2f193afcdc02d6f3975917a7358b0f0ab7f4b3695b135ff2008222c8", size = 180923, upload-time = "2025-11-15T07:50:52.32Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/4b/6b/0c0ee4766705824261779d00229fb95308d6b28422613e0e2af577f60ee3/solders-0.27.1-cp38-abi3-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:4dcd8e766bab24afbe9e0ae363d86f9810457e04b00c8a9149f69ca939ed587c", size = 24883435, upload-time = "2025-11-15T07:50:34.42Z" },
+ { url = "https://files.pythonhosted.org/packages/33/1c/be04a1b26e18c409dd006d214198dc03f0b657c1cb34f4c83b763f8348f0/solders-0.27.1-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5d87b145cc0129095f9cff8c7f28d2e910bc5b5a4cf257c263b08a4b95f111dd", size = 6480729, upload-time = "2025-11-15T07:50:37.323Z" },
+ { url = "https://files.pythonhosted.org/packages/48/03/98dc73c266b11ed5c13b3933510a1aa115becf97f45bec1a22da9d03ffa9/solders-0.27.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6082bbe46b7b1b2b005d046011f89fcae75fc5ea4f1a0ef5c2e9dfb5fe7930ce", size = 12744782, upload-time = "2025-11-15T07:50:39.283Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/39/35384d8fb80d05937bd9e8af7237cfe3f0d017c8aba357209d90d428f3a0/solders-0.27.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ccb821c2e4af43d976f312086f248a67352b3986e5f4c87af41cfeac6d8b5683", size = 6601257, upload-time = "2025-11-15T07:50:41.738Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/65/8989e521142473bf1130613476a4449e106bb97ed6cc86097f6f519b1234/solders-0.27.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:663a10566ae81f67c4515d4db5fbf51b735204741728c1a5cde11c4e019a51df", size = 7277802, upload-time = "2025-11-15T07:50:43.789Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/41/87ecf12cec0e7aa9c67b0cf1b8079fb28aa0af91e97328a3bd0c5e3001ba/solders-0.27.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:d14f05a77dbbf7966fb26f255c81302e6127550bdb66c2fdc99f522043fdf376", size = 7082541, upload-time = "2025-11-15T07:50:45.847Z" },
+ { url = "https://files.pythonhosted.org/packages/33/b9/35e6f59b41bb205b26c7318fcdca43f3d59464fd3ddc13d36f36427f64d4/solders-0.27.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f778eeab411acec0a765a01c7b772f8eca8a8543d98276bd83cb826960da211b", size = 6845568, upload-time = "2025-11-15T07:50:47.698Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/f3/14ed12d8d5047ababaca3271f82ebbf500ff74b6358f283962232103a12d/solders-0.27.1-cp38-abi3-win_amd64.whl", hash = "sha256:f3b787c29570a46d219c7a67543d8b0fadc73abda346653aa20e8eccd839e78b", size = 5295092, upload-time = "2025-11-15T07:50:50.517Z" },
+]
+
+[[package]]
+name = "starlette"
+version = "0.50.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ba/b8/73a0e6a6e079a9d9cfa64113d771e421640b6f679a52eeb9b32f72d871a1/starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca", size = 2646985, upload-time = "2025-11-01T15:25:27.516Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d9/52/1064f510b141bd54025f9b55105e26d1fa970b9be67ad766380a3c9b74b0/starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca", size = 74033, upload-time = "2025-11-01T15:25:25.461Z" },
+]
+
+[[package]]
+name = "tomli"
+version = "2.3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" },
+ { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" },
+ { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" },
+ { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" },
+ { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" },
+ { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" },
+ { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" },
+ { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" },
+ { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" },
+ { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" },
+ { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" },
+ { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" },
+ { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" },
+ { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" },
+ { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" },
+ { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" },
+ { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" },
+ { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" },
+ { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" },
+ { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" },
+ { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" },
+ { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" },
+ { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" },
+ { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" },
+ { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" },
+ { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" },
+ { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" },
+]
+
+[[package]]
+name = "toolz"
+version = "1.1.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/11/d6/114b492226588d6ff54579d95847662fc69196bdeec318eb45393b24c192/toolz-1.1.0.tar.gz", hash = "sha256:27a5c770d068c110d9ed9323f24f1543e83b2f300a687b7891c1a6d56b697b5b", size = 52613, upload-time = "2025-10-17T04:03:21.661Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/fb/12/5911ae3eeec47800503a238d971e51722ccea5feb8569b735184d5fcdbc0/toolz-1.1.0-py3-none-any.whl", hash = "sha256:15ccc861ac51c53696de0a5d6d4607f99c210739caf987b5d2054f3efed429d8", size = 58093, upload-time = "2025-10-17T04:03:20.435Z" },
+]
+
+[[package]]
+name = "typer"
+version = "0.21.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "rich" },
+ { name = "shellingham" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" },
+]
+
+[[package]]
+name = "types-requests"
+version = "2.32.4.20250913"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" },
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.15.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
+]
+
+[[package]]
+name = "typing-inspection"
+version = "0.4.2"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
+]
+
+[[package]]
+name = "urllib3"
+version = "2.6.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd/urllib3-2.6.2.tar.gz", hash = "sha256:016f9c98bb7e98085cb2b4b17b87d2c702975664e4f060c6532e64d1c1a5e797", size = 432930, upload-time = "2025-12-11T15:56:40.252Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/6d/b9/4095b668ea3678bf6a0af005527f39de12fb026516fb3df17495a733b7f8/urllib3-2.6.2-py3-none-any.whl", hash = "sha256:ec21cddfe7724fc7cb4ba4bea7aa8e2ef36f607a4bab81aa6ce42a13dc3f03dd", size = 131182, upload-time = "2025-12-11T15:56:38.584Z" },
+]
+
+[[package]]
+name = "uvicorn"
+version = "0.40.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "click" },
+ { name = "h11" },
+ { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" },
+]
+
+[package.optional-dependencies]
+standard = [
+ { name = "colorama", marker = "sys_platform == 'win32'" },
+ { name = "httptools" },
+ { name = "python-dotenv" },
+ { name = "pyyaml" },
+ { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" },
+ { name = "watchfiles" },
+ { name = "websockets" },
+]
+
+[[package]]
+name = "uvloop"
+version = "0.22.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/06/f0/18d39dbd1971d6d62c4629cc7fa67f74821b0dc1f5a77af43719de7936a7/uvloop-0.22.1.tar.gz", hash = "sha256:6c84bae345b9147082b17371e3dd5d42775bddce91f885499017f4607fdaf39f", size = 2443250, upload-time = "2025-10-16T22:17:19.342Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/eb/14/ecceb239b65adaaf7fde510aa8bd534075695d1e5f8dadfa32b5723d9cfb/uvloop-0.22.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ef6f0d4cc8a9fa1f6a910230cd53545d9a14479311e87e3cb225495952eb672c", size = 1343335, upload-time = "2025-10-16T22:16:11.43Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/ae/6f6f9af7f590b319c94532b9567409ba11f4fa71af1148cab1bf48a07048/uvloop-0.22.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7cd375a12b71d33d46af85a3343b35d98e8116134ba404bd657b3b1d15988792", size = 742903, upload-time = "2025-10-16T22:16:12.979Z" },
+ { url = "https://files.pythonhosted.org/packages/09/bd/3667151ad0702282a1f4d5d29288fce8a13c8b6858bf0978c219cd52b231/uvloop-0.22.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac33ed96229b7790eb729702751c0e93ac5bc3bcf52ae9eccbff30da09194b86", size = 3648499, upload-time = "2025-10-16T22:16:14.451Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/f6/21657bb3beb5f8c57ce8be3b83f653dd7933c2fd00545ed1b092d464799a/uvloop-0.22.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:481c990a7abe2c6f4fc3d98781cc9426ebd7f03a9aaa7eb03d3bfc68ac2a46bd", size = 3700133, upload-time = "2025-10-16T22:16:16.272Z" },
+ { url = "https://files.pythonhosted.org/packages/09/e0/604f61d004ded805f24974c87ddd8374ef675644f476f01f1df90e4cdf72/uvloop-0.22.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a592b043a47ad17911add5fbd087c76716d7c9ccc1d64ec9249ceafd735f03c2", size = 3512681, upload-time = "2025-10-16T22:16:18.07Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/ce/8491fd370b0230deb5eac69c7aae35b3be527e25a911c0acdffb922dc1cd/uvloop-0.22.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1489cf791aa7b6e8c8be1c5a080bae3a672791fcb4e9e12249b05862a2ca9cec", size = 3615261, upload-time = "2025-10-16T22:16:19.596Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/d5/69900f7883235562f1f50d8184bb7dd84a2fb61e9ec63f3782546fdbd057/uvloop-0.22.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c60ebcd36f7b240b30788554b6f0782454826a0ed765d8430652621b5de674b9", size = 1352420, upload-time = "2025-10-16T22:16:21.187Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/73/c4e271b3bce59724e291465cc936c37758886a4868787da0278b3b56b905/uvloop-0.22.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b7f102bf3cb1995cfeaee9321105e8f5da76fdb104cdad8986f85461a1b7b77", size = 748677, upload-time = "2025-10-16T22:16:22.558Z" },
+ { url = "https://files.pythonhosted.org/packages/86/94/9fb7fad2f824d25f8ecac0d70b94d0d48107ad5ece03769a9c543444f78a/uvloop-0.22.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53c85520781d84a4b8b230e24a5af5b0778efdb39142b424990ff1ef7c48ba21", size = 3753819, upload-time = "2025-10-16T22:16:23.903Z" },
+ { url = "https://files.pythonhosted.org/packages/74/4f/256aca690709e9b008b7108bc85fba619a2bc37c6d80743d18abad16ee09/uvloop-0.22.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702", size = 3804529, upload-time = "2025-10-16T22:16:25.246Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/74/03c05ae4737e871923d21a76fe28b6aad57f5c03b6e6bfcfa5ad616013e4/uvloop-0.22.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40631b049d5972c6755b06d0bfe8233b1bd9a8a6392d9d1c45c10b6f9e9b2733", size = 3621267, upload-time = "2025-10-16T22:16:26.819Z" },
+ { url = "https://files.pythonhosted.org/packages/75/be/f8e590fe61d18b4a92070905497aec4c0e64ae1761498cad09023f3f4b3e/uvloop-0.22.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:535cc37b3a04f6cd2c1ef65fa1d370c9a35b6695df735fcff5427323f2cd5473", size = 3723105, upload-time = "2025-10-16T22:16:28.252Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/ff/7f72e8170be527b4977b033239a83a68d5c881cc4775fca255c677f7ac5d/uvloop-0.22.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:fe94b4564e865d968414598eea1a6de60adba0c040ba4ed05ac1300de402cd42", size = 1359936, upload-time = "2025-10-16T22:16:29.436Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/c6/e5d433f88fd54d81ef4be58b2b7b0cea13c442454a1db703a1eea0db1a59/uvloop-0.22.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:51eb9bd88391483410daad430813d982010f9c9c89512321f5b60e2cddbdddd6", size = 752769, upload-time = "2025-10-16T22:16:30.493Z" },
+ { url = "https://files.pythonhosted.org/packages/24/68/a6ac446820273e71aa762fa21cdcc09861edd3536ff47c5cd3b7afb10eeb/uvloop-0.22.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:700e674a166ca5778255e0e1dc4e9d79ab2acc57b9171b79e65feba7184b3370", size = 4317413, upload-time = "2025-10-16T22:16:31.644Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/6f/e62b4dfc7ad6518e7eff2516f680d02a0f6eb62c0c212e152ca708a0085e/uvloop-0.22.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4", size = 4426307, upload-time = "2025-10-16T22:16:32.917Z" },
+ { url = "https://files.pythonhosted.org/packages/90/60/97362554ac21e20e81bcef1150cb2a7e4ffdaf8ea1e5b2e8bf7a053caa18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e047cc068570bac9866237739607d1313b9253c3051ad84738cbb095be0537b2", size = 4131970, upload-time = "2025-10-16T22:16:34.015Z" },
+ { url = "https://files.pythonhosted.org/packages/99/39/6b3f7d234ba3964c428a6e40006340f53ba37993f46ed6e111c6e9141d18/uvloop-0.22.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:512fec6815e2dd45161054592441ef76c830eddaad55c8aa30952e6fe1ed07c0", size = 4296343, upload-time = "2025-10-16T22:16:35.149Z" },
+ { url = "https://files.pythonhosted.org/packages/89/8c/182a2a593195bfd39842ea68ebc084e20c850806117213f5a299dfc513d9/uvloop-0.22.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:561577354eb94200d75aca23fbde86ee11be36b00e52a4eaf8f50fb0c86b7705", size = 1358611, upload-time = "2025-10-16T22:16:36.833Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/14/e301ee96a6dc95224b6f1162cd3312f6d1217be3907b79173b06785f2fe7/uvloop-0.22.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cdf5192ab3e674ca26da2eada35b288d2fa49fdd0f357a19f0e7c4e7d5077c8", size = 751811, upload-time = "2025-10-16T22:16:38.275Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/02/654426ce265ac19e2980bfd9ea6590ca96a56f10c76e63801a2df01c0486/uvloop-0.22.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e2ea3d6190a2968f4a14a23019d3b16870dd2190cd69c8180f7c632d21de68d", size = 4288562, upload-time = "2025-10-16T22:16:39.375Z" },
+ { url = "https://files.pythonhosted.org/packages/15/c0/0be24758891ef825f2065cd5db8741aaddabe3e248ee6acc5e8a80f04005/uvloop-0.22.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e", size = 4366890, upload-time = "2025-10-16T22:16:40.547Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/53/8369e5219a5855869bcee5f4d317f6da0e2c669aecf0ef7d371e3d084449/uvloop-0.22.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc5ef13bbc10b5335792360623cc378d52d7e62c2de64660616478c32cd0598e", size = 4119472, upload-time = "2025-10-16T22:16:41.694Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ba/d69adbe699b768f6b29a5eec7b47dd610bd17a69de51b251126a801369ea/uvloop-0.22.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1f38ec5e3f18c8a10ded09742f7fb8de0108796eb673f30ce7762ce1b8550cad", size = 4239051, upload-time = "2025-10-16T22:16:43.224Z" },
+ { url = "https://files.pythonhosted.org/packages/90/cd/b62bdeaa429758aee8de8b00ac0dd26593a9de93d302bff3d21439e9791d/uvloop-0.22.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142", size = 1362067, upload-time = "2025-10-16T22:16:44.503Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/f8/a132124dfda0777e489ca86732e85e69afcd1ff7686647000050ba670689/uvloop-0.22.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4baa86acedf1d62115c1dc6ad1e17134476688f08c6efd8a2ab076e815665c74", size = 752423, upload-time = "2025-10-16T22:16:45.968Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/94/94af78c156f88da4b3a733773ad5ba0b164393e357cc4bd0ab2e2677a7d6/uvloop-0.22.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35", size = 4272437, upload-time = "2025-10-16T22:16:47.451Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/35/60249e9fd07b32c665192cec7af29e06c7cd96fa1d08b84f012a56a0b38e/uvloop-0.22.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25", size = 4292101, upload-time = "2025-10-16T22:16:49.318Z" },
+ { url = "https://files.pythonhosted.org/packages/02/62/67d382dfcb25d0a98ce73c11ed1a6fba5037a1a1d533dcbb7cab033a2636/uvloop-0.22.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b31dc2fccbd42adc73bc4e7cdbae4fc5086cf378979e53ca5d0301838c5682c6", size = 4114158, upload-time = "2025-10-16T22:16:50.517Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/7a/f1171b4a882a5d13c8b7576f348acfe6074d72eaf52cccef752f748d4a9f/uvloop-0.22.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:93f617675b2d03af4e72a5333ef89450dfaa5321303ede6e67ba9c9d26878079", size = 4177360, upload-time = "2025-10-16T22:16:52.646Z" },
+ { url = "https://files.pythonhosted.org/packages/79/7b/b01414f31546caf0919da80ad57cbfe24c56b151d12af68cee1b04922ca8/uvloop-0.22.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:37554f70528f60cad66945b885eb01f1bb514f132d92b6eeed1c90fd54ed6289", size = 1454790, upload-time = "2025-10-16T22:16:54.355Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/31/0bb232318dd838cad3fa8fb0c68c8b40e1145b32025581975e18b11fab40/uvloop-0.22.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b76324e2dc033a0b2f435f33eb88ff9913c156ef78e153fb210e03c13da746b3", size = 796783, upload-time = "2025-10-16T22:16:55.906Z" },
+ { url = "https://files.pythonhosted.org/packages/42/38/c9b09f3271a7a723a5de69f8e237ab8e7803183131bc57c890db0b6bb872/uvloop-0.22.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:badb4d8e58ee08dad957002027830d5c3b06aea446a6a3744483c2b3b745345c", size = 4647548, upload-time = "2025-10-16T22:16:57.008Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/37/945b4ca0ac27e3dc4952642d4c900edd030b3da6c9634875af6e13ae80e5/uvloop-0.22.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b91328c72635f6f9e0282e4a57da7470c7350ab1c9f48546c0f2866205349d21", size = 4467065, upload-time = "2025-10-16T22:16:58.206Z" },
+ { url = "https://files.pythonhosted.org/packages/97/cc/48d232f33d60e2e2e0b42f4e73455b146b76ebe216487e862700457fbf3c/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:daf620c2995d193449393d6c62131b3fbd40a63bf7b307a1527856ace637fe88", size = 4328384, upload-time = "2025-10-16T22:16:59.36Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/16/c1fd27e9549f3c4baf1dc9c20c456cd2f822dbf8de9f463824b0c0357e06/uvloop-0.22.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6cde23eeda1a25c75b2e07d39970f3374105d5eafbaab2a4482be82f272d5a5e", size = 4296730, upload-time = "2025-10-16T22:17:00.744Z" },
+]
+
+[[package]]
+name = "watchfiles"
+version = "1.1.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "anyio" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/1a/206e8cf2dd86fddf939165a57b4df61607a1e0add2785f170a3f616b7d9f/watchfiles-1.1.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:eef58232d32daf2ac67f42dea51a2c80f0d03379075d44a587051e63cc2e368c", size = 407318, upload-time = "2025-10-14T15:04:18.753Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/0f/abaf5262b9c496b5dad4ed3c0e799cbecb1f8ea512ecb6ddd46646a9fca3/watchfiles-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03fa0f5237118a0c5e496185cafa92878568b652a2e9a9382a5151b1a0380a43", size = 394478, upload-time = "2025-10-14T15:04:20.297Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/04/9cc0ba88697b34b755371f5ace8d3a4d9a15719c07bdc7bd13d7d8c6a341/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca65483439f9c791897f7db49202301deb6e15fe9f8fe2fed555bf986d10c31", size = 449894, upload-time = "2025-10-14T15:04:21.527Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/9c/eda4615863cd8621e89aed4df680d8c3ec3da6a4cf1da113c17decd87c7f/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f0ab1c1af0cb38e3f598244c17919fb1a84d1629cc08355b0074b6d7f53138ac", size = 459065, upload-time = "2025-10-14T15:04:22.795Z" },
+ { url = "https://files.pythonhosted.org/packages/84/13/f28b3f340157d03cbc8197629bc109d1098764abe1e60874622a0be5c112/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bc570d6c01c206c46deb6e935a260be44f186a2f05179f52f7fcd2be086a94d", size = 488377, upload-time = "2025-10-14T15:04:24.138Z" },
+ { url = "https://files.pythonhosted.org/packages/86/93/cfa597fa9389e122488f7ffdbd6db505b3b915ca7435ecd7542e855898c2/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e84087b432b6ac94778de547e08611266f1f8ffad28c0ee4c82e028b0fc5966d", size = 595837, upload-time = "2025-10-14T15:04:25.057Z" },
+ { url = "https://files.pythonhosted.org/packages/57/1e/68c1ed5652b48d89fc24d6af905d88ee4f82fa8bc491e2666004e307ded1/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:620bae625f4cb18427b1bb1a2d9426dc0dd5a5ba74c7c2cdb9de405f7b129863", size = 473456, upload-time = "2025-10-14T15:04:26.497Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/dc/1a680b7458ffa3b14bb64878112aefc8f2e4f73c5af763cbf0bd43100658/watchfiles-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:544364b2b51a9b0c7000a4b4b02f90e9423d97fbbf7e06689236443ebcad81ab", size = 455614, upload-time = "2025-10-14T15:04:27.539Z" },
+ { url = "https://files.pythonhosted.org/packages/61/a5/3d782a666512e01eaa6541a72ebac1d3aae191ff4a31274a66b8dd85760c/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bbe1ef33d45bc71cf21364df962af171f96ecaeca06bd9e3d0b583efb12aec82", size = 630690, upload-time = "2025-10-14T15:04:28.495Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/73/bb5f38590e34687b2a9c47a244aa4dd50c56a825969c92c9c5fc7387cea1/watchfiles-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1a0bb430adb19ef49389e1ad368450193a90038b5b752f4ac089ec6942c4dff4", size = 622459, upload-time = "2025-10-14T15:04:29.491Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/ac/c9bb0ec696e07a20bd58af5399aeadaef195fb2c73d26baf55180fe4a942/watchfiles-1.1.1-cp310-cp310-win32.whl", hash = "sha256:3f6d37644155fb5beca5378feb8c1708d5783145f2a0f1c4d5a061a210254844", size = 272663, upload-time = "2025-10-14T15:04:30.435Z" },
+ { url = "https://files.pythonhosted.org/packages/11/a0/a60c5a7c2ec59fa062d9a9c61d02e3b6abd94d32aac2d8344c4bdd033326/watchfiles-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a36d8efe0f290835fd0f33da35042a1bb5dc0e83cbc092dcf69bce442579e88e", size = 287453, upload-time = "2025-10-14T15:04:31.53Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f8/2c5f479fb531ce2f0564eda479faecf253d886b1ab3630a39b7bf7362d46/watchfiles-1.1.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f57b396167a2565a4e8b5e56a5a1c537571733992b226f4f1197d79e94cf0ae5", size = 406529, upload-time = "2025-10-14T15:04:32.899Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/cd/f515660b1f32f65df671ddf6f85bfaca621aee177712874dc30a97397977/watchfiles-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:421e29339983e1bebc281fab40d812742268ad057db4aee8c4d2bce0af43b741", size = 394384, upload-time = "2025-10-14T15:04:33.761Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/c3/28b7dc99733eab43fca2d10f55c86e03bd6ab11ca31b802abac26b23d161/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e43d39a741e972bab5d8100b5cdacf69db64e34eb19b6e9af162bccf63c5cc6", size = 448789, upload-time = "2025-10-14T15:04:34.679Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/24/33e71113b320030011c8e4316ccca04194bf0cbbaeee207f00cbc7d6b9f5/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f537afb3276d12814082a2e9b242bdcf416c2e8fd9f799a737990a1dbe906e5b", size = 460521, upload-time = "2025-10-14T15:04:35.963Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/c3/3c9a55f255aa57b91579ae9e98c88704955fa9dac3e5614fb378291155df/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2cd9e04277e756a2e2d2543d65d1e2166d6fd4c9b183f8808634fda23f17b14", size = 488722, upload-time = "2025-10-14T15:04:37.091Z" },
+ { url = "https://files.pythonhosted.org/packages/49/36/506447b73eb46c120169dc1717fe2eff07c234bb3232a7200b5f5bd816e9/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f3f58818dc0b07f7d9aa7fe9eb1037aecb9700e63e1f6acfed13e9fef648f5d", size = 596088, upload-time = "2025-10-14T15:04:38.39Z" },
+ { url = "https://files.pythonhosted.org/packages/82/ab/5f39e752a9838ec4d52e9b87c1e80f1ee3ccdbe92e183c15b6577ab9de16/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb9f66367023ae783551042d31b1d7fd422e8289eedd91f26754a66f44d5cff", size = 472923, upload-time = "2025-10-14T15:04:39.666Z" },
+ { url = "https://files.pythonhosted.org/packages/af/b9/a419292f05e302dea372fa7e6fda5178a92998411f8581b9830d28fb9edb/watchfiles-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606", size = 456080, upload-time = "2025-10-14T15:04:40.643Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/c3/d5932fd62bde1a30c36e10c409dc5d54506726f08cb3e1d8d0ba5e2bc8db/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5fac835b4ab3c6487b5dbad78c4b3724e26bcc468e886f8ba8cc4306f68f6701", size = 629432, upload-time = "2025-10-14T15:04:41.789Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/77/16bddd9779fafb795f1a94319dc965209c5641db5bf1edbbccace6d1b3c0/watchfiles-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:399600947b170270e80134ac854e21b3ccdefa11a9529a3decc1327088180f10", size = 623046, upload-time = "2025-10-14T15:04:42.718Z" },
+ { url = "https://files.pythonhosted.org/packages/46/ef/f2ecb9a0f342b4bfad13a2787155c6ee7ce792140eac63a34676a2feeef2/watchfiles-1.1.1-cp311-cp311-win32.whl", hash = "sha256:de6da501c883f58ad50db3a32ad397b09ad29865b5f26f64c24d3e3281685849", size = 271473, upload-time = "2025-10-14T15:04:43.624Z" },
+ { url = "https://files.pythonhosted.org/packages/94/bc/f42d71125f19731ea435c3948cad148d31a64fccde3867e5ba4edee901f9/watchfiles-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:35c53bd62a0b885bf653ebf6b700d1bf05debb78ad9292cf2a942b23513dc4c4", size = 287598, upload-time = "2025-10-14T15:04:44.516Z" },
+ { url = "https://files.pythonhosted.org/packages/57/c9/a30f897351f95bbbfb6abcadafbaca711ce1162f4db95fc908c98a9165f3/watchfiles-1.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:57ca5281a8b5e27593cb7d82c2ac927ad88a96ed406aa446f6344e4328208e9e", size = 277210, upload-time = "2025-10-14T15:04:45.883Z" },
+ { url = "https://files.pythonhosted.org/packages/74/d5/f039e7e3c639d9b1d09b07ea412a6806d38123f0508e5f9b48a87b0a76cc/watchfiles-1.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8c89f9f2f740a6b7dcc753140dd5e1ab9215966f7a3530d0c0705c83b401bd7d", size = 404745, upload-time = "2025-10-14T15:04:46.731Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/96/a881a13aa1349827490dab2d363c8039527060cfcc2c92cc6d13d1b1049e/watchfiles-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd404be08018c37350f0d6e34676bd1e2889990117a2b90070b3007f172d0610", size = 391769, upload-time = "2025-10-14T15:04:48.003Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/5b/d3b460364aeb8da471c1989238ea0e56bec24b6042a68046adf3d9ddb01c/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8526e8f916bb5b9a0a777c8317c23ce65de259422bba5b31325a6fa6029d33af", size = 449374, upload-time = "2025-10-14T15:04:49.179Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/44/5769cb62d4ed055cb17417c0a109a92f007114a4e07f30812a73a4efdb11/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2edc3553362b1c38d9f06242416a5d8e9fe235c204a4072e988ce2e5bb1f69f6", size = 459485, upload-time = "2025-10-14T15:04:50.155Z" },
+ { url = "https://files.pythonhosted.org/packages/19/0c/286b6301ded2eccd4ffd0041a1b726afda999926cf720aab63adb68a1e36/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30f7da3fb3f2844259cba4720c3fc7138eb0f7b659c38f3bfa65084c7fc7abce", size = 488813, upload-time = "2025-10-14T15:04:51.059Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/2b/8530ed41112dd4a22f4dcfdb5ccf6a1baad1ff6eed8dc5a5f09e7e8c41c7/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8979280bdafff686ba5e4d8f97840f929a87ed9cdf133cbbd42f7766774d2aa", size = 594816, upload-time = "2025-10-14T15:04:52.031Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/d2/f5f9fb49489f184f18470d4f99f4e862a4b3e9ac2865688eb2099e3d837a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dcc5c24523771db3a294c77d94771abcfcb82a0e0ee8efd910c37c59ec1b31bb", size = 475186, upload-time = "2025-10-14T15:04:53.064Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/68/5707da262a119fb06fbe214d82dd1fe4a6f4af32d2d14de368d0349eb52a/watchfiles-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803", size = 456812, upload-time = "2025-10-14T15:04:55.174Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ab/3cbb8756323e8f9b6f9acb9ef4ec26d42b2109bce830cc1f3468df20511d/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:28475ddbde92df1874b6c5c8aaeb24ad5be47a11f87cde5a28ef3835932e3e94", size = 630196, upload-time = "2025-10-14T15:04:56.22Z" },
+ { url = "https://files.pythonhosted.org/packages/78/46/7152ec29b8335f80167928944a94955015a345440f524d2dfe63fc2f437b/watchfiles-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:36193ed342f5b9842edd3532729a2ad55c4160ffcfa3700e0d54be496b70dd43", size = 622657, upload-time = "2025-10-14T15:04:57.521Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/bf/95895e78dd75efe9a7f31733607f384b42eb5feb54bd2eb6ed57cc2e94f4/watchfiles-1.1.1-cp312-cp312-win32.whl", hash = "sha256:859e43a1951717cc8de7f4c77674a6d389b106361585951d9e69572823f311d9", size = 272042, upload-time = "2025-10-14T15:04:59.046Z" },
+ { url = "https://files.pythonhosted.org/packages/87/0a/90eb755f568de2688cb220171c4191df932232c20946966c27a59c400850/watchfiles-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:91d4c9a823a8c987cce8fa2690923b069966dabb196dd8d137ea2cede885fde9", size = 288410, upload-time = "2025-10-14T15:05:00.081Z" },
+ { url = "https://files.pythonhosted.org/packages/36/76/f322701530586922fbd6723c4f91ace21364924822a8772c549483abed13/watchfiles-1.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:a625815d4a2bdca61953dbba5a39d60164451ef34c88d751f6c368c3ea73d404", size = 278209, upload-time = "2025-10-14T15:05:01.168Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" },
+ { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" },
+ { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" },
+ { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" },
+ { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" },
+ { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" },
+ { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" },
+ { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" },
+ { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" },
+ { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" },
+ { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" },
+ { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" },
+ { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" },
+ { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" },
+ { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" },
+ { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" },
+ { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" },
+ { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/4c/a888c91e2e326872fa4705095d64acd8aa2fb9c1f7b9bd0588f33850516c/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:17ef139237dfced9da49fb7f2232c86ca9421f666d78c264c7ffca6601d154c3", size = 409611, upload-time = "2025-10-14T15:06:05.809Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/c7/5420d1943c8e3ce1a21c0a9330bcf7edafb6aa65d26b21dbb3267c9e8112/watchfiles-1.1.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:672b8adf25b1a0d35c96b5888b7b18699d27d4194bac8beeae75be4b7a3fc9b2", size = 396889, upload-time = "2025-10-14T15:06:07.035Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/e5/0072cef3804ce8d3aaddbfe7788aadff6b3d3f98a286fdbee9fd74ca59a7/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77a13aea58bc2b90173bc69f2a90de8e282648939a00a602e1dc4ee23e26b66d", size = 451616, upload-time = "2025-10-14T15:06:08.072Z" },
+ { url = "https://files.pythonhosted.org/packages/83/4e/b87b71cbdfad81ad7e83358b3e447fedd281b880a03d64a760fe0a11fc2e/watchfiles-1.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b495de0bb386df6a12b18335a0285dda90260f51bdb505503c02bcd1ce27a8b", size = 458413, upload-time = "2025-10-14T15:06:09.209Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/8e/e500f8b0b77be4ff753ac94dc06b33d8f0d839377fee1b78e8c8d8f031bf/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:db476ab59b6765134de1d4fe96a1a9c96ddf091683599be0f26147ea1b2e4b88", size = 408250, upload-time = "2025-10-14T15:06:10.264Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/95/615e72cd27b85b61eec764a5ca51bd94d40b5adea5ff47567d9ebc4d275a/watchfiles-1.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89eef07eee5e9d1fda06e38822ad167a044153457e6fd997f8a858ab7564a336", size = 396117, upload-time = "2025-10-14T15:06:11.28Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/81/e7fe958ce8a7fb5c73cc9fb07f5aeaf755e6aa72498c57d760af760c91f8/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce19e06cbda693e9e7686358af9cd6f5d61312ab8b00488bc36f5aabbaf77e24", size = 450493, upload-time = "2025-10-14T15:06:12.321Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/d4/ed38dd3b1767193de971e694aa544356e63353c33a85d948166b5ff58b9e/watchfiles-1.1.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e6f39af2eab0118338902798b5aa6664f46ff66bc0280de76fca67a7f262a49", size = 457546, upload-time = "2025-10-14T15:06:13.372Z" },
+]
+
+[[package]]
+name = "web3"
+version = "7.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "aiohttp" },
+ { name = "eth-abi" },
+ { name = "eth-account" },
+ { name = "eth-hash", extra = ["pycryptodome"] },
+ { name = "eth-typing" },
+ { name = "eth-utils" },
+ { name = "hexbytes" },
+ { name = "pydantic" },
+ { name = "pyunormalize" },
+ { name = "pywin32", marker = "sys_platform == 'win32'" },
+ { name = "requests" },
+ { name = "types-requests" },
+ { name = "typing-extensions" },
+ { name = "websockets" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/4c/85/1515644dc1b0170e43e8c26531a3cec8ebc916185fbe2db0020e450b7114/web3-7.14.0.tar.gz", hash = "sha256:d82c78007c280e478b3920cd56658df17f2f76af584ee3318df6b60d4944b8a2", size = 2194249, upload-time = "2025-10-16T19:25:07.484Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/66/94/68ce430b5e19803d2b34736dd653e8627bde68d93cd8b0bec44384487a59/web3-7.14.0-py3-none-any.whl", hash = "sha256:a78c0a979bf11c47795f564512131c01b7598a276976f7031c55140f733e210a", size = 1370801, upload-time = "2025-10-16T19:25:04.052Z" },
+]
+
+[[package]]
+name = "websockets"
+version = "15.0.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" },
+ { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" },
+ { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" },
+ { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" },
+ { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" },
+ { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" },
+ { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" },
+ { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" },
+ { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" },
+ { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" },
+ { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" },
+ { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" },
+ { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" },
+ { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" },
+ { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" },
+ { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" },
+ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" },
+ { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" },
+ { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" },
+ { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" },
+ { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
+]
+
+[[package]]
+name = "x402"
+version = "2.1.0"
+source = { editable = "../../../../python/x402" }
+dependencies = [
+ { name = "nest-asyncio" },
+ { name = "pydantic" },
+ { name = "typing-extensions" },
+]
+
+[package.optional-dependencies]
+evm = [
+ { name = "eth-abi" },
+ { name = "eth-account" },
+ { name = "eth-keys" },
+ { name = "eth-utils" },
+ { name = "web3" },
+]
+fastapi = [
+ { name = "fastapi", extra = ["standard"] },
+ { name = "starlette" },
+]
+svm = [
+ { name = "solana" },
+ { name = "solders" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "eth-abi", marker = "extra == 'evm'", specifier = ">=5.0.0" },
+ { name = "eth-account", marker = "extra == 'evm'", specifier = ">=0.12.0" },
+ { name = "eth-keys", marker = "extra == 'evm'", specifier = ">=0.5.0" },
+ { name = "eth-utils", marker = "extra == 'evm'", specifier = ">=4.0.0" },
+ { name = "fastapi", extras = ["standard"], marker = "extra == 'fastapi'", specifier = ">=0.115.0" },
+ { name = "flask", marker = "extra == 'flask'", specifier = ">=3.0.0" },
+ { name = "httpx", marker = "extra == 'httpx'", specifier = ">=0.28.1" },
+ { name = "jsonschema", marker = "extra == 'extensions'", specifier = ">=4.0.0" },
+ { name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.0.0" },
+ { name = "nest-asyncio", specifier = ">=1.6.0" },
+ { name = "pydantic", specifier = ">=2.0.0" },
+ { name = "requests", marker = "extra == 'requests'", specifier = ">=2.31.0" },
+ { name = "solana", marker = "extra == 'svm'", specifier = ">=0.36.0" },
+ { name = "solders", marker = "extra == 'svm'", specifier = ">=0.27.0" },
+ { name = "starlette", marker = "extra == 'fastapi'", specifier = ">=0.27.0" },
+ { name = "typing-extensions", specifier = ">=4.0.0" },
+ { name = "web3", marker = "extra == 'evm'", specifier = ">=7.0.0" },
+ { name = "x402", extras = ["evm", "svm"], marker = "extra == 'mechanisms'" },
+ { name = "x402", extras = ["flask", "fastapi"], marker = "extra == 'servers'" },
+ { name = "x402", extras = ["httpx", "requests"], marker = "extra == 'clients'" },
+ { name = "x402", extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp", "extensions"], marker = "extra == 'all'" },
+]
+provides-extras = ["httpx", "requests", "flask", "fastapi", "evm", "svm", "mcp", "extensions", "clients", "servers", "mechanisms", "all"]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "black", specifier = ">=23.0.0" },
+ { name = "eth-abi", specifier = ">=5.0.0" },
+ { name = "eth-account", specifier = ">=0.12.0" },
+ { name = "eth-keys", specifier = ">=0.5.0" },
+ { name = "eth-utils", specifier = ">=4.0.0" },
+ { name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
+ { name = "flask", specifier = ">=3.0.0" },
+ { name = "httpx", specifier = ">=0.28.1" },
+ { name = "jsonschema", specifier = ">=4.0.0" },
+ { name = "mcp", specifier = ">=1.26.0" },
+ { name = "mypy", specifier = ">=1.0.0" },
+ { name = "nest-asyncio", specifier = ">=1.6.0" },
+ { name = "pytest", specifier = ">=7.0.0" },
+ { name = "pytest-asyncio", specifier = ">=0.21.0" },
+ { name = "requests", specifier = ">=2.31.0" },
+ { name = "ruff", specifier = ">=0.1.0" },
+ { name = "solana", specifier = ">=0.36.0" },
+ { name = "solders", specifier = ">=0.27.0" },
+ { name = "starlette", specifier = ">=0.27.0" },
+ { name = "towncrier", specifier = ">=24.8.0,<25" },
+ { name = "web3", specifier = ">=7.0.0" },
+]
+
+[[package]]
+name = "x402-v2-fastapi-example"
+version = "0.1.0"
+source = { virtual = "." }
+dependencies = [
+ { name = "python-dotenv" },
+ { name = "uvicorn", extra = ["standard"] },
+ { name = "x402", extra = ["evm", "fastapi", "svm"] },
+]
+
+[package.dev-dependencies]
+dev = [
+ { name = "black" },
+ { name = "mypy" },
+ { name = "pytest" },
+ { name = "pytest-asyncio" },
+ { name = "ruff" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "python-dotenv", specifier = ">=1.2.1" },
+ { name = "uvicorn", extras = ["standard"], specifier = ">=0.40.0" },
+ { name = "x402", extras = ["fastapi", "evm", "svm"], editable = "../../../../python/x402" },
+]
+
+[package.metadata.requires-dev]
+dev = [
+ { name = "black", specifier = ">=23.0.0" },
+ { name = "mypy", specifier = ">=1.0.0" },
+ { name = "pytest", specifier = ">=7.0.0" },
+ { name = "pytest-asyncio", specifier = ">=0.21.0" },
+ { name = "ruff", specifier = ">=0.1.0" },
+]
+
+[[package]]
+name = "yarl"
+version = "1.22.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "idna" },
+ { name = "multidict" },
+ { name = "propcache" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" },
+ { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" },
+ { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" },
+ { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" },
+ { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" },
+ { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" },
+ { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" },
+ { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" },
+ { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" },
+ { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" },
+ { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" },
+ { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" },
+ { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" },
+ { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" },
+ { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" },
+ { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" },
+ { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" },
+ { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" },
+ { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" },
+ { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" },
+ { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" },
+ { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" },
+ { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" },
+ { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" },
+ { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" },
+ { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" },
+ { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" },
+ { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" },
+ { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" },
+ { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" },
+ { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" },
+ { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" },
+ { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" },
+ { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" },
+ { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" },
+ { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" },
+ { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" },
+ { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" },
+ { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" },
+ { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" },
+ { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" },
+ { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" },
+ { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" },
+ { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" },
+ { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" },
+ { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" },
+ { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" },
+ { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" },
+ { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" },
+ { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" },
+ { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" },
+ { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" },
+ { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" },
+ { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" },
+ { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" },
+ { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" },
+ { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" },
+ { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" },
+ { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" },
+ { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" },
+]
diff --git a/examples/typescript/clients/advanced/.env-local b/examples/typescript/clients/advanced/.env-local
index 8339ac0ab9..57f92e9636 100644
--- a/examples/typescript/clients/advanced/.env-local
+++ b/examples/typescript/clients/advanced/.env-local
@@ -1,4 +1,5 @@
EVM_PRIVATE_KEY=
SVM_PRIVATE_KEY=
+STELLAR_PRIVATE_KEY=
RESOURCE_SERVER_URL=http://localhost:4021
ENDPOINT_PATH=/weather
\ No newline at end of file
diff --git a/examples/typescript/clients/advanced/README.md b/examples/typescript/clients/advanced/README.md
index 743e968637..4c3470db26 100644
--- a/examples/typescript/clients/advanced/README.md
+++ b/examples/typescript/clients/advanced/README.md
@@ -24,7 +24,7 @@ const response = await fetchWithPayment("http://localhost:4021/weather");
- Node.js v20+ (install via [nvm](https://github.com/nvm-sh/nvm))
- pnpm v10 (install via [pnpm.io/installation](https://pnpm.io/installation))
-- Valid EVM and/or SVM private keys for making payments
+- Valid EVM, SVM, and/or Stellar private keys for making payments
- A running x402 server (see [server examples](../../servers/))
- Familiarity with the [basic fetch client](../fetch/)
@@ -40,6 +40,7 @@ and fill required environment variables:
- `EVM_PRIVATE_KEY` - Ethereum private key for EVM payments
- `SVM_PRIVATE_KEY` - Solana private key for SVM payments
+- `STELLAR_PRIVATE_KEY` - Stellar secret key (starts with `S`) for signing Stellar payments
2. Install and build all packages from the typescript examples root:
@@ -55,6 +56,16 @@ cd clients/advanced
pnpm dev
```
+### Account Setup Instructions
+
+#### Stellar Testnet
+
+Stellar accounts need to be created and funded with both XLM and USDC. Instructions:
+
+1. Go to [Stellar Laboratory](https://lab.stellar.org/account/create) ➡️ Generate keypair ➡️ Fund account with Friendbot, then copy the `Secret` and `Public` keys so you can use them.
+2. Add USDC trustline (required to transact USDC): go to [Fund Account](https://lab.stellar.org/account/fund) ➡️ Paste your `Public Key` ➡️ Add USDC Trustline ➡️ paste your `Secret key` ➡️ Sign transaction ➡️ Add Trustline.
+3. Get testnet USDC from [Circle Faucet](https://faucet.circle.com/) (select Stellar network).
+
## Available Examples
Each example demonstrates a specific advanced pattern:
@@ -90,6 +101,7 @@ Use the builder pattern for fine-grained control over which networks are support
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { ExactSvmScheme } from "@x402/svm/exact/client";
+import { ExactStellarScheme } from "@x402/stellar/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const evmSigner = privateKeyToAccount(evmPrivateKey);
@@ -100,6 +112,7 @@ const client = new x402Client()
.register("eip155:*", new ExactEvmScheme(evmSigner)) // All EVM networks
.register("eip155:1", new ExactEvmScheme(mainnetSigner)) // Ethereum mainnet override
.register("solana:*", new ExactSvmScheme(svmSigner)); // All Solana networks
+ .register("stellar:*", new ExactStellarScheme(stellarSigner)); // All Stellar networks
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("http://localhost:4021/weather");
@@ -162,9 +175,10 @@ Configure client-side network preferences with automatic fallback:
import { x402Client, wrapFetchWithPayment, type PaymentRequirements } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { ExactSvmScheme } from "@x402/svm/exact/client";
+import { ExactStellarScheme } from "@x402/stellar/exact/client";
// Define network preference order (most preferred first)
-const networkPreferences = ["solana:", "eip155:"];
+const networkPreferences = ["eip155:", "solana:", "stellar:"];
const preferredNetworkSelector = (
_x402Version: number,
@@ -181,7 +195,8 @@ const preferredNetworkSelector = (
const client = new x402Client(preferredNetworkSelector)
.register("eip155:*", new ExactEvmScheme(evmSigner))
- .register("solana:*", new ExactSvmScheme(svmSigner));
+ .register("solana:*", new ExactSvmScheme(svmSigner))
+ .register("stellar:*", new ExactStellarScheme(stellarSigner));
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const response = await fetchWithPayment("http://localhost:4021/weather");
diff --git a/examples/typescript/clients/advanced/all_networks.ts b/examples/typescript/clients/advanced/all_networks.ts
index 620819f476..01c8b78fad 100644
--- a/examples/typescript/clients/advanced/all_networks.ts
+++ b/examples/typescript/clients/advanced/all_networks.ts
@@ -5,13 +5,15 @@
* optional chain configuration via environment variables.
*
* New chain support should be added here in alphabetic order by network prefix
- * (e.g., "eip155" before "solana").
+ * (e.g., "eip155" before "solana" before "stellar").
*/
import { config } from "dotenv";
import { x402Client, wrapFetchWithPayment, x402HTTPClient } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { ExactSvmScheme } from "@x402/svm/exact/client";
+import { ExactStellarScheme } from "@x402/stellar/exact/client";
+import { createEd25519Signer } from "@x402/stellar";
import { privateKeyToAccount } from "viem/accounts";
import { createKeyPairSignerFromBytes } from "@solana/kit";
import { base58 } from "@scure/base";
@@ -21,6 +23,7 @@ config();
// Configuration - optional per network
const evmPrivateKey = process.env.EVM_PRIVATE_KEY as `0x${string}` | undefined;
const svmPrivateKey = process.env.SVM_PRIVATE_KEY as string | undefined;
+const stellarPrivateKey = process.env.STELLAR_PRIVATE_KEY as string | undefined;
const baseURL = process.env.RESOURCE_SERVER_URL || "http://localhost:4021";
const endpointPath = process.env.ENDPOINT_PATH || "/weather";
const url = `${baseURL}${endpointPath}`;
@@ -31,8 +34,10 @@ const url = `${baseURL}${endpointPath}`;
*/
async function main(): Promise {
// Validate at least one private key is provided
- if (!evmPrivateKey && !svmPrivateKey) {
- console.error("❌ At least one of EVM_PRIVATE_KEY or SVM_PRIVATE_KEY is required");
+ if (!evmPrivateKey && !svmPrivateKey && !stellarPrivateKey) {
+ console.error(
+ "❌ At least one of EVM_PRIVATE_KEY, SVM_PRIVATE_KEY, or STELLAR_PRIVATE_KEY is required",
+ );
process.exit(1);
}
@@ -53,6 +58,13 @@ async function main(): Promise {
console.log(`Initialized SVM account: ${svmSigner.address}`);
}
+ // Register Stellar scheme if private key is provided
+ if (stellarPrivateKey) {
+ const stellarSigner = createEd25519Signer(stellarPrivateKey);
+ client.register("stellar:*", new ExactStellarScheme(stellarSigner));
+ console.log(`Initialized Stellar account: ${stellarSigner.address}`);
+ }
+
// Wrap fetch with payment handling
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
@@ -63,15 +75,10 @@ async function main(): Promise {
const body = await response.json();
console.log("Response body:", body);
- // Extract payment response if present
- if (response.ok) {
- const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(name =>
- response.headers.get(name),
- );
- console.log("\nPayment response:", JSON.stringify(paymentResponse, null, 2));
- } else {
- console.log(`\nNo payment settled (response status: ${response.status})`);
- }
+ const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(name =>
+ response.headers.get(name),
+ );
+ console.log("\nPayment response:", JSON.stringify(paymentResponse, null, 2));
}
main().catch(error => {
diff --git a/examples/typescript/clients/advanced/builder-pattern.ts b/examples/typescript/clients/advanced/builder-pattern.ts
index 67c098145b..0a91bd122e 100644
--- a/examples/typescript/clients/advanced/builder-pattern.ts
+++ b/examples/typescript/clients/advanced/builder-pattern.ts
@@ -56,12 +56,10 @@ export async function runBuilderPatternExample(
console.log("✅ Request completed\n");
console.log("Response body:", body);
- if (response.ok) {
- const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(name =>
- response.headers.get(name),
- );
- if (paymentResponse) {
- console.log("\n💰 Payment Details:", paymentResponse);
- }
+ const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(name =>
+ response.headers.get(name),
+ );
+ if (paymentResponse) {
+ console.log("\n💰 Payment Details:", paymentResponse);
}
}
diff --git a/examples/typescript/clients/advanced/package.json b/examples/typescript/clients/advanced/package.json
index fa8b4a4b4d..66b2fc1a26 100644
--- a/examples/typescript/clients/advanced/package.json
+++ b/examples/typescript/clients/advanced/package.json
@@ -18,6 +18,7 @@
"@scure/base": "^1.2.6",
"@x402/evm": "workspace:*",
"@x402/svm": "workspace:*",
+ "@x402/stellar": "workspace:*",
"@x402/fetch": "workspace:*",
"dotenv": "^16.4.7",
"viem": "^2.39.0",
diff --git a/examples/typescript/clients/axios/index.ts b/examples/typescript/clients/axios/index.ts
index b3cfed0f18..b0d96c249d 100644
--- a/examples/typescript/clients/axios/index.ts
+++ b/examples/typescript/clients/axios/index.ts
@@ -39,14 +39,10 @@ async function main(): Promise {
const body = response.data;
console.log("Response body:", body);
- if (response.status < 400) {
- const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(
- name => response.headers[name.toLowerCase()],
- );
- console.log("\nPayment response:", paymentResponse);
- } else {
- console.log(`\nNo payment settled (response status: ${response.status})`);
- }
+ const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(
+ name => response.headers[name.toLowerCase()],
+ );
+ console.log("\nPayment response:", paymentResponse);
}
main().catch(error => {
diff --git a/examples/typescript/clients/fetch/index.ts b/examples/typescript/clients/fetch/index.ts
index ef5670feb8..d51b341025 100644
--- a/examples/typescript/clients/fetch/index.ts
+++ b/examples/typescript/clients/fetch/index.ts
@@ -35,17 +35,16 @@ async function main(): Promise {
console.log(`Making request to: ${url}\n`);
const response = await fetchWithPayment(url, { method: "GET" });
- const body = await response.json();
+ const contentType = response.headers.get("content-type") ?? "";
+ const body = contentType.includes("application/json")
+ ? await response.json()
+ : await response.text();
console.log("Response body:", body);
- if (response.ok) {
- const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(name =>
- response.headers.get(name),
- );
- console.log("\nPayment response:", JSON.stringify(paymentResponse, null, 2));
- } else {
- console.log(`\nNo payment settled (response status: ${response.status})`);
- }
+ const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(name =>
+ response.headers.get(name),
+ );
+ console.log("\nPayment response:", JSON.stringify(paymentResponse, null, 2));
}
main().catch(error => {
diff --git a/examples/typescript/clients/fetch/package.json b/examples/typescript/clients/fetch/package.json
index dd487128e8..b7dc80299d 100644
--- a/examples/typescript/clients/fetch/package.json
+++ b/examples/typescript/clients/fetch/package.json
@@ -23,6 +23,7 @@
},
"devDependencies": {
"@eslint/js": "^9.24.0",
+ "@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^8.29.1",
"@typescript-eslint/parser": "^8.29.1",
"eslint": "^9.24.0",
diff --git a/examples/typescript/clients/offer-receipt/.env-local b/examples/typescript/clients/offer-receipt/.env-local
new file mode 100644
index 0000000000..181329cc56
--- /dev/null
+++ b/examples/typescript/clients/offer-receipt/.env-local
@@ -0,0 +1,13 @@
+# x402 Receipt Attestation Example Configuration
+
+# EVM private key for making payments (required)
+EVM_PRIVATE_KEY=0x_your_evm_private_key_here
+
+# SVM private key for Solana payments (required)
+SVM_PRIVATE_KEY=your_solana_private_key_here
+
+# Resource server URL (optional, defaults to localhost:4021)
+RESOURCE_SERVER_URL=http://localhost:4021
+
+# Endpoint path (optional, defaults to /weather)
+ENDPOINT_PATH=/weather
diff --git a/examples/typescript/clients/offer-receipt/.prettierignore b/examples/typescript/clients/offer-receipt/.prettierignore
new file mode 100644
index 0000000000..3049672b5c
--- /dev/null
+++ b/examples/typescript/clients/offer-receipt/.prettierignore
@@ -0,0 +1,8 @@
+docs/
+dist/
+node_modules/
+coverage/
+.github/
+src/client
+**/**/*.json
+*.md
diff --git a/examples/typescript/clients/offer-receipt/.prettierrc b/examples/typescript/clients/offer-receipt/.prettierrc
new file mode 100644
index 0000000000..ffb416b74b
--- /dev/null
+++ b/examples/typescript/clients/offer-receipt/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": false,
+ "trailingComma": "all",
+ "bracketSpacing": true,
+ "arrowParens": "avoid",
+ "printWidth": 100,
+ "proseWrap": "never"
+}
diff --git a/examples/typescript/clients/offer-receipt/README.md b/examples/typescript/clients/offer-receipt/README.md
new file mode 100644
index 0000000000..05eecfe6c3
--- /dev/null
+++ b/examples/typescript/clients/offer-receipt/README.md
@@ -0,0 +1,109 @@
+# Offer/Receipt Client Example
+
+Demonstrates how clients extract and verify signed offers and receipts from x402 payment flows.
+
+For background on why receipts matter, payload structure, and security considerations, see the [Offer/Receipt Extension README](../../../../typescript/packages/extensions/src/offer-receipt/README.md).
+
+## Use Cases for Signed Receipts/Offers
+
+- Verified user reviews ("Verified Purchase" badges)
+- Audit trails and compliance records
+- Dispute resolution evidence
+- Agent memory (AI agents proving past interactions)
+
+## Quick Start
+
+1. Install dependencies from the typescript examples root:
+
+```bash
+cd ../../
+pnpm install && pnpm build
+cd clients/offer-receipt
+```
+
+2. Copy `.env-local` to `.env` and configure:
+
+```bash
+cp .env-local .env
+```
+
+Required environment variables:
+- `EVM_PRIVATE_KEY` - Private key for EVM payments
+- `SVM_PRIVATE_KEY` - Private key for Solana payments (base58)
+- `RESOURCE_SERVER_URL` - Server URL (default: `http://localhost:4021`)
+- `ENDPOINT_PATH` - Endpoint path (default: `/weather`)
+
+3. Run the example:
+
+```bash
+pnpm start
+```
+
+## What This Example Shows
+
+The example uses the raw flow (not the wrapper) for visibility into each step:
+
+1. Make initial request → receive 402 with signed offers
+2. Extract and decode offers to inspect payment options
+3. Verify offer signatures and select a verified offer
+4. Find matching `accepts[]` entry for selected offer
+5. Create payment and retry the request
+6. Extract signed receipt from success response
+7. Verify receipt signature
+8. Verify receipt payload matches the offer
+
+See [index.ts](./index.ts) for the full implementation with detailed comments.
+
+## Signature Verification
+
+The extraction functions (`extractReceiptPayload`, `extractOfferPayload`) decode payloads without verifying signatures. To verify that offers and receipts are authentic, use the verification functions from `@x402/extensions/offer-receipt`:
+
+- `verifyOfferSignatureJWS` / `verifyReceiptSignatureJWS` - For JWS signatures
+- `verifyOfferSignatureEIP712` / `verifyReceiptSignatureEIP712` - For EIP-712 signatures
+
+See [index.ts](./index.ts) for usage examples.
+
+### Supported Key Types (JWS)
+
+- **Ed25519** - EdDSA signatures
+- **secp256k1** - ES256K signatures (Ethereum-compatible)
+- **secp256r1** - ES256 signatures (NIST P-256)
+
+### Supported DID Methods (JWS)
+
+The `kid` header identifies the signing key. These DID methods support automatic key extraction:
+
+| Method | Description | Example |
+|------------|------------------------------------------|----------------------------------|
+| `did:key` | Self-contained key in the DID | `did:key:z6Mk...` |
+| `did:jwk` | Base64url-encoded JWK | `did:jwk:eyJrdH...` |
+| `did:web` | Fetches key from `.well-known/did.json` | `did:web:api.example.com#key-1` |
+
+For other DID methods, provide the public key explicitly to the verification function.
+
+### EIP-712 Verification
+
+EIP-712 signatures don't use DIDs - the signer address is recovered directly from the signature.
+
+## Key-to-Domain Binding
+
+Signature verification proves the offer/receipt was signed by a specific key. To fully trust it, verify the key is authorized to sign for the resource's domain via:
+
+- `did:web` document at `https:///.well-known/did.json`
+- DNS TXT record binding the DID to the domain
+- On-chain attestation (e.g., OMATrust key binding attestation)
+
+See: [Extension Specification §4.5.1](../../../../specs/extensions/extension-offer-and-receipt.md)
+
+## Security Considerations
+
+1. **Private Key Management**: Loading private keys from environment variables is for demonstration only. In production, use secure key management (HSM, KMS, hardware wallets).
+
+2. **Key Separation**: The payment signing key SHOULD be different from keys controlling wallets with significant funds.
+
+3. **Key-to-Domain Binding** (for servers): See [Extension Specification §4.5.1](../../../../specs/extensions/extension-offer-and-receipt.md)
+
+## Related
+
+- [Offer/Receipt Extension](../../../../typescript/packages/extensions/src/offer-receipt/) - Types, signing utilities, client functions
+- [Extension Specification](../../../../specs/extensions/extension-offer-and-receipt.md) - Full protocol spec
diff --git a/examples/typescript/clients/offer-receipt/eslint.config.js b/examples/typescript/clients/offer-receipt/eslint.config.js
new file mode 100644
index 0000000000..ca28b5c47f
--- /dev/null
+++ b/examples/typescript/clients/offer-receipt/eslint.config.js
@@ -0,0 +1,72 @@
+import js from "@eslint/js";
+import ts from "@typescript-eslint/eslint-plugin";
+import tsParser from "@typescript-eslint/parser";
+import prettier from "eslint-plugin-prettier";
+import jsdoc from "eslint-plugin-jsdoc";
+import importPlugin from "eslint-plugin-import";
+
+export default [
+ {
+ ignores: ["dist/**", "node_modules/**"],
+ },
+ {
+ files: ["**/*.ts"],
+ languageOptions: {
+ parser: tsParser,
+ sourceType: "module",
+ ecmaVersion: 2020,
+ globals: {
+ process: "readonly",
+ __dirname: "readonly",
+ module: "readonly",
+ require: "readonly",
+ Buffer: "readonly",
+ exports: "readonly",
+ setTimeout: "readonly",
+ clearTimeout: "readonly",
+ setInterval: "readonly",
+ clearInterval: "readonly",
+ },
+ },
+ plugins: {
+ "@typescript-eslint": ts,
+ prettier: prettier,
+ jsdoc: jsdoc,
+ import: importPlugin,
+ },
+ rules: {
+ ...ts.configs.recommended.rules,
+ "import/first": "error",
+ "prettier/prettier": "error",
+ "@typescript-eslint/member-ordering": "error",
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }],
+ "jsdoc/tag-lines": ["error", "any", { startLines: 1 }],
+ "jsdoc/check-alignment": "error",
+ "jsdoc/no-undefined-types": "off",
+ "jsdoc/check-param-names": "error",
+ "jsdoc/check-tag-names": "error",
+ "jsdoc/check-types": "error",
+ "jsdoc/implements-on-classes": "error",
+ "jsdoc/require-description": "error",
+ "jsdoc/require-jsdoc": [
+ "error",
+ {
+ require: {
+ FunctionDeclaration: true,
+ MethodDefinition: true,
+ ClassDeclaration: true,
+ ArrowFunctionExpression: false,
+ FunctionExpression: false,
+ },
+ },
+ ],
+ "jsdoc/require-param": "error",
+ "jsdoc/require-param-description": "error",
+ "jsdoc/require-param-type": "off",
+ "jsdoc/require-returns": "error",
+ "jsdoc/require-returns-description": "error",
+ "jsdoc/require-returns-type": "off",
+ "jsdoc/require-hyphen-before-param-description": ["error", "always"],
+ },
+ },
+];
diff --git a/examples/typescript/clients/offer-receipt/index.ts b/examples/typescript/clients/offer-receipt/index.ts
new file mode 100644
index 0000000000..be46cd1ce5
--- /dev/null
+++ b/examples/typescript/clients/offer-receipt/index.ts
@@ -0,0 +1,250 @@
+/**
+ * x402 Receipt Attestation Client Example
+ *
+ * Demonstrates extracting signed offers and receipts from x402 payment flows.
+ * Uses the raw flow for visibility into what's happening at each step.
+ */
+
+import { config } from "dotenv";
+import { x402Client, x402HTTPClient, type PaymentRequired } from "@x402/fetch";
+import { registerExactEvmScheme } from "@x402/evm/exact/client";
+import { registerExactSvmScheme } from "@x402/svm/exact/client";
+import { privateKeyToAccount } from "viem/accounts";
+import { createKeyPairSignerFromBytes } from "@solana/kit";
+import { base58 } from "@scure/base";
+import {
+ extractOffersFromPaymentRequired,
+ decodeSignedOffers,
+ findAcceptsObjectFromSignedOffer,
+ extractReceiptFromResponse,
+ extractReceiptPayload,
+ verifyReceiptMatchesOffer,
+ verifyOfferSignatureJWS,
+ verifyOfferSignatureEIP712,
+ verifyReceiptSignatureJWS,
+ verifyReceiptSignatureEIP712,
+ isJWSSignedOffer,
+ isJWSSignedReceipt,
+} from "@x402/extensions/offer-receipt";
+
+config();
+
+const evmPrivateKey = process.env.EVM_PRIVATE_KEY as `0x${string}`;
+const svmPrivateKey = process.env.SVM_PRIVATE_KEY as string;
+const baseURL = process.env.RESOURCE_SERVER_URL || "http://localhost:4021";
+const endpointPath = process.env.ENDPOINT_PATH || "/weather";
+const url = `${baseURL}${endpointPath}`;
+
+/**
+ * Main entry point demonstrating x402 payment flow with offer-receipt extension
+ *
+ * @returns - Promise that resolves when the example completes
+ */
+async function main(): Promise {
+ // Set up payment client
+ const evmSigner = privateKeyToAccount(evmPrivateKey);
+ const svmSigner = await createKeyPairSignerFromBytes(base58.decode(svmPrivateKey));
+
+ const client = new x402Client();
+ registerExactEvmScheme(client, { signer: evmSigner });
+ registerExactSvmScheme(client, { signer: svmSigner });
+
+ const httpClient = new x402HTTPClient(client);
+
+ // =========================================================================
+ // Step 1: Initial request (expect 402)
+ // =========================================================================
+ console.log(`Requesting: ${url}`);
+ const initialResponse = await fetch(url, { method: "GET" });
+
+ if (initialResponse.status !== 402) {
+ const body = await initialResponse.json();
+ console.log("Response:", body);
+ return;
+ }
+
+ // =========================================================================
+ // Step 2: Extract and decode signed offers from 402 response
+ // =========================================================================
+ const paymentRequiredBody = (await initialResponse.json()) as PaymentRequired;
+ const getHeader = (name: string) => initialResponse.headers.get(name);
+ const paymentRequired = httpClient.getPaymentRequiredResponse(getHeader, paymentRequiredBody);
+
+ const signedOffers = extractOffersFromPaymentRequired(paymentRequired);
+
+ if (signedOffers.length === 0) {
+ console.log("No signed offers (server may not have offer signing enabled)");
+ return;
+ }
+
+ // Decode all offers to inspect their payloads
+ const decodedOffers = decodeSignedOffers(signedOffers);
+
+ console.log(`\nSigned Offers (${decodedOffers.length}):`);
+ decodedOffers.forEach((d, i) => {
+ console.log(` [${i}] ${d.scheme} on ${d.network}: ${d.amount} to ${d.payTo}`);
+ });
+
+ // =========================================================================
+ // Step 3: Verify offer signatures and select a verified offer
+ // =========================================================================
+ // Only consider offers that pass signature verification
+ console.log("\nVerifying offer signatures...");
+
+ let selected = null;
+ for (const decoded of decodedOffers) {
+ try {
+ if (isJWSSignedOffer(decoded.signedOffer)) {
+ await verifyOfferSignatureJWS(decoded.signedOffer);
+ console.log(` [${decoded.acceptIndex}] JWS: ✓ Valid`);
+ } else {
+ const { signer } = await verifyOfferSignatureEIP712(decoded.signedOffer);
+ console.log(` [${decoded.acceptIndex}] EIP-712: ✓ Valid (signer: ${signer})`);
+ }
+ selected = decoded;
+ break;
+ } catch (err) {
+ console.log(
+ ` [${decoded.acceptIndex}] ✗ FAILED - ${err instanceof Error ? err.message : err}`,
+ );
+ }
+ }
+
+ if (!selected) {
+ console.log("\nNo offers passed signature verification");
+ return;
+ }
+
+ // =========================================================================
+ // Step 4: Find matching accepts entry for selected offer
+ // =========================================================================
+ const matchingAccept = findAcceptsObjectFromSignedOffer(selected, paymentRequired.accepts);
+
+ if (!matchingAccept) {
+ console.log("\nNo matching accepts[] entry for signed offer");
+ return;
+ }
+
+ console.log(`\nSelected: ${selected.scheme} on ${selected.network}`);
+
+ // =========================================================================
+ // Step 5: Create payment and retry
+ // =========================================================================
+ console.log("Making payment...");
+
+ const paymentPayload = await client.createPaymentPayload(paymentRequired);
+ const paymentHeaders = httpClient.encodePaymentSignatureHeader(paymentPayload);
+
+ const paidResponse = await fetch(url, {
+ method: "GET",
+ headers: paymentHeaders,
+ });
+
+ if (!paidResponse.ok) {
+ console.error(`Payment failed: ${paidResponse.status}`);
+ return;
+ }
+
+ const responseBody = await paidResponse.json();
+ console.log("Response:", responseBody);
+
+ const paymentResponse = httpClient.getPaymentSettleResponse(name =>
+ paidResponse.headers.get(name),
+ );
+ console.log("\nPayment response:", JSON.stringify(paymentResponse, null, 2));
+
+ // =========================================================================
+ // Step 6: Extract signed receipt from success response
+ // =========================================================================
+ const signedReceipt = extractReceiptFromResponse(paidResponse);
+
+ if (signedReceipt) {
+ const receiptPayload = extractReceiptPayload(signedReceipt);
+ console.log(`\nSigned Receipt:`);
+ console.log(` format: ${signedReceipt.format}`);
+ console.log(` resourceUrl: ${receiptPayload.resourceUrl}`);
+ console.log(` payer: ${receiptPayload.payer}`);
+ console.log(` network: ${receiptPayload.network}`);
+ console.log(` issuedAt: ${new Date(receiptPayload.issuedAt * 1000).toISOString()}`);
+ if (receiptPayload.transaction) {
+ console.log(` transaction: ${receiptPayload.transaction}`);
+ }
+ } else {
+ console.log("\nNo signed receipt (server may not have receipt signing enabled)");
+ }
+
+ // =========================================================================
+ // Step 7: Verify receipt signature
+ // =========================================================================
+ if (signedReceipt) {
+ console.log("\nReceipt Signature Verification:");
+ try {
+ if (isJWSSignedReceipt(signedReceipt)) {
+ const verifiedPayload = await verifyReceiptSignatureJWS(signedReceipt);
+ console.log(` JWS: ✓ Valid - payer: ${verifiedPayload.payer}`);
+ } else {
+ const { signer } = await verifyReceiptSignatureEIP712(signedReceipt);
+ console.log(` EIP-712: ✓ Valid - signer: ${signer}`);
+ }
+ } catch (err) {
+ console.log(` ✗ FAILED - ${err instanceof Error ? err.message : err}`);
+ }
+ }
+
+ // =========================================================================
+ // Step 8: Verify receipt matches offer (payload field verification)
+ // =========================================================================
+
+ if (signedReceipt) {
+ const payerAddresses = [evmSigner.address, svmSigner.address];
+ const verified = verifyReceiptMatchesOffer(signedReceipt, selected, payerAddresses);
+
+ console.log(`\nPayload Verification: ${verified ? "✓ PASSED" : "✗ FAILED"}`);
+
+ if (!verified) {
+ const receiptPayload = extractReceiptPayload(signedReceipt);
+ console.log(
+ ` resourceUrl: ${receiptPayload.resourceUrl === selected.resourceUrl ? "✓" : "✗"}`,
+ );
+ console.log(` network: ${receiptPayload.network === selected.network ? "✓" : "✗"}`);
+ const payerMatch = payerAddresses.some(
+ addr => receiptPayload.payer.toLowerCase() === addr.toLowerCase(),
+ );
+ console.log(` payer: ${payerMatch ? "✓" : "✗"}`);
+ const issuedRecently = Math.floor(Date.now() / 1000) - receiptPayload.issuedAt < 3600;
+ console.log(` recent: ${issuedRecently ? "✓" : "✗"}`);
+ }
+ }
+
+ // =========================================================================
+ // Step 9: Summary - Proofs available for downstream use
+ // =========================================================================
+ console.log("\n--- Proofs Available ---");
+ if (signedReceipt) {
+ console.log("✓ x402-receipt (proves payment received AND service delivered)");
+ }
+ if (selected) {
+ console.log("✓ x402-offer (proves server committed to payment terms)");
+ }
+
+ // -------------------------------------------------------------------------
+ // Integration Point: Trust Systems (OMATrust, PEAC, etc.)
+ // -------------------------------------------------------------------------
+ //
+ // This is where integration with downstream systems like OMATrust and PEAC
+ // can reside. These systems are planning to support x402 signed receipts
+ // and offers for use cases like:
+ //
+ // - Verified user reviews ("Verified Purchase" badges)
+ // - Audit trails and compliance records
+ // - Dispute resolution evidence
+ // - Agent memory proofs
+ //
+ // Integration examples will be added in a future update.
+ // -------------------------------------------------------------------------
+}
+
+main().catch(error => {
+ console.error(error?.response?.data?.error ?? error);
+ process.exit(1);
+});
diff --git a/examples/typescript/clients/offer-receipt/package.json b/examples/typescript/clients/offer-receipt/package.json
new file mode 100644
index 0000000000..f523c4e2b5
--- /dev/null
+++ b/examples/typescript/clients/offer-receipt/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "@x402/offer-receipt-example",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "start": "tsx index.ts",
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
+ "lint": "eslint . --ext .ts --fix",
+ "lint:check": "eslint . --ext .ts"
+ },
+ "dependencies": {
+ "@scure/base": "^1.2.6",
+ "@solana/kit": "^2.1.1",
+ "@x402/evm": "workspace:*",
+ "@x402/extensions": "workspace:*",
+ "@x402/fetch": "workspace:*",
+ "@x402/svm": "workspace:*",
+ "dotenv": "^16.4.7",
+ "viem": "^2.39.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.24.0",
+ "@types/node": "^20.0.0",
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
+ "@typescript-eslint/parser": "^8.29.1",
+ "eslint": "^9.24.0",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsdoc": "^50.6.9",
+ "eslint-plugin-prettier": "^5.2.6",
+ "prettier": "3.5.2",
+ "tsx": "^4.7.0",
+ "typescript": "^5.3.0"
+ }
+}
diff --git a/examples/typescript/clients/offer-receipt/tsconfig.json b/examples/typescript/clients/offer-receipt/tsconfig.json
new file mode 100644
index 0000000000..78f9479b1b
--- /dev/null
+++ b/examples/typescript/clients/offer-receipt/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ES2020",
+ "moduleResolution": "bundler",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "resolveJsonModule": true,
+ "baseUrl": ".",
+ "types": ["node"]
+ },
+ "include": ["index.ts"]
+}
diff --git a/examples/typescript/clients/payment-identifier/index.ts b/examples/typescript/clients/payment-identifier/index.ts
index c11c80201a..51196ffbaf 100644
--- a/examples/typescript/clients/payment-identifier/index.ts
+++ b/examples/typescript/clients/payment-identifier/index.ts
@@ -65,13 +65,11 @@ async function main(): Promise {
console.log(`Response (${duration1}ms):`, JSON.stringify(body1, null, 2));
- if (response1.ok) {
- const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(name =>
- response1.headers.get(name),
- );
- if (paymentResponse) {
- console.log(`\n💰 Payment settled on ${paymentResponse.network}`);
- }
+ const paymentResponse1 = new x402HTTPClient(client).getPaymentSettleResponse(name =>
+ response1.headers.get(name),
+ );
+ if (paymentResponse1) {
+ console.log(`\n💰 Payment settled on ${paymentResponse1.network}`);
}
// Second request - same payment ID, should return from cache
@@ -88,15 +86,13 @@ async function main(): Promise {
console.log(`Response (${duration2}ms):`, JSON.stringify(body2, null, 2));
- if (response2.ok) {
- const paymentResponse = new x402HTTPClient(client).getPaymentSettleResponse(name =>
- response2.headers.get(name),
- );
- if (paymentResponse) {
- console.log(`\n💰 Payment settled (unexpected - should have been cached)`);
- } else {
- console.log(`\n✅ No payment processed - response served from cache!`);
- }
+ const paymentResponse2 = new x402HTTPClient(client).getPaymentSettleResponse(name =>
+ response2.headers.get(name),
+ );
+ if (paymentResponse2) {
+ console.log(`\n💰 Payment settled (unexpected - should have been cached)`);
+ } else {
+ console.log(`\n✅ No payment processed - response served from cache!`);
}
// Summary
diff --git a/examples/typescript/clients/sign-in-with-x/README.md b/examples/typescript/clients/sign-in-with-x/README.md
index e5dc2f0db5..08db11a13a 100644
--- a/examples/typescript/clients/sign-in-with-x/README.md
+++ b/examples/typescript/clients/sign-in-with-x/README.md
@@ -1,6 +1,8 @@
# Sign-In-With-X (SIWX) Client Example
-Client demonstrating how to use Sign-In-With-X authentication with x402, allowing wallet signatures to prove prior payment and skip re-payment on subsequent requests.
+Client demonstrating both SIWX flows supported by x402:
+- Auth-only access for routes that require a wallet signature but no payment
+- Paid-once access where SIWX proves a wallet has already paid
```typescript
import { x402Client, x402HTTPClient, wrapFetchWithPayment } from "@x402/fetch";
@@ -24,19 +26,22 @@ const httpClient = new x402HTTPClient(client).onPaymentRequired(
const fetchWithPayment = wrapFetchWithPayment(fetch, httpClient);
-// First request: pays for access
-const response1 = await fetchWithPayment("http://localhost:4021/weather");
+// Auth-only route: 402 challenge -> sign -> retry, no payment
+const profile = await fetchWithPayment("http://localhost:4021/profile");
-// Second request: SIWX proves we already paid, no payment needed
-const response2 = await fetchWithPayment("http://localhost:4021/weather");
+// Paid route: first request pays for access
+const weather1 = await fetchWithPayment("http://localhost:4021/weather");
+
+// Paid route: second request uses SIWX to prove prior payment
+const weather2 = await fetchWithPayment("http://localhost:4021/weather");
```
## How It Works
-1. **First request** — Client pays for resource access
-2. **Server remembers** — Payment is recorded against wallet address
-3. **Second request** — Client signs SIWX message proving wallet ownership
-4. **Server grants access** — No payment required, authenticated via signature
+1. **Auth-only route** — Client receives a SIWX challenge, signs it, and retries without payment
+2. **Paid route, first request** — Client pays for resource access
+3. **Server remembers** — Payment is recorded against wallet address
+4. **Paid route, later request** — Client signs SIWX message proving wallet ownership instead of paying again
## Prerequisites
@@ -59,7 +64,7 @@ and provide at least one private key:
- `SVM_PRIVATE_KEY` - (Optional) Solana private key for SVM payments and SIWX authentication
- `RESOURCE_SERVER_URL` - (Optional) Server URL (defaults to `http://localhost:4021`)
-**Note:** At least one private key (EVM or SVM) is required. SIWX supports both EVM signatures (EIP-191) and Solana signatures (Ed25519), so authentication works with either key type.
+**Note:** At least one private key (EVM or SVM) is required. The `/profile` auth-only example and the paid `/weather` and `/joke` routes all work with either signer type.
2. Install and build from typescript examples root:
@@ -87,8 +92,13 @@ pnpm start
```
Client EVM address: 0x...
+Client SVM address: ...
Server: http://localhost:4021
+--- /profile (auth-only, no payment) ---
+ ✓ Authenticated via SIWX (no payment required)
+ Response: { address: '0x...', data: 'Your profile data' }
+
--- /weather ---
1. First request...
✓ Paid via payment settlement
@@ -110,4 +120,6 @@ Server: http://localhost:4021
2. Second request...
✓ Authenticated via SIWX (previously paid)
...
-```
\ No newline at end of file
+
+Done. /profile used auth-only SIWX. /weather and /joke used payment + SIWX.
+```
diff --git a/examples/typescript/clients/sign-in-with-x/index.ts b/examples/typescript/clients/sign-in-with-x/index.ts
index 4659510f82..a8b1b31cfe 100644
--- a/examples/typescript/clients/sign-in-with-x/index.ts
+++ b/examples/typescript/clients/sign-in-with-x/index.ts
@@ -79,8 +79,8 @@ async function demonstrateResource(path: string): Promise {
const response1 = await fetchWithPayment(url);
const body1 = await response1.json();
+ logPaymentResponse(response1);
if (response1.ok) {
- logPaymentResponse(response1);
console.log(" Response:", body1);
} else if (body1.error) {
console.log(" ✗ Payment failed:", body1.details || body1.error);
@@ -91,8 +91,8 @@ async function demonstrateResource(path: string): Promise {
const response2 = await fetchWithPayment(url);
const body2 = await response2.json();
+ const hasPayment = logPaymentResponse(response2);
if (response2.ok) {
- const hasPayment = logPaymentResponse(response2);
if (!hasPayment) {
console.log(" ✓ Authenticated via SIWX (previously paid)");
}
@@ -102,6 +102,27 @@ async function demonstrateResource(path: string): Promise {
}
}
+/**
+ * Demonstrates auth-only SIWX flow (no payment required).
+ * The client hook handles the 402 → sign → retry cycle automatically.
+ */
+async function demonstrateAuthOnly(): Promise {
+ const url = `${baseURL}/profile`;
+ console.log("\n--- /profile (auth-only, no payment) ---");
+
+ // fetchWithPayment handles auth-only routes the same way as paid routes:
+ // 402 → SIWX client hook signs the challenge → retry with signature
+ const response = await fetchWithPayment(url);
+ const body = await response.json();
+
+ if (response.ok) {
+ console.log(" ✓ Authenticated via SIWX (no payment required)");
+ console.log(" Response:", body);
+ } else {
+ console.log(" ✗ Auth failed:", body);
+ }
+}
+
/**
* Main entry point - demonstrates SIWX authentication flow.
*/
@@ -114,6 +135,9 @@ async function main(): Promise {
}
console.log(`Server: ${baseURL}`);
+ // Auth-only: SIWX signature without payment
+ await demonstrateAuthOnly();
+
await demonstrateResource("/weather");
// Small delay to avoid facilitator race condition with rapid payments
@@ -121,7 +145,7 @@ async function main(): Promise {
await demonstrateResource("/joke");
- console.log("\nDone. Each resource required payment once, then SIWX auth worked.");
+ console.log("\nDone. /profile used auth-only SIWX. /weather and /joke used payment + SIWX.");
}
main().catch(err => {
diff --git a/examples/typescript/facilitator/.env-local b/examples/typescript/facilitator/.env-local
index 7534548c3f..b9b7a2492b 100644
--- a/examples/typescript/facilitator/.env-local
+++ b/examples/typescript/facilitator/.env-local
@@ -1,3 +1,4 @@
PORT=
EVM_PRIVATE_KEY=
+STELLAR_PRIVATE_KEY=
SVM_PRIVATE_KEY=
\ No newline at end of file
diff --git a/examples/typescript/facilitator/advanced/README.md b/examples/typescript/facilitator/advanced/README.md
index d357b0d478..a0ab84758e 100644
--- a/examples/typescript/facilitator/advanced/README.md
+++ b/examples/typescript/facilitator/advanced/README.md
@@ -8,6 +8,7 @@ Express.js facilitator service demonstrating advanced x402 patterns including al
- pnpm v10 (install via [pnpm.io/installation](https://pnpm.io/installation))
- EVM private key with Base Sepolia ETH for transaction fees
- SVM private key with Solana Devnet SOL for transaction fees
+- Stellar private key with testnet XLM for transaction fees (fund via [Stellar Laboratory](https://lab.stellar.org/account/create) ➡️ Generate keypair ➡️ Fund account with Friendbot)
## Setup
@@ -21,6 +22,7 @@ and fill required environment variables:
- `EVM_PRIVATE_KEY` - Ethereum private key
- `SVM_PRIVATE_KEY` - Solana private key
+- `STELLAR_PRIVATE_KEY` - Stellar secret key (starts with `S`)
- `PORT` - Server port (optional, defaults to 4022)
2. Install and build all packages from the typescript examples root:
@@ -42,10 +44,10 @@ pnpm dev:bazaar # Bazaar discovery extension
Each example demonstrates a specific advanced pattern:
-| Example | Command | Description |
-| --- | --- | --- |
+| Example | Command | Description |
+| -------------- | ----------------------- | -------------------------------------------------------- |
| `all-networks` | `pnpm dev:all-networks` | All supported networks with optional chain configuration |
-| `bazaar` | `pnpm dev:bazaar` | Bazaar discovery extension for cataloging x402 resources |
+| `bazaar` | `pnpm dev:bazaar` | Bazaar discovery extension for cataloging x402 resources |
## API Endpoints
@@ -68,12 +70,21 @@ Returns payment schemes and networks this facilitator supports.
"extra": {
"feePayer": "..."
}
+ },
+ {
+ "x402Version": 2,
+ "scheme": "exact",
+ "network": "stellar:testnet",
+ "extra": {
+ "areFeesSponsored": true
+ }
}
],
"extensions": [],
"signers": {
"eip155": ["0x..."],
- "solana": ["..."]
+ "solana": ["..."],
+ "stellar": ["G..."]
}
}
```
@@ -229,3 +240,5 @@ Networks use [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/cai
- `eip155:8453` — Base Mainnet
- `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` — Solana Devnet
- `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` — Solana Mainnet
+- `stellar:testnet` — Stellar Testnet
+- `stellar:pubnet` — Stellar Mainnet
diff --git a/examples/typescript/facilitator/advanced/all_networks.ts b/examples/typescript/facilitator/advanced/all_networks.ts
index 3692fd9b17..1513417b41 100644
--- a/examples/typescript/facilitator/advanced/all_networks.ts
+++ b/examples/typescript/facilitator/advanced/all_networks.ts
@@ -5,7 +5,7 @@
* optional chain configuration via environment variables.
*
* New chain support should be added here in alphabetic order by network prefix
- * (e.g., "eip155" before "solana").
+ * (e.g., "eip155" before "solana" before "stellar").
*/
import { base58 } from "@scure/base";
@@ -21,6 +21,8 @@ import { toFacilitatorEvmSigner } from "@x402/evm";
import { ExactEvmScheme } from "@x402/evm/exact/facilitator";
import { toFacilitatorSvmSigner } from "@x402/svm";
import { ExactSvmScheme } from "@x402/svm/exact/facilitator";
+import { createEd25519Signer } from "@x402/stellar";
+import { ExactStellarScheme } from "@x402/stellar/exact/facilitator";
import dotenv from "dotenv";
import express from "express";
import { createWalletClient, http, publicActions } from "viem";
@@ -35,11 +37,12 @@ const PORT = process.env.PORT || "4022";
// Configuration - optional per network
const evmPrivateKey = process.env.EVM_PRIVATE_KEY as `0x${string}` | undefined;
const svmPrivateKey = process.env.SVM_PRIVATE_KEY as string | undefined;
+const stellarPrivateKey = process.env.STELLAR_PRIVATE_KEY as string | undefined;
// Validate at least one private key is provided
-if (!evmPrivateKey && !svmPrivateKey) {
+if (!evmPrivateKey && !svmPrivateKey && !stellarPrivateKey) {
console.error(
- "❌ At least one of EVM_PRIVATE_KEY or SVM_PRIVATE_KEY is required",
+ "❌ At least one of EVM_PRIVATE_KEY, SVM_PRIVATE_KEY, or STELLAR_PRIVATE_KEY is required",
);
process.exit(1);
}
@@ -47,25 +50,26 @@ if (!evmPrivateKey && !svmPrivateKey) {
// Network configuration
const EVM_NETWORK = "eip155:84532"; // Base Sepolia
const SVM_NETWORK = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"; // Solana Devnet
+const STELLAR_NETWORK = "stellar:testnet"; // Stellar Testnet
// Initialize the x402 Facilitator
const facilitator = new x402Facilitator()
- .onBeforeVerify(async context => {
+ .onBeforeVerify(async (context) => {
console.log("Before verify", context);
})
- .onAfterVerify(async context => {
+ .onAfterVerify(async (context) => {
console.log("After verify", context);
})
- .onVerifyFailure(async context => {
+ .onVerifyFailure(async (context) => {
console.log("Verify failure", context);
})
- .onBeforeSettle(async context => {
+ .onBeforeSettle(async (context) => {
console.log("Before settle", context);
})
- .onAfterSettle(async context => {
+ .onAfterSettle(async (context) => {
console.log("After settle", context);
})
- .onSettleFailure(async context => {
+ .onSettleFailure(async (context) => {
console.log("Settle failure", context);
});
@@ -137,6 +141,17 @@ if (svmPrivateKey) {
facilitator.register(SVM_NETWORK, new ExactSvmScheme(svmSigner));
}
+// Register Stellar scheme if private key is provided
+if (stellarPrivateKey) {
+ const stellarSigner = createEd25519Signer(stellarPrivateKey);
+ console.info(`Stellar Facilitator account: ${stellarSigner.address}`);
+
+ facilitator.register(
+ STELLAR_NETWORK,
+ new ExactStellarScheme([stellarSigner]),
+ );
+}
+
// Initialize Express app
const app = express();
app.use(express.json());
@@ -239,7 +254,14 @@ app.get("/health", (req, res) => {
// Start the server
app.listen(parseInt(PORT), () => {
- console.log(`🚀 All Networks Facilitator listening on http://localhost:${PORT}`);
- console.log(` Supported networks: ${facilitator.getSupported().kinds.map(k => k.network).join(", ")}`);
+ console.log(
+ `🚀 All Networks Facilitator listening on http://localhost:${PORT}`,
+ );
+ console.log(
+ ` Supported networks: ${facilitator
+ .getSupported()
+ .kinds.map((k) => k.network)
+ .join(", ")}`,
+ );
console.log();
});
diff --git a/examples/typescript/facilitator/advanced/bazaar.ts b/examples/typescript/facilitator/advanced/bazaar.ts
index d0637f4148..d1a701b560 100644
--- a/examples/typescript/facilitator/advanced/bazaar.ts
+++ b/examples/typescript/facilitator/advanced/bazaar.ts
@@ -59,13 +59,26 @@ interface DiscoveredResource {
}
// BazaarCatalog stores discovered resources
+/**
+ * Catalog of discovered resources from bazaar discovery extension.
+ */
class BazaarCatalog {
private resources: Map = new Map();
+ /**
+ * Adds a discovered resource to the catalog.
+ *
+ * @param res - The discovered resource to add
+ */
add(res: DiscoveredResource): void {
this.resources.set(res.resource, res);
}
+ /**
+ * Returns all discovered resources in the catalog.
+ *
+ * @returns Array of all discovered resources
+ */
getAll(): DiscoveredResource[] {
return Array.from(this.resources.values());
}
@@ -75,10 +88,10 @@ const bazaarCatalog = new BazaarCatalog();
// Initialize the x402 Facilitator with discovery hooks
const facilitator = new x402Facilitator()
- .onBeforeVerify(async context => {
+ .onBeforeVerify(async (context) => {
console.log("Before verify", context);
})
- .onAfterVerify(async context => {
+ .onAfterVerify(async (context) => {
console.log("✅ Payment verified");
// Extract discovered resource from payment for bazaar catalog
@@ -93,7 +106,11 @@ const facilitator = new x402Facilitator()
console.log(` 📝 Discovered resource: ${discovered.resourceUrl}`);
console.log(` 📝 Description: ${discovered.description}`);
console.log(` 📝 MimeType: ${discovered.mimeType}`);
- console.log(` 📝 Method: ${discovered.method}`);
+ if ("method" in discovered && discovered.method !== undefined) {
+ console.log(` 📝 Method: ${discovered.method}`);
+ } else if ("toolName" in discovered) {
+ console.log(` 📝 Tool: ${discovered.toolName}`);
+ }
console.log(` 📝 X402Version: ${discovered.x402Version}`);
bazaarCatalog.add({
@@ -112,16 +129,16 @@ const facilitator = new x402Facilitator()
console.log(` ⚠️ Failed to extract discovery info: ${err}`);
}
})
- .onVerifyFailure(async context => {
+ .onVerifyFailure(async (context) => {
console.log("Verify failure", context);
})
- .onBeforeSettle(async context => {
+ .onBeforeSettle(async (context) => {
console.log("Before settle", context);
})
- .onAfterSettle(async context => {
+ .onAfterSettle(async (context) => {
console.log(`🎉 Payment settled: ${context.result.transaction}`);
})
- .onSettleFailure(async context => {
+ .onSettleFailure(async (context) => {
console.log("Settle failure", context);
});
@@ -325,7 +342,12 @@ app.get("/health", (req, res) => {
// Start the server
app.listen(parseInt(PORT), () => {
console.log(`🚀 Discovery Facilitator listening on http://localhost:${PORT}`);
- console.log(` Supported networks: ${facilitator.getSupported().kinds.map(k => k.network).join(", ")}`);
+ console.log(
+ ` Supported networks: ${facilitator
+ .getSupported()
+ .kinds.map((k) => k.network)
+ .join(", ")}`,
+ );
console.log(` Discovery endpoint: GET /discovery/resources`);
console.log();
});
diff --git a/examples/typescript/facilitator/advanced/package.json b/examples/typescript/facilitator/advanced/package.json
index 4f8c88d05a..34253157d8 100644
--- a/examples/typescript/facilitator/advanced/package.json
+++ b/examples/typescript/facilitator/advanced/package.json
@@ -20,6 +20,7 @@
"@x402/evm": "workspace:*",
"@x402/extensions": "workspace:*",
"@x402/svm": "workspace:*",
+ "@x402/stellar": "workspace:*",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"viem": "^2.21.54"
@@ -38,4 +39,4 @@
"tsx": "^4.19.2",
"typescript": "^5.7.2"
}
-}
\ No newline at end of file
+}
diff --git a/examples/typescript/facilitator/basic/index.ts b/examples/typescript/facilitator/basic/index.ts
index b9cff1eec3..baf957bea8 100644
--- a/examples/typescript/facilitator/basic/index.ts
+++ b/examples/typescript/facilitator/basic/index.ts
@@ -116,8 +116,14 @@ const facilitator = new x402Facilitator()
});
// Register EVM and SVM schemes
-facilitator.register("eip155:84532", new ExactEvmScheme(evmSigner, { deployERC4337WithEIP6492: true })); // Base Sepolia
-facilitator.register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme(svmSigner)); // Devnet
+facilitator.register(
+ "eip155:84532",
+ new ExactEvmScheme(evmSigner, { deployERC4337WithEIP6492: true }),
+); // Base Sepolia
+facilitator.register(
+ "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ new ExactSvmScheme(svmSigner),
+); // Devnet
// Initialize Express app
const app = express();
diff --git a/examples/typescript/fullstack/miniapp/app/page.tsx b/examples/typescript/fullstack/miniapp/app/page.tsx
index 75ef8a3a7c..5aca262219 100644
--- a/examples/typescript/fullstack/miniapp/app/page.tsx
+++ b/examples/typescript/fullstack/miniapp/app/page.tsx
@@ -15,35 +15,53 @@ import {
WalletDropdownDisconnect,
} from "@coinbase/onchainkit/wallet";
import { useEffect, useMemo, useState, useCallback } from "react";
-import { useAccount, useWalletClient, useSwitchChain } from "wagmi";
+import { useAccount, useWalletClient, useSwitchChain, usePublicClient } from "wagmi";
import { baseSepolia } from "wagmi/chains";
import { sdk } from "@farcaster/miniapp-sdk";
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm/exact/client";
+import { toClientEvmSigner } from "@x402/evm";
import type { ClientEvmSigner } from "@x402/evm";
import type { WalletClient, Account } from "viem";
/**
* Converts a wagmi/viem WalletClient to a ClientEvmSigner for x402Client
*/
-function wagmiToClientSigner(walletClient: WalletClient): ClientEvmSigner {
+function wagmiToClientSigner(
+ walletClient: WalletClient,
+ publicClient: { readContract: (args: unknown) => Promise }
+): ClientEvmSigner {
if (!walletClient.account) {
throw new Error("Wallet client must have an account");
}
- return {
- address: walletClient.account.address,
- signTypedData: async (message) => {
- const signature = await walletClient.signTypedData({
- account: walletClient.account as Account,
- domain: message.domain,
- types: message.types,
- primaryType: message.primaryType,
- message: message.message,
- });
- return signature;
+ const readContractAdapter = {
+ readContract(args: {
+ address: `0x${string}`;
+ abi: readonly unknown[];
+ functionName: string;
+ args?: readonly unknown[];
+ }): Promise {
+ return publicClient.readContract(args);
},
};
+
+ return toClientEvmSigner(
+ {
+ address: walletClient.account.address,
+ signTypedData: async (message) => {
+ const signature = await walletClient.signTypedData({
+ account: walletClient.account as Account,
+ domain: message.domain,
+ types: message.types,
+ primaryType: message.primaryType,
+ message: message.message,
+ });
+ return signature;
+ },
+ },
+ readContractAdapter
+ );
}
export default function App() {
@@ -54,6 +72,7 @@ export default function App() {
const [message, setMessage] = useState("");
const { address, isConnected, chainId } = useAccount();
const { data: walletClient } = useWalletClient();
+ const publicClient = usePublicClient();
const { switchChainAsync } = useSwitchChain();
sdk.actions.ready();
@@ -96,7 +115,7 @@ export default function App() {
}, [addFrame]);
const handleProtectedAction = useCallback(async () => {
- if (!isConnected || !walletClient) {
+ if (!isConnected || !walletClient || !publicClient) {
setMessage("Please connect your wallet first");
return;
}
@@ -112,7 +131,7 @@ export default function App() {
// Create x402 client and register EVM scheme with wagmi signer
const client = new x402Client();
- const signer = wagmiToClientSigner(walletClient);
+ const signer = wagmiToClientSigner(walletClient, publicClient);
client.register("eip155:*", new ExactEvmScheme(signer));
// Wrap fetch with payment handling
@@ -137,7 +156,7 @@ export default function App() {
} finally {
setIsLoading(false);
}
- }, [isConnected, walletClient, chainId, switchChainAsync]);
+ }, [isConnected, walletClient, publicClient, chainId, switchChainAsync]);
const saveFrameButton = useMemo(() => {
if (context && !context.client.added) {
diff --git a/examples/typescript/legacy/mcp-embedded-wallet/README.md b/examples/typescript/legacy/mcp-embedded-wallet/README.md
index 0c65830ea8..1c669ec86c 100644
--- a/examples/typescript/legacy/mcp-embedded-wallet/README.md
+++ b/examples/typescript/legacy/mcp-embedded-wallet/README.md
@@ -110,7 +110,7 @@ We now have everything we need on the renderer side.
3. Set up call from electron
-Call from electron live in `operations` in `electron.ts` just for convinience.
+Call from electron live in `operations` in `electron.ts` just for convenience.
You might add something like this
```typescript
diff --git a/examples/typescript/pnpm-lock.yaml b/examples/typescript/pnpm-lock.yaml
index c3c6d73c07..0897f2bcf7 100644
--- a/examples/typescript/pnpm-lock.yaml
+++ b/examples/typescript/pnpm-lock.yaml
@@ -72,6 +72,9 @@ importers:
../../typescript/packages/extensions:
dependencies:
+ '@noble/curves':
+ specifier: ^1.9.0
+ version: 1.9.7
'@scure/base':
specifier: ^1.2.6
version: 1.2.6
@@ -81,6 +84,9 @@ importers:
ajv:
specifier: ^8.17.1
version: 8.17.1
+ jose:
+ specifier: ^5.9.6
+ version: 5.10.0
siwe:
specifier: ^2.3.2
version: 2.3.2(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -200,12 +206,6 @@ importers:
../../typescript/packages/http/express:
dependencies:
- '@coinbase/cdp-sdk':
- specifier: ^1.22.0
- version: 1.36.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)
- '@solana/kit':
- specifier: ^6.1.0
- version: 6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)
'@x402/core':
specifier: workspace:~
version: link:../../core
@@ -213,7 +213,7 @@ importers:
specifier: workspace:~
version: link:../../extensions
'@x402/paywall':
- specifier: workspace:*
+ specifier: workspace:^
version: link:../paywall
viem:
specifier: ^2.39.3
@@ -274,6 +274,67 @@ importers:
specifier: ^3.0.5
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.20.4)(yaml@2.8.1)
+ ../../typescript/packages/http/fastify:
+ dependencies:
+ '@x402/core':
+ specifier: workspace:~
+ version: link:../../core
+ '@x402/extensions':
+ specifier: workspace:~
+ version: link:../../extensions
+ '@x402/paywall':
+ specifier: workspace:*
+ version: link:../paywall
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@types/node':
+ specifier: ^22.13.4
+ version: 22.17.2
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2)
+ fastify:
+ specifier: ^5.0.0
+ version: 5.8.2
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsup:
+ specifier: ^8.4.0
+ version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.4)(typescript@5.9.2)(yaml@2.8.1)
+ tsx:
+ specifier: ^4.19.2
+ version: 4.20.4
+ typescript:
+ specifier: ^5.7.3
+ version: 5.9.2
+ vite:
+ specifier: ^6.2.6
+ version: 6.3.5(@types/node@22.17.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.4)(yaml@2.8.1)
+ vite-tsconfig-paths:
+ specifier: ^5.1.4
+ version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.17.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.4)(yaml@2.8.1))
+ vitest:
+ specifier: ^3.0.5
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.20.4)(yaml@2.8.1)
+
../../typescript/packages/http/fetch:
dependencies:
'@x402/core':
@@ -341,7 +402,7 @@ importers:
specifier: workspace:~
version: link:../../extensions
'@x402/paywall':
- specifier: workspace:*
+ specifier: workspace:^
version: link:../paywall
zod:
specifier: ^3.24.2
@@ -373,7 +434,7 @@ importers:
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2)
hono:
specifier: ^4.7.1
- version: 4.9.2
+ version: 4.10.7
prettier:
specifier: 3.5.2
version: 3.5.2
@@ -398,9 +459,6 @@ importers:
../../typescript/packages/http/next:
dependencies:
- '@coinbase/cdp-sdk':
- specifier: ^1.22.0
- version: 1.36.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)
'@x402/core':
specifier: workspace:~
version: link:../../core
@@ -408,7 +466,7 @@ importers:
specifier: workspace:~
version: link:../../extensions
'@x402/paywall':
- specifier: workspace:*
+ specifier: workspace:^
version: link:../paywall
next:
specifier: ^16.0.10
@@ -907,7 +965,7 @@ importers:
version: 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
hono:
specifier: ^4.7.1
- version: 4.9.2
+ version: 4.10.7
viem:
specifier: ^2.21.26
version: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
@@ -1158,9 +1216,6 @@ importers:
'@x402/core':
specifier: workspace:~
version: link:../../core
- '@x402/extensions':
- specifier: workspace:~
- version: link:../../extensions
viem:
specifier: ^2.39.3
version: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
@@ -1214,6 +1269,61 @@ importers:
specifier: ^3.0.5
version: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.20.4)(yaml@2.8.1)
+ ../../typescript/packages/mechanisms/stellar:
+ dependencies:
+ '@stellar/stellar-sdk':
+ specifier: ^14.6.1
+ version: 14.6.1
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../core
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@types/node':
+ specifier: ^22.13.4
+ version: 22.17.2
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2)
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsup:
+ specifier: ^8.4.0
+ version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.4)(typescript@5.9.2)(yaml@2.8.1)
+ tsx:
+ specifier: ^4.19.2
+ version: 4.20.4
+ typescript:
+ specifier: ^5.7.3
+ version: 5.9.2
+ vite:
+ specifier: ^6.2.6
+ version: 6.3.5(@types/node@22.17.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.4)(yaml@2.8.1)
+ vite-tsconfig-paths:
+ specifier: ^5.1.4
+ version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.17.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.20.4)(yaml@2.8.1))
+ vitest:
+ specifier: ^3.0.5
+ version: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@2.6.1)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(tsx@4.20.4)(yaml@2.8.1)
+
../../typescript/packages/mechanisms/svm:
dependencies:
'@solana-program/compute-budget':
@@ -1295,6 +1405,9 @@ importers:
'@x402/fetch':
specifier: workspace:*
version: link:../../../../typescript/packages/http/fetch
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/stellar
'@x402/svm':
specifier: workspace:*
version: link:../../../../typescript/packages/mechanisms/svm
@@ -1476,6 +1589,9 @@ importers:
'@eslint/js':
specifier: ^9.24.0
version: 9.33.0
+ '@types/node':
+ specifier: ^20.0.0
+ version: 20.19.11
'@typescript-eslint/eslint-plugin':
specifier: ^8.29.1
version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
@@ -1614,6 +1730,67 @@ importers:
specifier: ^5.9.2
version: 5.9.2
+ clients/offer-receipt:
+ dependencies:
+ '@scure/base':
+ specifier: ^1.2.6
+ version: 1.2.6
+ '@solana/kit':
+ specifier: ^2.1.1
+ version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@x402/evm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/evm
+ '@x402/extensions':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/extensions
+ '@x402/fetch':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/http/fetch
+ '@x402/svm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/svm
+ dotenv:
+ specifier: ^16.4.7
+ version: 16.6.1
+ viem:
+ specifier: ^2.39.0
+ version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@types/node':
+ specifier: ^20.0.0
+ version: 20.19.11
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2)
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsx:
+ specifier: ^4.7.0
+ version: 4.20.4
+ typescript:
+ specifier: ^5.3.0
+ version: 5.9.2
+
clients/payment-identifier:
dependencies:
'@x402/evm':
@@ -1727,11 +1904,148 @@ importers:
specifier: ^5.3.0
version: 5.9.2
+ facilitator/advanced:
+ dependencies:
+ '@scure/base':
+ specifier: ^1.2.6
+ version: 1.2.6
+ '@solana/kit':
+ specifier: ^6.1.0
+ version: 6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/core
+ '@x402/evm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/evm
+ '@x402/extensions':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/extensions
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/stellar
+ '@x402/svm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/svm
+ dotenv:
+ specifier: ^16.4.5
+ version: 16.6.1
+ express:
+ specifier: ^4.19.2
+ version: 4.21.2
+ viem:
+ specifier: ^2.21.54
+ version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@types/express':
+ specifier: ^4.17.21
+ version: 4.17.25
+ '@types/node':
+ specifier: ^22.10.1
+ version: 22.17.2
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.15.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.6.2)
+ prettier:
+ specifier: ^3.3.3
+ version: 3.6.2
+ tsx:
+ specifier: ^4.19.2
+ version: 4.20.4
+ typescript:
+ specifier: ^5.7.2
+ version: 5.9.2
+
+ facilitator/basic:
+ dependencies:
+ '@scure/base':
+ specifier: ^1.2.6
+ version: 1.2.6
+ '@solana/kit':
+ specifier: ^6.1.0
+ version: 6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/core
+ '@x402/evm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/evm
+ '@x402/extensions':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/extensions
+ '@x402/svm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/svm
+ dotenv:
+ specifier: ^16.4.5
+ version: 16.6.1
+ express:
+ specifier: ^4.19.2
+ version: 4.21.2
+ viem:
+ specifier: ^2.21.54
+ version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@types/express':
+ specifier: ^4.17.21
+ version: 4.17.25
+ '@types/node':
+ specifier: ^22.10.1
+ version: 22.17.2
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.15.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.6.2)
+ prettier:
+ specifier: ^3.3.3
+ version: 3.6.2
+ tsx:
+ specifier: ^4.19.2
+ version: 4.20.4
+ typescript:
+ specifier: ^5.7.2
+ version: 5.9.2
+
fullstack/miniapp:
dependencies:
'@coinbase/onchainkit':
specifier: 1.1.2
- version: 1.1.2(@tanstack/query-core@5.90.11)(@types/react-dom@19.2.1(@types/react@19.2.1))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)
+ version: 1.1.2(@tanstack/query-core@5.90.11)(@types/react-dom@19.2.1(@types/react@19.2.1))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(zod@4.1.13)
'@farcaster/miniapp-sdk':
specifier: 0.2.1
version: 0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
@@ -1764,7 +2078,7 @@ importers:
version: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
wagmi:
specifier: ^2.14.11
- version: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ version: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
devDependencies:
'@eslint/js':
specifier: ^9.24.0
@@ -2054,7 +2368,7 @@ importers:
dependencies:
'@hono/node-server':
specifier: ^1.13.8
- version: 1.19.0(hono@4.9.2)
+ version: 1.19.0(hono@4.10.7)
axios:
specifier: ^1.7.9
version: 1.13.2
@@ -2063,7 +2377,7 @@ importers:
version: 16.6.1
hono:
specifier: ^4.7.2
- version: 4.9.2
+ version: 4.10.7
viem:
specifier: ^2.23.5
version: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
@@ -2128,7 +2442,7 @@ importers:
version: 0.39.0(encoding@0.1.13)
'@hono/node-server':
specifier: ^1.14.1
- version: 1.19.0(hono@4.9.2)
+ version: 1.19.0(hono@4.10.7)
'@llamaindex/anthropic':
specifier: ^0.3.3
version: 0.3.23(@llamaindex/core@0.6.5)(@llamaindex/env@0.1.30)
@@ -2140,7 +2454,7 @@ importers:
version: 1.13.2
hono:
specifier: ^4.7.7
- version: 4.9.2
+ version: 4.10.7
llamaindex:
specifier: ^0.10.2
version: 0.10.6(encoding@0.1.13)(tree-sitter@0.22.4)(web-tree-sitter@0.24.7)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)
@@ -2241,13 +2555,13 @@ importers:
dependencies:
'@hono/node-server':
specifier: ^1.11.2
- version: 1.19.0(hono@4.9.2)
+ version: 1.19.0(hono@4.10.7)
dotenv:
specifier: ^16.4.5
version: 16.6.1
hono:
specifier: ^4.4.0
- version: 4.9.2
+ version: 4.10.7
node-fetch:
specifier: ^3.3.2
version: 3.3.2
@@ -2284,7 +2598,7 @@ importers:
dependencies:
'@hono/node-server':
specifier: ^1.14.0
- version: 1.19.0(hono@4.9.2)
+ version: 1.19.0(hono@4.10.7)
axios:
specifier: ^1.10.0
version: 1.13.2
@@ -2293,7 +2607,7 @@ importers:
version: 16.6.1
hono:
specifier: ^4.6.17
- version: 4.9.2
+ version: 4.10.7
react:
specifier: ^18.3.1
version: 18.3.1
@@ -2345,7 +2659,7 @@ importers:
dependencies:
'@coinbase/onchainkit':
specifier: ^0.38.14
- version: 0.38.19(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
+ version: 0.38.19(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
'@farcaster/frame-sdk':
specifier: ^0.0.60
version: 0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
@@ -2375,7 +2689,7 @@ importers:
version: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
wagmi:
specifier: ^2.14.11
- version: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ version: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
x402-fetch:
specifier: workspace:*
version: link:../../../../../typescript/packages/legacy/x402-fetch
@@ -2497,7 +2811,7 @@ importers:
dependencies:
'@coinbase/onchainkit':
specifier: 0.38.14
- version: 0.38.14(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ version: 0.38.14(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
'@tanstack/react-query':
specifier: ^5
version: 5.90.11(react@19.2.3)
@@ -2515,7 +2829,7 @@ importers:
version: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
wagmi:
specifier: ^2.15.6
- version: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ version: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
x402:
specifier: workspace:*
version: link:../../../../../typescript/packages/legacy/x402
@@ -2762,13 +3076,13 @@ importers:
dependencies:
'@hono/node-server':
specifier: ^1.13.8
- version: 1.19.0(hono@4.9.2)
+ version: 1.19.0(hono@4.10.7)
dotenv:
specifier: ^16.4.7
version: 16.6.1
hono:
specifier: ^4.7.1
- version: 4.9.2
+ version: 4.10.7
x402-hono:
specifier: workspace:*
version: link:../../../../../typescript/packages/legacy/x402-hono
@@ -2807,7 +3121,71 @@ importers:
specifier: ^5.3.0
version: 5.9.2
- servers/advanced:
+ servers/advanced:
+ dependencies:
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/core
+ '@x402/evm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/evm
+ '@x402/express':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/http/express
+ '@x402/extensions':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/extensions
+ '@x402/stellar':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/stellar
+ '@x402/svm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/svm
+ dotenv:
+ specifier: ^16.4.7
+ version: 16.6.1
+ express:
+ specifier: ^4.18.2
+ version: 4.21.2
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@types/express':
+ specifier: ^5.0.1
+ version: 5.0.3
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.40.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2)
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsup:
+ specifier: ^7.2.0
+ version: 7.3.0(postcss@8.5.6)(typescript@5.9.2)
+ tsx:
+ specifier: ^4.7.0
+ version: 4.20.4
+ typescript:
+ specifier: ^5.3.0
+ version: 5.9.2
+
+ servers/bazaar:
dependencies:
'@x402/core':
specifier: workspace:*
@@ -2839,16 +3217,16 @@ importers:
version: 5.0.3
'@typescript-eslint/eslint-plugin':
specifier: ^8.29.1
- version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
'@typescript-eslint/parser':
specifier: ^8.29.1
- version: 8.40.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
eslint:
specifier: ^9.24.0
version: 9.33.0(jiti@2.6.1)
eslint-plugin-import:
specifier: ^2.31.0
- version: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
eslint-plugin-jsdoc:
specifier: ^50.6.9
version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
@@ -2921,9 +3299,6 @@ importers:
servers/express:
dependencies:
- '@coinbase/x402':
- specifier: ^2.1.0
- version: 2.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@x402/core':
specifier: workspace:*
version: link:../../../../typescript/packages/core
@@ -2983,11 +3358,69 @@ importers:
specifier: ^5.3.0
version: 5.9.2
+ servers/fastify:
+ dependencies:
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/core
+ '@x402/evm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/evm
+ '@x402/extensions':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/extensions
+ '@x402/fastify':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/http/fastify
+ '@x402/svm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/svm
+ dotenv:
+ specifier: ^16.4.7
+ version: 16.6.1
+ fastify:
+ specifier: ^5.0.0
+ version: 5.8.2
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2)
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsup:
+ specifier: ^7.2.0
+ version: 7.3.0(postcss@8.5.6)(typescript@5.9.2)
+ tsx:
+ specifier: ^4.7.0
+ version: 4.20.4
+ typescript:
+ specifier: ^5.3.0
+ version: 5.9.2
+
servers/hono:
dependencies:
'@hono/node-server':
specifier: ^1.14.0
- version: 1.19.0(hono@4.9.2)
+ version: 1.19.0(hono@4.10.7)
'@x402/core':
specifier: workspace:*
version: link:../../../../typescript/packages/core
@@ -3008,7 +3441,7 @@ importers:
version: 16.6.1
hono:
specifier: ^4.7.1
- version: 4.9.2
+ version: 4.10.7
devDependencies:
'@eslint/js':
specifier: ^9.24.0
@@ -3108,6 +3541,70 @@ importers:
specifier: ^5.9.2
version: 5.9.2
+ servers/offer-receipt:
+ dependencies:
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/core
+ '@x402/evm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/evm
+ '@x402/express':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/http/express
+ '@x402/extensions':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/extensions
+ '@x402/svm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/svm
+ dotenv:
+ specifier: ^16.4.7
+ version: 16.6.1
+ express:
+ specifier: ^4.18.2
+ version: 4.21.2
+ viem:
+ specifier: ^2.21.54
+ version: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@types/express':
+ specifier: ^5.0.1
+ version: 5.0.3
+ '@types/node':
+ specifier: ^22.0.0
+ version: 22.17.2
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2)
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsx:
+ specifier: ^4.7.0
+ version: 4.20.4
+ typescript:
+ specifier: ^5.3.0
+ version: 5.9.2
+
servers/payment-identifier:
dependencies:
'@x402/core':
@@ -3227,6 +3724,64 @@ importers:
specifier: ^5.3.0
version: 5.9.2
+ servers/upto:
+ dependencies:
+ '@x402/core':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/core
+ '@x402/evm':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/mechanisms/evm
+ '@x402/express':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/http/express
+ '@x402/extensions':
+ specifier: workspace:*
+ version: link:../../../../typescript/packages/extensions
+ dotenv:
+ specifier: ^16.4.7
+ version: 16.6.1
+ express:
+ specifier: ^4.18.2
+ version: 4.21.2
+ devDependencies:
+ '@eslint/js':
+ specifier: ^9.24.0
+ version: 9.33.0
+ '@types/express':
+ specifier: ^5.0.1
+ version: 5.0.3
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.29.1
+ version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ '@typescript-eslint/parser':
+ specifier: ^8.29.1
+ version: 8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2)
+ eslint:
+ specifier: ^9.24.0
+ version: 9.33.0(jiti@2.6.1)
+ eslint-plugin-import:
+ specifier: ^2.31.0
+ version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.33.0(jiti@2.6.1))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-jsdoc:
+ specifier: ^50.6.9
+ version: 50.8.0(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier:
+ specifier: ^5.2.6
+ version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.5.2)
+ prettier:
+ specifier: 3.5.2
+ version: 3.5.2
+ tsup:
+ specifier: ^7.2.0
+ version: 7.3.0(postcss@8.5.6)(typescript@5.9.2)
+ tsx:
+ specifier: ^4.7.0
+ version: 4.20.4
+ typescript:
+ specifier: ^5.3.0
+ version: 5.9.2
+
packages:
7zip-bin@5.2.0:
@@ -3929,9 +4484,6 @@ packages:
'@coinbase/wallet-sdk@4.3.6':
resolution: {integrity: sha512-4q8BNG1ViL4mSAAvPAtpwlOs1gpC+67eQtgIwNvT3xyeyFFd+guwkc8bcX5rTmQhXpqnhzC4f0obACbP9CqMSA==}
- '@coinbase/x402@2.1.0':
- resolution: {integrity: sha512-aKeM+cz//+FjzPVu/zgz7830x0KLtKarwCyxoeC71QgCn+Xcf0NhFpn3Qyw0H496y5YOuR/IQ67gP8DZ/hXFqQ==}
-
'@colors/colors@1.6.0':
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
engines: {node: '>=0.1.90'}
@@ -4445,6 +4997,24 @@ packages:
peerDependencies:
typescript: 5.8.3
+ '@fastify/ajv-compiler@4.0.5':
+ resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==}
+
+ '@fastify/error@4.2.0':
+ resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==}
+
+ '@fastify/fast-json-stringify-compiler@5.0.3':
+ resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==}
+
+ '@fastify/forwarded@3.0.1':
+ resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==}
+
+ '@fastify/merge-json-schemas@0.2.1':
+ resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==}
+
+ '@fastify/proxy-addr@5.1.0':
+ resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==}
+
'@floating-ui/core@1.7.3':
resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==}
@@ -5110,6 +5680,9 @@ packages:
resolution: {integrity: sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ==}
deprecated: 'The package is now available as "qr": npm install qr'
+ '@pinojs/redact@0.4.0':
+ resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==}
+
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -6086,6 +6659,12 @@ packages:
peerDependencies:
'@solana/kit': ^5.0
+ '@solana/accounts@2.3.0':
+ resolution: {integrity: sha512-QgQTj404Z6PXNOyzaOpSzjgMOuGwG8vC66jSDB+3zHaRcEPRVRd2sVSrd1U6sHtnV3aiaS6YyDuPQMheg4K2jw==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/accounts@3.0.3':
resolution: {integrity: sha512-KqlePrlZaHXfu8YQTCxN204ZuVm9o68CCcUr6l27MG2cuRUtEM1Ta0iR8JFkRUAEfZJC4Cu0ZDjK/v49loXjZQ==}
engines: {node: '>=20.18.0'}
@@ -6355,6 +6934,12 @@ packages:
peerDependencies:
typescript: '>=5'
+ '@solana/codecs@2.3.0':
+ resolution: {integrity: sha512-JVqGPkzoeyU262hJGdH64kNLH0M+Oew2CIPOa/9tR3++q2pEd4jU2Rxdfye9sd0Ce3XJrR5AIa8ZfbyQXzjh+g==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/codecs@3.0.3':
resolution: {integrity: sha512-GOHwTlIQsCoJx9Ryr6cEf0FHKAQ7pY4aO4xgncAftrv0lveTQ1rPP2inQ1QT0gJllsIa8nwbfXAADs9nNJxQDA==}
engines: {node: '>=20.18.0'}
@@ -6585,6 +7170,12 @@ packages:
typescript:
optional: true
+ '@solana/kit@2.3.0':
+ resolution: {integrity: sha512-sb6PgwoW2LjE5oTFu4lhlS/cGt/NB3YrShEyx7JgWFWysfgLdJnhwWThgwy/4HjNsmtMrQGWVls0yVBHcMvlMQ==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/kit@3.0.3':
resolution: {integrity: sha512-CEEhCDmkvztd1zbgADsEQhmj9GyWOOGeW1hZD+gtwbBSF5YN1uofS/pex5MIh/VIqKRj+A2UnYWI1V+9+q/lyQ==}
engines: {node: '>=20.18.0'}
@@ -6665,6 +7256,12 @@ packages:
peerDependencies:
typescript: '>=5'
+ '@solana/options@2.3.0':
+ resolution: {integrity: sha512-PPnnZBRCWWoZQ11exPxf//DRzN2C6AoFsDI/u2AsQfYih434/7Kp4XLpfOMT/XESi+gdBMFNNfbES5zg3wAIkw==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/options@3.0.3':
resolution: {integrity: sha512-jarsmnQ63RN0JPC5j9sgUat07NrL9PC71XU7pUItd6LOHtu4+wJMio3l5mT0DHVfkfbFLL6iI6+QmXSVhTNF3g==}
engines: {node: '>=20.18.0'}
@@ -6719,6 +7316,12 @@ packages:
typescript:
optional: true
+ '@solana/programs@2.3.0':
+ resolution: {integrity: sha512-UXKujV71VCI5uPs+cFdwxybtHZAIZyQkqDiDnmK+DawtOO9mBn4Nimdb/6RjR2CXT78mzO9ZCZ3qfyX+ydcB7w==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/programs@3.0.3':
resolution: {integrity: sha512-JZlVE3/AeSNDuH3aEzCZoDu8GTXkMpGXxf93zXLzbxfxhiQ/kHrReN4XE/JWZ/uGWbaFZGR5B3UtdN2QsoZL7w==}
engines: {node: '>=20.18.0'}
@@ -7182,6 +7785,12 @@ packages:
typescript:
optional: true
+ '@solana/signers@2.3.0':
+ resolution: {integrity: sha512-OSv6fGr/MFRx6J+ZChQMRqKNPGGmdjkqarKkRzkwmv7v8quWsIRnJT5EV8tBy3LI4DLO/A8vKiNSPzvm1TdaiQ==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/signers@3.0.3':
resolution: {integrity: sha512-UwCd/uPYTZiwd283JKVyOWLLN5sIgMBqGDyUmNU3vo9hcmXKv5ZGm/9TvwMY2z35sXWuIOcj7etxJ8OoWc/ObQ==}
engines: {node: '>=20.18.0'}
@@ -7260,6 +7869,12 @@ packages:
typescript:
optional: true
+ '@solana/sysvars@2.3.0':
+ resolution: {integrity: sha512-LvjADZrpZ+CnhlHqfI5cmsRzX9Rpyb1Ox2dMHnbsRNzeKAMhu9w4ZBIaeTdO322zsTr509G1B+k2ABD3whvUBA==}
+ engines: {node: '>=20.18.0'}
+ peerDependencies:
+ typescript: '>=5.3.3'
+
'@solana/sysvars@3.0.3':
resolution: {integrity: sha512-GnHew+QeKCs2f9ow+20swEJMH4mDfJA/QhtPgOPTYQx/z69J4IieYJ7fZenSHnA//lJ45fVdNdmy1trypvPLBQ==}
engines: {node: '>=20.18.0'}
@@ -7411,6 +8026,18 @@ packages:
'@stablelib/wipe@1.0.1':
resolution: {integrity: sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==}
+ '@stellar/js-xdr@3.1.2':
+ resolution: {integrity: sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==}
+
+ '@stellar/stellar-base@14.1.0':
+ resolution: {integrity: sha512-A8kFli6QGy22SRF45IjgPAJfUNGjnI+R7g4DF5NZYVsD1kGf7B4ITyc4OPclLV9tqNI4/lXxafGEw0JEUbHixw==}
+ engines: {node: '>=20.0.0'}
+
+ '@stellar/stellar-sdk@14.6.1':
+ resolution: {integrity: sha512-A1rQWDLdUasXkMXnYSuhgep+3ZZzyuXJKdt5/KAIc0gkmSp906HTvUpbT4pu+bVr41tu0+J4Ugz9J4BQAGGytg==}
+ engines: {node: '>=20.0.0'}
+ hasBin: true
+
'@svgr/babel-plugin-add-jsx-attribute@8.0.0':
resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==}
engines: {node: '>=14'}
@@ -7639,9 +8266,15 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+ '@types/express-serve-static-core@4.19.8':
+ resolution: {integrity: sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==}
+
'@types/express-serve-static-core@5.0.7':
resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==}
+ '@types/express@4.17.25':
+ resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==}
+
'@types/express@5.0.3':
resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==}
@@ -8166,9 +8799,6 @@ packages:
'@walletconnect/window-metadata@1.0.1':
resolution: {integrity: sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==}
- '@x402/core@2.3.1':
- resolution: {integrity: sha512-CWvsf09tslISoVOzQ2TIoBLBP+bUycPsYmmRVe3EV5X2FtD7eXXpiPsiXLEVtWP7zhqLNP/5OIATsA2hSVLSfw==}
-
'@xmldom/xmldom@0.8.11':
resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==}
engines: {node: '>=10.0.0'}
@@ -8224,6 +8854,9 @@ packages:
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
engines: {node: '>=6.5'}
+ abstract-logging@2.0.1:
+ resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==}
+
accepts@1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@@ -8261,6 +8894,14 @@ packages:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
+ ajv-formats@3.0.1:
+ resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==}
+ peerDependencies:
+ ajv: ^8.0.0
+ peerDependenciesMeta:
+ ajv:
+ optional: true
+
ajv-keywords@3.5.2:
resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
peerDependencies:
@@ -8413,6 +9054,9 @@ packages:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
+ avvio@9.2.0:
+ resolution: {integrity: sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==}
+
axe-core@4.10.3:
resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
engines: {node: '>=4'}
@@ -8430,6 +9074,9 @@ packages:
axios@1.13.2:
resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==}
+ axios@1.13.4:
+ resolution: {integrity: sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==}
+
axobject-query@4.1.0:
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
engines: {node: '>= 0.4'}
@@ -8461,6 +9108,10 @@ packages:
base-x@5.0.1:
resolution: {integrity: sha512-M7uio8Zt++eg3jPj+rHMfCC+IuygQHHCOU+IYsVtik6FWjuYpVt/+MRKcgsAMHh8mMFAwnB+Bs+mTrFiXjMzKg==}
+ base32.js@0.1.0:
+ resolution: {integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==}
+ engines: {node: '>=0.12.0'}
+
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -8862,6 +9513,10 @@ packages:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
+ cookie@1.1.1:
+ resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
+ engines: {node: '>=18'}
+
core-js-compat@3.45.1:
resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==}
@@ -9077,6 +9732,10 @@ packages:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
derive-valtio@0.1.0:
resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==}
peerDependencies:
@@ -9566,6 +10225,10 @@ packages:
resolution: {integrity: sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==}
engines: {node: '>=20.0.0'}
+ eventsource@2.0.2:
+ resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==}
+ engines: {node: '>=12.0.0'}
+
eventsource@3.0.7:
resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==}
engines: {node: '>=18.0.0'}
@@ -9612,6 +10275,9 @@ packages:
resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==}
engines: {node: '> 0.1.90'}
+ fast-decode-uri-component@1.0.1:
+ resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
+
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@@ -9629,9 +10295,15 @@ packages:
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+ fast-json-stringify@6.3.0:
+ resolution: {integrity: sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==}
+
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+ fast-querystring@1.1.2:
+ resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
+
fast-redact@3.5.0:
resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==}
engines: {node: '>=6'}
@@ -9651,6 +10323,9 @@ packages:
fastestsmallesttextencoderdecoder@1.0.22:
resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==}
+ fastify@5.8.2:
+ resolution: {integrity: sha512-lZmt3navvZG915IE+f7/TIVamxIwmBd+OMB+O9WBzcpIwOo6F0LTh0sluoMFk5VkrKTvvrwIaoJPkir4Z+jtAg==}
+
fastq@1.19.1:
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
@@ -9666,6 +10341,9 @@ packages:
picomatch:
optional: true
+ feaxios@0.0.23:
+ resolution: {integrity: sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==}
+
fecha@4.2.3:
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
@@ -9703,6 +10381,10 @@ packages:
resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
engines: {node: '>= 0.8'}
+ find-my-way@9.5.0:
+ resolution: {integrity: sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ==}
+ engines: {node: '>=20'}
+
find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
@@ -9976,10 +10658,6 @@ packages:
resolution: {integrity: sha512-icXIITfw/07Q88nLSkB9aiUrd8rYzSweK681Kjo/TSggaGbOX4RRyxxm71v+3PC8C/j+4rlxGeoTRxQDkaJkUw==}
engines: {node: '>=16.9.0'}
- hono@4.9.2:
- resolution: {integrity: sha512-UG2jXGS/gkLH42l/1uROnwXpkjvvxkl3kpopL3LBo27NuaDPI6xHNfuUSilIHcrBkPfl4y0z6y2ByI455TjNRw==}
- engines: {node: '>=16.9.0'}
-
hosted-git-info@4.1.0:
resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==}
engines: {node: '>=10'}
@@ -10093,6 +10771,10 @@ packages:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
+ ipaddr.js@2.3.0:
+ resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==}
+ engines: {node: '>= 10'}
+
iron-webcrypto@1.2.1:
resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
@@ -10221,6 +10903,10 @@ packages:
resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==}
engines: {node: '>=10'}
+ is-retry-allowed@3.0.0:
+ resolution: {integrity: sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==}
+ engines: {node: '>=12'}
+
is-set@2.0.3:
resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
engines: {node: '>= 0.4'}
@@ -10383,6 +11069,9 @@ packages:
json-rpc-random-id@1.0.1:
resolution: {integrity: sha512-RJ9YYNCkhVDBuP4zN5BBtYAzEl03yq/jIIsyif0JY9qyJuQQZNeDK7anAPKKlyEtLSj2s8h6hNh2F8zO5q7ScA==}
+ json-schema-ref-resolver@3.0.0:
+ resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==}
+
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -10468,6 +11157,9 @@ packages:
libphonenumber-js@1.12.13:
resolution: {integrity: sha512-QZXnR/OGiDcBjF4hGk0wwVrPcZvbSSyzlvkjXv5LFfktj7O2VZDrt4Xs8SgR/vOFco+qk1i8J43ikMXZoTrtPw==}
+ light-my-request@6.6.0:
+ resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==}
+
lightningcss-android-arm64@1.30.2:
resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
engines: {node: '>= 12.0.0'}
@@ -11017,6 +11709,10 @@ packages:
on-exit-leak-free@0.2.0:
resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==}
+ on-exit-leak-free@2.1.2:
+ resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
+ engines: {node: '>=14.0.0'}
+
on-finished@2.4.1:
resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
engines: {node: '>= 0.8'}
@@ -11284,9 +11980,19 @@ packages:
pino-abstract-transport@0.5.0:
resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==}
+ pino-abstract-transport@3.0.0:
+ resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==}
+
pino-std-serializers@4.0.0:
resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==}
+ pino-std-serializers@7.1.0:
+ resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==}
+
+ pino@10.3.1:
+ resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==}
+ hasBin: true
+
pino@7.11.0:
resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==}
hasBin: true
@@ -11455,6 +12161,12 @@ packages:
process-warning@1.0.0:
resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==}
+ process-warning@4.0.1:
+ resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==}
+
+ process-warning@5.0.0:
+ resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
+
progress@2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
@@ -11545,6 +12257,9 @@ packages:
radix3@1.1.2:
resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
+ randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+
range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
@@ -11668,6 +12383,10 @@ packages:
resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==}
engines: {node: '>= 12.13.0'}
+ real-require@0.2.0:
+ resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
+ engines: {node: '>= 12.13.0'}
+
redaxios@0.5.1:
resolution: {integrity: sha512-FSD2AmfdbkYwl7KDExYQlVvIrFz6Yd83pGfaGjBzM9F6rpq8g652Q4Yq5QD4c+nf4g2AgeElv1y+8ajUPiOYMg==}
@@ -11751,6 +12470,10 @@ packages:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
engines: {node: '>=8'}
+ ret@0.5.0:
+ resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==}
+ engines: {node: '>=10'}
+
retry@0.12.0:
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
engines: {node: '>= 4'}
@@ -11763,6 +12486,9 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
rimraf@2.6.3:
resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==}
deprecated: Rimraf versions prior to v4 are no longer supported
@@ -11819,6 +12545,10 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
+ safe-regex2@5.1.0:
+ resolution: {integrity: sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw==}
+ hasBin: true
+
safe-stable-stringify@2.5.0:
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
engines: {node: '>=10'}
@@ -11849,6 +12579,9 @@ packages:
resolution: {integrity: sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==}
engines: {node: '>=18.0.0'}
+ secure-json-parse@4.1.0:
+ resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==}
+
selderee@0.11.0:
resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
@@ -11896,6 +12629,9 @@ packages:
set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+ set-cookie-parser@2.7.2:
+ resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
+
set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
@@ -12007,6 +12743,9 @@ packages:
sonic-boom@2.8.0:
resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==}
+ sonic-boom@4.2.1:
+ resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
@@ -12263,6 +13002,10 @@ packages:
thread-stream@0.15.2:
resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==}
+ thread-stream@4.0.0:
+ resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==}
+ engines: {node: '>=20'}
+
tiny-async-pool@1.3.0:
resolution: {integrity: sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==}
@@ -12314,10 +13057,17 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ toad-cache@3.7.0:
+ resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==}
+ engines: {node: '>=12'}
+
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
+ toml@3.0.0:
+ resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
+
tough-cookie@5.1.2:
resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
engines: {node: '>=16'}
@@ -12662,6 +13412,9 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ urijs@1.19.11:
+ resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==}
+
url-parse@1.5.10:
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
@@ -14138,9 +14891,9 @@ snapshots:
- ws
- zod
- '@base-org/account@2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)':
+ '@base-org/account@2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)':
dependencies:
- '@coinbase/cdp-sdk': 1.39.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)
+ '@coinbase/cdp-sdk': 1.39.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@noble/hashes': 1.4.0
clsx: 1.2.1
eventemitter3: 5.0.1
@@ -14163,9 +14916,9 @@ snapshots:
- ws
- zod
- '@base-org/account@2.4.0(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)':
+ '@base-org/account@2.4.0(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)':
dependencies:
- '@coinbase/cdp-sdk': 1.39.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)
+ '@coinbase/cdp-sdk': 1.39.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@noble/hashes': 1.4.0
clsx: 1.2.1
eventemitter3: 5.0.1
@@ -14297,8 +15050,8 @@ snapshots:
'@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/web3.js': 1.98.4(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)
abitype: 1.0.6(typescript@5.9.2)(zod@3.25.76)
- axios: 1.13.2
- axios-retry: 4.5.0(axios@1.13.2)
+ axios: 1.13.4
+ axios-retry: 4.5.0(axios@1.13.4)
jose: 6.0.12
md5: 2.3.0
uncrypto: 0.1.3
@@ -14320,31 +15073,8 @@ snapshots:
'@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/web3.js': 1.98.4(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)
abitype: 1.0.6(typescript@5.9.2)(zod@3.25.76)
- axios: 1.13.2
- axios-retry: 4.5.0(axios@1.13.2)
- jose: 6.0.12
- md5: 2.3.0
- uncrypto: 0.1.3
- viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
- zod: 3.25.76
- transitivePeerDependencies:
- - bufferutil
- - debug
- - encoding
- - fastestsmallesttextencoderdecoder
- - typescript
- - utf-8-validate
- - ws
-
- '@coinbase/cdp-sdk@1.39.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)':
- dependencies:
- '@solana-program/system': 0.8.1(@solana/kit@3.0.3(typescript@5.9.2))
- '@solana-program/token': 0.6.0(@solana/kit@3.0.3(typescript@5.9.2))
- '@solana/kit': 3.0.3(typescript@5.9.2)
- '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)
- abitype: 1.0.6(typescript@5.9.2)(zod@3.25.76)
- axios: 1.13.2
- axios-retry: 4.5.0(axios@1.13.2)
+ axios: 1.13.4
+ axios-retry: 4.5.0(axios@1.13.4)
jose: 6.0.12
md5: 2.3.0
uncrypto: 0.1.3
@@ -14382,7 +15112,7 @@ snapshots:
- utf-8-validate
- zod
- '@coinbase/onchainkit@0.38.14(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ '@coinbase/onchainkit@0.38.14(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)':
dependencies:
'@farcaster/frame-sdk': 0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@farcaster/frame-wagmi-connector': 0.0.42(@farcaster/frame-sdk@0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
@@ -14396,7 +15126,7 @@ snapshots:
react-dom: 19.2.3(react@19.2.3)
tailwind-merge: 2.6.0
viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -14487,7 +15217,7 @@ snapshots:
- ws
- zod
- '@coinbase/onchainkit@0.38.19(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)':
+ '@coinbase/onchainkit@0.38.19(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)':
dependencies:
'@farcaster/frame-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
@@ -14501,7 +15231,7 @@ snapshots:
react-dom: 19.2.3(react@19.2.3)
tailwind-merge: 2.6.0
viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
transitivePeerDependencies:
- '@azure/app-configuration'
- '@azure/cosmos'
@@ -14540,7 +15270,7 @@ snapshots:
- ws
- zod
- '@coinbase/onchainkit@1.1.2(@tanstack/query-core@5.90.11)(@types/react-dom@19.2.1(@types/react@19.2.1))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)':
+ '@coinbase/onchainkit@1.1.2(@tanstack/query-core@5.90.11)(@types/react-dom@19.2.1(@types/react@19.2.1))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(zod@4.1.13)':
dependencies:
'@farcaster/miniapp-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
@@ -14561,7 +15291,7 @@ snapshots:
tailwind-merge: 3.4.0
usehooks-ts: 3.1.1(react@19.2.3)
viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
transitivePeerDependencies:
- '@tanstack/query-core'
- '@types/react'
@@ -14708,21 +15438,6 @@ snapshots:
- utf-8-validate
- zod
- '@coinbase/x402@2.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
- dependencies:
- '@coinbase/cdp-sdk': 1.39.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
- '@x402/core': 2.3.1
- viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
- zod: 3.25.76
- transitivePeerDependencies:
- - bufferutil
- - debug
- - encoding
- - fastestsmallesttextencoderdecoder
- - typescript
- - utf-8-validate
- - ws
-
'@colors/colors@1.6.0': {}
'@craftamap/esbuild-plugin-html@0.9.0(bufferutil@4.0.9)(esbuild@0.25.9)(utf-8-validate@5.0.10)':
@@ -14900,7 +15615,7 @@ snapshots:
'@es-joy/jsdoccomment@0.50.2':
dependencies:
'@types/estree': 1.0.8
- '@typescript-eslint/types': 8.40.0
+ '@typescript-eslint/types': 8.48.0
comment-parser: 1.4.1
esquery: 1.6.0
jsdoc-type-pratt-parser: 4.1.0
@@ -15304,6 +16019,29 @@ snapshots:
typescript: 5.9.2
zod: 3.25.76
+ '@fastify/ajv-compiler@4.0.5':
+ dependencies:
+ ajv: 8.17.1
+ ajv-formats: 3.0.1(ajv@8.17.1)
+ fast-uri: 3.0.6
+
+ '@fastify/error@4.2.0': {}
+
+ '@fastify/fast-json-stringify-compiler@5.0.3':
+ dependencies:
+ fast-json-stringify: 6.3.0
+
+ '@fastify/forwarded@3.0.1': {}
+
+ '@fastify/merge-json-schemas@0.2.1':
+ dependencies:
+ dequal: 2.0.3
+
+ '@fastify/proxy-addr@5.1.0':
+ dependencies:
+ '@fastify/forwarded': 3.0.1
+ ipaddr.js: 2.3.0
+
'@floating-ui/core@1.7.3':
dependencies:
'@floating-ui/utils': 0.2.10
@@ -15396,9 +16134,9 @@ snapshots:
dependencies:
react: 19.2.3
- '@hono/node-server@1.19.0(hono@4.9.2)':
+ '@hono/node-server@1.19.0(hono@4.10.7)':
dependencies:
- hono: 4.9.2
+ hono: 4.10.7
'@hpke/chacha20poly1305@1.7.1':
dependencies:
@@ -16087,6 +16825,8 @@ snapshots:
'@paulmillr/qr@0.2.1': {}
+ '@pinojs/redact@0.4.0': {}
+
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -17364,7 +18104,7 @@ snapshots:
dependencies:
'@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)
- '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3)
viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
transitivePeerDependencies:
@@ -17398,7 +18138,7 @@ snapshots:
dependencies:
'@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)
- '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
valtio: 1.13.2(@types/react@19.2.1)(react@19.2.3)
viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
transitivePeerDependencies:
@@ -18075,7 +18815,7 @@ snapshots:
'@reown/appkit-polyfills': 1.7.8
'@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)
'@walletconnect/logger': 2.1.2
- '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3)
viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
transitivePeerDependencies:
@@ -18112,7 +18852,7 @@ snapshots:
'@reown/appkit-polyfills': 1.7.8
'@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)
'@walletconnect/logger': 2.1.2
- '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
valtio: 1.13.2(@types/react@19.2.1)(react@19.2.3)
viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
transitivePeerDependencies:
@@ -18290,7 +19030,7 @@ snapshots:
'@reown/appkit-utils': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@19.2.3))(zod@4.1.13)
'@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)
'@walletconnect/types': 2.21.0(@upstash/redis@1.35.3)
- '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
bs58: 6.0.0
valtio: 1.13.2(@types/react@19.1.10)(react@19.2.3)
viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
@@ -18332,7 +19072,7 @@ snapshots:
'@reown/appkit-utils': 1.7.8(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.2.1)(react@19.2.3))(zod@4.1.13)
'@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)
'@walletconnect/types': 2.21.0(@upstash/redis@1.35.3)
- '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
bs58: 6.0.0
valtio: 1.13.2(@types/react@19.2.1)(react@19.2.3)
viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
@@ -18555,10 +19295,6 @@ snapshots:
dependencies:
'@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
- '@solana-program/system@0.8.1(@solana/kit@3.0.3(typescript@5.9.2))':
- dependencies:
- '@solana/kit': 3.0.3(typescript@5.9.2)
-
'@solana-program/token-2022@0.4.2(@solana/kit@6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10))(@solana/sysvars@6.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))':
dependencies:
'@solana/kit': 6.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)
@@ -18586,10 +19322,6 @@ snapshots:
dependencies:
'@solana/kit': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
- '@solana-program/token@0.6.0(@solana/kit@3.0.3(typescript@5.9.2))':
- dependencies:
- '@solana/kit': 3.0.3(typescript@5.9.2)
-
'@solana-program/token@0.9.0(@solana/kit@5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))':
dependencies:
'@solana/kit': 5.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
@@ -18598,6 +19330,18 @@ snapshots:
dependencies:
'@solana/kit': 5.1.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
+ dependencies:
+ '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/codecs-core': 2.3.0(typescript@5.9.2)
+ '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-spec': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+
'@solana/accounts@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
dependencies:
'@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -18917,6 +19661,17 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
+ '@solana/codecs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
+ dependencies:
+ '@solana/codecs-core': 2.3.0(typescript@5.9.2)
+ '@solana/codecs-data-structures': 2.3.0(typescript@5.9.2)
+ '@solana/codecs-numbers': 2.3.0(typescript@5.9.2)
+ '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/options': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+
'@solana/codecs@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
dependencies:
'@solana/codecs-core': 3.0.3(typescript@5.9.2)
@@ -18971,7 +19726,7 @@ snapshots:
'@solana/errors@2.3.0(typescript@5.9.2)':
dependencies:
chalk: 5.6.2
- commander: 14.0.2
+ commander: 14.0.3
typescript: 5.9.2
'@solana/errors@3.0.3(typescript@5.9.2)':
@@ -19173,33 +19928,32 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- '@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
- '@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/codecs': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/errors': 3.0.3(typescript@5.9.2)
- '@solana/functional': 3.0.3(typescript@5.9.2)
- '@solana/instruction-plans': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/instructions': 3.0.3(typescript@5.9.2)
- '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/programs': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/rpc-parsed-types': 3.0.3(typescript@5.9.2)
- '@solana/rpc-spec-types': 3.0.3(typescript@5.9.2)
- '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
- '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/signers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/sysvars': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/transaction-confirmation': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
- '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ '@solana/functional': 2.3.0(typescript@5.9.2)
+ '@solana/instructions': 2.3.0(typescript@5.9.2)
+ '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
typescript: 5.9.2
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- ws
- '@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ '@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -19213,11 +19967,11 @@ snapshots:
'@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/rpc-parsed-types': 3.0.3(typescript@5.9.2)
'@solana/rpc-spec-types': 3.0.3(typescript@5.9.2)
- '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/signers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/sysvars': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/transaction-confirmation': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/transaction-confirmation': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
typescript: 5.9.2
@@ -19225,7 +19979,7 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
- '@solana/kit@3.0.3(typescript@5.9.2)':
+ '@solana/kit@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -19239,11 +19993,11 @@ snapshots:
'@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/rpc-parsed-types': 3.0.3(typescript@5.9.2)
'@solana/rpc-spec-types': 3.0.3(typescript@5.9.2)
- '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/signers': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/sysvars': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/transaction-confirmation': 3.0.3(typescript@5.9.2)
+ '@solana/transaction-confirmation': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
typescript: 5.9.2
@@ -19397,6 +20151,17 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
+ '@solana/options@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
+ dependencies:
+ '@solana/codecs-core': 2.3.0(typescript@5.9.2)
+ '@solana/codecs-data-structures': 2.3.0(typescript@5.9.2)
+ '@solana/codecs-numbers': 2.3.0(typescript@5.9.2)
+ '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+
'@solana/options@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
dependencies:
'@solana/codecs-core': 3.0.3(typescript@5.9.2)
@@ -19476,6 +20241,14 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
+ '@solana/programs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
+ dependencies:
+ '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+
'@solana/programs@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
dependencies:
'@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -19761,6 +20534,15 @@ snapshots:
typescript: 5.9.2
ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ '@solana/functional': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2)
+ '@solana/subscribable': 2.3.0(typescript@5.9.2)
+ typescript: 5.9.2
+ ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+
'@solana/rpc-subscriptions-channel-websocket@3.0.3(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/errors': 3.0.3(typescript@5.9.2)
@@ -19870,6 +20652,24 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
+ '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ dependencies:
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2)
+ '@solana/functional': 2.3.0(typescript@5.9.2)
+ '@solana/promises': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/subscribable': 2.3.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+ - ws
+
'@solana/rpc-subscriptions@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/errors': 3.0.3(typescript@5.9.2)
@@ -20024,7 +20824,7 @@ snapshots:
'@solana/rpc-spec': 2.3.0(typescript@5.9.2)
'@solana/rpc-spec-types': 2.3.0(typescript@5.9.2)
typescript: 5.9.2
- undici-types: 7.16.0
+ undici-types: 7.22.0
'@solana/rpc-transport-http@3.0.3(typescript@5.9.2)':
dependencies:
@@ -20032,7 +20832,7 @@ snapshots:
'@solana/rpc-spec': 3.0.3(typescript@5.9.2)
'@solana/rpc-spec-types': 3.0.3(typescript@5.9.2)
typescript: 5.9.2
- undici-types: 7.16.0
+ undici-types: 7.22.0
'@solana/rpc-transport-http@5.0.0(typescript@5.9.2)':
dependencies:
@@ -20196,6 +20996,20 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
+ '@solana/signers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
+ dependencies:
+ '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/codecs-core': 2.3.0(typescript@5.9.2)
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ '@solana/instructions': 2.3.0(typescript@5.9.2)
+ '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/nominal-types': 2.3.0(typescript@5.9.2)
+ '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+
'@solana/signers@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
dependencies:
'@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -20312,6 +21126,16 @@ snapshots:
optionalDependencies:
typescript: 5.9.2
+ '@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
+ dependencies:
+ '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - fastestsmallesttextencoderdecoder
+
'@solana/sysvars@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)':
dependencies:
'@solana/accounts': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -20370,24 +21194,24 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
- '@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
- '@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/errors': 3.0.3(typescript@5.9.2)
- '@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/promises': 3.0.3(typescript@5.9.2)
- '@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
- '@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/errors': 2.3.0(typescript@5.9.2)
+ '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/promises': 2.3.0(typescript@5.9.2)
+ '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
+ '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
typescript: 5.9.2
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- ws
- '@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
+ '@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -20395,7 +21219,7 @@ snapshots:
'@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/promises': 3.0.3(typescript@5.9.2)
'@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -20404,7 +21228,7 @@ snapshots:
- fastestsmallesttextencoderdecoder
- ws
- '@solana/transaction-confirmation@3.0.3(typescript@5.9.2)':
+ '@solana/transaction-confirmation@3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))':
dependencies:
'@solana/addresses': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/codecs-strings': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -20412,7 +21236,7 @@ snapshots:
'@solana/keys': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/promises': 3.0.3(typescript@5.9.2)
'@solana/rpc': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
- '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
+ '@solana/rpc-subscriptions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))
'@solana/rpc-types': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/transaction-messages': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
'@solana/transactions': 3.0.3(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)
@@ -20691,6 +21515,31 @@ snapshots:
'@stablelib/wipe@1.0.1': {}
+ '@stellar/js-xdr@3.1.2': {}
+
+ '@stellar/stellar-base@14.1.0':
+ dependencies:
+ '@noble/curves': 1.9.7
+ '@stellar/js-xdr': 3.1.2
+ base32.js: 0.1.0
+ bignumber.js: 9.3.1
+ buffer: 6.0.3
+ sha.js: 2.4.12
+
+ '@stellar/stellar-sdk@14.6.1':
+ dependencies:
+ '@stellar/stellar-base': 14.1.0
+ axios: 1.13.4
+ bignumber.js: 9.3.1
+ commander: 14.0.3
+ eventsource: 2.0.2
+ feaxios: 0.0.23
+ randombytes: 2.1.0
+ toml: 3.0.0
+ urijs: 1.19.11
+ transitivePeerDependencies:
+ - debug
+
'@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.3)':
dependencies:
'@babel/core': 7.28.3
@@ -20940,6 +21789,13 @@ snapshots:
'@types/estree@1.0.8': {}
+ '@types/express-serve-static-core@4.19.8':
+ dependencies:
+ '@types/node': 22.17.2
+ '@types/qs': 6.14.0
+ '@types/range-parser': 1.2.7
+ '@types/send': 0.17.5
+
'@types/express-serve-static-core@5.0.7':
dependencies:
'@types/node': 22.17.2
@@ -20947,6 +21803,13 @@ snapshots:
'@types/range-parser': 1.2.7
'@types/send': 0.17.5
+ '@types/express@4.17.25':
+ dependencies:
+ '@types/body-parser': 1.19.6
+ '@types/express-serve-static-core': 4.19.8
+ '@types/qs': 6.14.0
+ '@types/serve-static': 1.15.8
+
'@types/express@5.0.3':
dependencies:
'@types/body-parser': 1.19.6
@@ -21536,19 +22399,19 @@ snapshots:
- utf-8-validate
- zod
- '@wagmi/connectors@6.2.0(7c0fb5ad6e91c192e500875cd884eb4c)':
+ '@wagmi/connectors@6.2.0(1aee367860625464d156864b651beb8c)':
dependencies:
- '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)
- '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76)
- '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))
+ '@base-org/account': 2.4.0(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
+ '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.1)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
'@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
- '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
- '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
- '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))
- '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
cbw-sdk: '@coinbase/wallet-sdk@3.9.3'
- porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.1.1))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))
- viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))
+ viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -21589,19 +22452,19 @@ snapshots:
- ws
- zod
- '@wagmi/connectors@6.2.0(826cb7847d3729c23c5bbd1840c9486a)':
+ '@wagmi/connectors@6.2.0(26b6de6e2b894d3d8ec3de3fd9d7a34e)':
dependencies:
- '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)
- '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(zod@3.25.76)
- '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))
+ '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
+ '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
'@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
- '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
- '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
- '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))
- '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
cbw-sdk: '@coinbase/wallet-sdk@3.9.3'
- porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))
- viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))
+ viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -21642,19 +22505,19 @@ snapshots:
- ws
- zod
- '@wagmi/connectors@6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)':
+ '@wagmi/connectors@6.2.0(7c0fb5ad6e91c192e500875cd884eb4c)':
dependencies:
- '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
- '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
- '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)
+ '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))
'@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
- '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
- '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))
+ '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
cbw-sdk: '@coinbase/wallet-sdk@3.9.3'
- porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))
- viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.1.1))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))
+ viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -21695,19 +22558,19 @@ snapshots:
- ws
- zod
- '@wagmi/connectors@6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)':
+ '@wagmi/connectors@6.2.0(826cb7847d3729c23c5bbd1840c9486a)':
dependencies:
- '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
- '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
- '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)
+ '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))
'@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
- '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
- '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))
+ '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
cbw-sdk: '@coinbase/wallet-sdk@3.9.3'
- porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))
- viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(react@19.2.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.1))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))
+ viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -21748,19 +22611,19 @@ snapshots:
- ws
- zod
- '@wagmi/connectors@6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)':
+ '@wagmi/connectors@6.2.0(87f53a8d2b85f9075dbb2195dfb00723)':
dependencies:
- '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
'@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
- '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
'@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
'@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
- '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
cbw-sdk: '@coinbase/wallet-sdk@3.9.3'
- porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))
- viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))
+ viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -21801,19 +22664,19 @@ snapshots:
- ws
- zod
- '@wagmi/connectors@6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)':
+ '@wagmi/connectors@6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)':
dependencies:
- '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
'@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
- '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
'@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
'@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
'@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
cbw-sdk: '@coinbase/wallet-sdk@3.9.3'
- porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))
- viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))
+ viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -21854,19 +22717,19 @@ snapshots:
- ws
- zod
- '@wagmi/connectors@6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)':
+ '@wagmi/connectors@6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)':
dependencies:
- '@base-org/account': 2.4.0(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
- '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.1)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
- '@gemini-wallet/core': 0.3.2(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@base-org/account': 2.4.0(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
+ '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@gemini-wallet/core': 0.3.2(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
'@metamask/sdk': 0.33.1(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)
'@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
- '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
+ '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
cbw-sdk: '@coinbase/wallet-sdk@3.9.3'
- porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))
- viem: 2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ porto: 0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))
+ viem: 2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
optionalDependencies:
typescript: 5.9.2
transitivePeerDependencies:
@@ -22221,6 +23084,49 @@ snapshots:
- utf-8-validate
- zod
+ '@walletconnect/core@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ dependencies:
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3)
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.0(@upstash/redis@1.35.3)
+ '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/window-getters': 1.0.1
+ es-toolkit: 1.33.0
+ events: 3.3.0
+ uint8arrays: 3.1.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
'@walletconnect/core@2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)':
dependencies:
'@walletconnect/heartbeat': 1.2.2
@@ -22307,6 +23213,49 @@ snapshots:
- utf-8-validate
- zod
+ '@walletconnect/core@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ dependencies:
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3)
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.1(@upstash/redis@1.35.3)
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/window-getters': 1.0.1
+ es-toolkit: 1.33.0
+ events: 3.3.0
+ uint8arrays: 3.1.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
'@walletconnect/environment@1.0.1':
dependencies:
tslib: 1.14.1
@@ -22439,10 +23388,10 @@ snapshots:
'@walletconnect/jsonrpc-types': 1.0.4
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3)
- '@walletconnect/sign-client': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@walletconnect/types': 2.21.1(@upstash/redis@1.35.3)
- '@walletconnect/universal-provider': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
events: 3.3.0
transitivePeerDependencies:
- '@azure/app-configuration'
@@ -22479,10 +23428,10 @@ snapshots:
'@walletconnect/jsonrpc-types': 1.0.4
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3)
- '@walletconnect/sign-client': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@walletconnect/types': 2.21.1(@upstash/redis@1.35.3)
- '@walletconnect/universal-provider': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
- '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
events: 3.3.0
transitivePeerDependencies:
- '@azure/app-configuration'
@@ -22673,16 +23622,86 @@ snapshots:
- utf-8-validate
- zod
+ '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ dependencies:
+ '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.0(@upstash/redis@1.35.3)
+ '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
'@walletconnect/sign-client@2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)':
dependencies:
- '@walletconnect/core': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@walletconnect/core': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/heartbeat': 1.2.2
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.1(@upstash/redis@1.35.3)
+ '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
+ '@walletconnect/sign-client@2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ dependencies:
+ '@walletconnect/core': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@walletconnect/events': 1.0.1
'@walletconnect/heartbeat': 1.2.2
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/logger': 2.1.2
'@walletconnect/time': 1.0.2
'@walletconnect/types': 2.21.1(@upstash/redis@1.35.3)
- '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)
+ '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
events: 3.3.0
transitivePeerDependencies:
- '@azure/app-configuration'
@@ -22708,16 +23727,16 @@ snapshots:
- utf-8-validate
- zod
- '@walletconnect/sign-client@2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ '@walletconnect/sign-client@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
dependencies:
- '@walletconnect/core': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/core': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
'@walletconnect/events': 1.0.1
'@walletconnect/heartbeat': 1.2.2
'@walletconnect/jsonrpc-utils': 1.0.8
'@walletconnect/logger': 2.1.2
'@walletconnect/time': 1.0.2
'@walletconnect/types': 2.21.1(@upstash/redis@1.35.3)
- '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
events: 3.3.0
transitivePeerDependencies:
- '@azure/app-configuration'
@@ -22881,6 +23900,45 @@ snapshots:
- utf-8-validate
- zod
+ '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13)
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3)
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/sign-client': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/types': 2.21.0(@upstash/redis@1.35.3)
+ '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ es-toolkit: 1.33.0
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
'@walletconnect/universal-provider@2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)':
dependencies:
'@walletconnect/events': 1.0.1
@@ -22959,6 +24017,45 @@ snapshots:
- utf-8-validate
- zod
+ '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ dependencies:
+ '@walletconnect/events': 1.0.1
+ '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13)
+ '@walletconnect/jsonrpc-provider': 1.0.14
+ '@walletconnect/jsonrpc-types': 1.0.4
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3)
+ '@walletconnect/logger': 2.1.2
+ '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ '@walletconnect/types': 2.21.1(@upstash/redis@1.35.3)
+ '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ es-toolkit: 1.33.0
+ events: 3.3.0
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - encoding
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
'@walletconnect/utils@2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)':
dependencies:
'@noble/ciphers': 1.2.1
@@ -23045,6 +24142,49 @@ snapshots:
- utf-8-validate
- zod
+ '@walletconnect/utils@2.21.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ dependencies:
+ '@noble/ciphers': 1.2.1
+ '@noble/curves': 1.8.1
+ '@noble/hashes': 1.7.1
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3)
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.0(@upstash/redis@1.35.3)
+ '@walletconnect/window-getters': 1.0.1
+ '@walletconnect/window-metadata': 1.0.1
+ bs58: 6.0.0
+ detect-browser: 5.3.0
+ query-string: 7.1.3
+ uint8arrays: 3.1.0
+ viem: 2.23.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
'@walletconnect/utils@2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)':
dependencies:
'@noble/ciphers': 1.2.1
@@ -23131,6 +24271,49 @@ snapshots:
- utf-8-validate
- zod
+ '@walletconnect/utils@2.21.1(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)':
+ dependencies:
+ '@noble/ciphers': 1.2.1
+ '@noble/curves': 1.8.1
+ '@noble/hashes': 1.7.1
+ '@walletconnect/jsonrpc-utils': 1.0.8
+ '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3)
+ '@walletconnect/relay-api': 1.0.11
+ '@walletconnect/relay-auth': 1.1.0
+ '@walletconnect/safe-json': 1.0.2
+ '@walletconnect/time': 1.0.2
+ '@walletconnect/types': 2.21.1(@upstash/redis@1.35.3)
+ '@walletconnect/window-getters': 1.0.1
+ '@walletconnect/window-metadata': 1.0.1
+ bs58: 6.0.0
+ detect-browser: 5.3.0
+ query-string: 7.1.3
+ uint8arrays: 3.1.0
+ viem: 2.23.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)
+ transitivePeerDependencies:
+ - '@azure/app-configuration'
+ - '@azure/cosmos'
+ - '@azure/data-tables'
+ - '@azure/identity'
+ - '@azure/keyvault-secrets'
+ - '@azure/storage-blob'
+ - '@capacitor/preferences'
+ - '@deno/kv'
+ - '@netlify/blobs'
+ - '@planetscale/database'
+ - '@react-native-async-storage/async-storage'
+ - '@upstash/redis'
+ - '@vercel/blob'
+ - '@vercel/kv'
+ - aws4fetch
+ - bufferutil
+ - db0
+ - ioredis
+ - typescript
+ - uploadthing
+ - utf-8-validate
+ - zod
+
'@walletconnect/window-getters@1.0.1':
dependencies:
tslib: 1.14.1
@@ -23140,10 +24323,6 @@ snapshots:
'@walletconnect/window-getters': 1.0.1
tslib: 1.14.1
- '@x402/core@2.3.1':
- dependencies:
- zod: 3.25.76
-
'@xmldom/xmldom@0.8.11': {}
abbrev@1.1.1: {}
@@ -23192,6 +24371,8 @@ snapshots:
dependencies:
event-target-shim: 5.0.1
+ abstract-logging@2.0.1: {}
+
accepts@1.3.8:
dependencies:
mime-types: 2.1.35
@@ -23227,6 +24408,10 @@ snapshots:
clean-stack: 2.2.0
indent-string: 4.0.0
+ ajv-formats@3.0.1(ajv@8.17.1):
+ optionalDependencies:
+ ajv: 8.17.1
+
ajv-keywords@3.5.2(ajv@6.12.6):
dependencies:
ajv: 6.12.6
@@ -23301,7 +24486,7 @@ snapshots:
minimatch: 10.0.3
plist: 3.1.0
resedit: 1.7.2
- semver: 7.7.2
+ semver: 7.7.3
tar: 6.2.1
temp-file: 3.4.0
tiny-async-pool: 1.3.0
@@ -23422,6 +24607,11 @@ snapshots:
dependencies:
possible-typed-array-names: 1.1.0
+ avvio@9.2.0:
+ dependencies:
+ '@fastify/error': 4.2.0
+ fastq: 1.19.1
+
axe-core@4.10.3: {}
axios-mock-adapter@1.22.0(axios@1.13.2):
@@ -23435,6 +24625,11 @@ snapshots:
axios: 1.13.2
is-retry-allowed: 2.2.0
+ axios-retry@4.5.0(axios@1.13.4):
+ dependencies:
+ axios: 1.13.4
+ is-retry-allowed: 2.2.0
+
axios@1.13.2:
dependencies:
follow-redirects: 1.15.11
@@ -23443,6 +24638,14 @@ snapshots:
transitivePeerDependencies:
- debug
+ axios@1.13.4:
+ dependencies:
+ follow-redirects: 1.15.11
+ form-data: 4.0.4
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
axobject-query@4.1.0: {}
babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.3):
@@ -23479,6 +24682,8 @@ snapshots:
base-x@5.0.1: {}
+ base32.js@0.1.0: {}
+
base64-js@1.5.1: {}
big.js@6.2.2: {}
@@ -23930,6 +25135,8 @@ snapshots:
cookie@0.7.2: {}
+ cookie@1.1.1: {}
+
core-js-compat@3.45.1:
dependencies:
browserslist: 4.25.3
@@ -24125,6 +25332,8 @@ snapshots:
depd@2.0.0: {}
+ dequal@2.0.3: {}
+
derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.1.10)(react@19.1.1)):
dependencies:
valtio: 1.13.2(@types/react@19.1.10)(react@19.1.1)
@@ -24824,7 +26033,7 @@ snapshots:
espree: 10.4.0
esquery: 1.6.0
parse-imports-exports: 0.2.4
- semver: 7.7.2
+ semver: 7.7.3
spdx-expression-parse: 4.0.0
transitivePeerDependencies:
- supports-color
@@ -24885,6 +26094,15 @@ snapshots:
optionalDependencies:
eslint-config-prettier: 10.1.8(eslint@9.33.0(jiti@2.6.1))
+ eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.6.1)))(eslint@9.33.0(jiti@2.6.1))(prettier@3.6.2):
+ dependencies:
+ eslint: 9.33.0(jiti@2.6.1)
+ prettier: 3.6.2
+ prettier-linter-helpers: 1.0.0
+ synckit: 0.11.11
+ optionalDependencies:
+ eslint-config-prettier: 10.1.8(eslint@9.33.0(jiti@2.6.1))
+
eslint-plugin-react-hooks@5.2.0(eslint@8.57.1):
dependencies:
eslint: 8.57.1
@@ -25132,6 +26350,8 @@ snapshots:
eventsource-parser@3.0.5: {}
+ eventsource@2.0.2: {}
+
eventsource@3.0.7:
dependencies:
eventsource-parser: 3.0.5
@@ -25244,6 +26464,8 @@ snapshots:
eyes@0.1.8: {}
+ fast-decode-uri-component@1.0.1: {}
+
fast-deep-equal@3.1.3: {}
fast-diff@1.3.0: {}
@@ -25266,8 +26488,21 @@ snapshots:
fast-json-stable-stringify@2.1.0: {}
+ fast-json-stringify@6.3.0:
+ dependencies:
+ '@fastify/merge-json-schemas': 0.2.1
+ ajv: 8.17.1
+ ajv-formats: 3.0.1(ajv@8.17.1)
+ fast-uri: 3.0.6
+ json-schema-ref-resolver: 3.0.0
+ rfdc: 1.4.1
+
fast-levenshtein@2.0.6: {}
+ fast-querystring@1.1.2:
+ dependencies:
+ fast-decode-uri-component: 1.0.1
+
fast-redact@3.5.0: {}
fast-safe-stringify@2.1.1: {}
@@ -25280,6 +26515,24 @@ snapshots:
fastestsmallesttextencoderdecoder@1.0.22: {}
+ fastify@5.8.2:
+ dependencies:
+ '@fastify/ajv-compiler': 4.0.5
+ '@fastify/error': 4.2.0
+ '@fastify/fast-json-stringify-compiler': 5.0.3
+ '@fastify/proxy-addr': 5.1.0
+ abstract-logging: 2.0.1
+ avvio: 9.2.0
+ fast-json-stringify: 6.3.0
+ find-my-way: 9.5.0
+ light-my-request: 6.6.0
+ pino: 10.3.1
+ process-warning: 5.0.0
+ rfdc: 1.4.1
+ secure-json-parse: 4.1.0
+ semver: 7.7.3
+ toad-cache: 3.7.0
+
fastq@1.19.1:
dependencies:
reusify: 1.1.0
@@ -25292,6 +26545,10 @@ snapshots:
optionalDependencies:
picomatch: 4.0.3
+ feaxios@0.0.23:
+ dependencies:
+ is-retry-allowed: 3.0.0
+
fecha@4.2.3: {}
fetch-blob@3.2.0:
@@ -25342,6 +26599,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ find-my-way@9.5.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-querystring: 1.1.2
+ safe-regex2: 5.1.0
+
find-up@4.1.0:
dependencies:
locate-path: 5.0.0
@@ -25663,8 +26926,6 @@ snapshots:
hono@4.10.7: {}
- hono@4.9.2: {}
-
hosted-git-info@4.1.0:
dependencies:
lru-cache: 6.0.0
@@ -25790,6 +27051,8 @@ snapshots:
ipaddr.js@1.9.1: {}
+ ipaddr.js@2.3.0: {}
+
iron-webcrypto@1.2.1: {}
is-arguments@1.2.0:
@@ -25908,6 +27171,8 @@ snapshots:
is-retry-allowed@2.2.0: {}
+ is-retry-allowed@3.0.0: {}
+
is-set@2.0.3: {}
is-shared-array-buffer@1.0.4:
@@ -26053,7 +27318,7 @@ snapshots:
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.2.0
- ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
@@ -26075,6 +27340,10 @@ snapshots:
json-rpc-random-id@1.0.1: {}
+ json-schema-ref-resolver@3.0.0:
+ dependencies:
+ dequal: 2.0.3
+
json-schema-traverse@0.4.1: {}
json-schema-traverse@1.0.0: {}
@@ -26151,6 +27420,12 @@ snapshots:
libphonenumber-js@1.12.13: {}
+ light-my-request@6.6.0:
+ dependencies:
+ cookie: 1.1.1
+ process-warning: 4.0.1
+ set-cookie-parser: 2.7.2
+
lightningcss-android-arm64@1.30.2:
optional: true
@@ -26674,6 +27949,8 @@ snapshots:
on-exit-leak-free@0.2.0: {}
+ on-exit-leak-free@2.1.2: {}
+
on-finished@2.4.1:
dependencies:
ee-first: 1.1.1
@@ -27079,8 +28356,28 @@ snapshots:
duplexify: 4.1.3
split2: 4.2.0
+ pino-abstract-transport@3.0.0:
+ dependencies:
+ split2: 4.2.0
+
pino-std-serializers@4.0.0: {}
+ pino-std-serializers@7.1.0: {}
+
+ pino@10.3.1:
+ dependencies:
+ '@pinojs/redact': 0.4.0
+ atomic-sleep: 1.0.0
+ on-exit-leak-free: 2.1.2
+ pino-abstract-transport: 3.0.0
+ pino-std-serializers: 7.1.0
+ process-warning: 5.0.0
+ quick-format-unescaped: 4.0.4
+ real-require: 0.2.0
+ safe-stable-stringify: 2.5.0
+ sonic-boom: 4.2.1
+ thread-stream: 4.0.0
+
pino@7.11.0:
dependencies:
atomic-sleep: 1.0.0
@@ -27175,7 +28472,7 @@ snapshots:
- immer
- use-sync-external-store
- porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)):
+ porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)):
dependencies:
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
hono: 4.10.7
@@ -27189,13 +28486,13 @@ snapshots:
'@tanstack/react-query': 5.90.11(react@19.2.3)
react: 19.2.3
typescript: 5.9.2
- wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
transitivePeerDependencies:
- '@types/react'
- immer
- use-sync-external-store
- porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)):
+ porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)):
dependencies:
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
hono: 4.10.7
@@ -27209,13 +28506,13 @@ snapshots:
'@tanstack/react-query': 5.90.11(react@19.2.3)
react: 19.2.3
typescript: 5.9.2
- wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
transitivePeerDependencies:
- '@types/react'
- immer
- use-sync-external-store
- porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)):
+ porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)):
dependencies:
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
hono: 4.10.7
@@ -27229,13 +28526,13 @@ snapshots:
'@tanstack/react-query': 5.90.11(react@19.2.3)
react: 19.2.3
typescript: 5.9.2
- wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
transitivePeerDependencies:
- '@types/react'
- immer
- use-sync-external-store
- porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)):
+ porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)):
dependencies:
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
hono: 4.10.7
@@ -27249,13 +28546,13 @@ snapshots:
'@tanstack/react-query': 5.90.11(react@19.2.3)
react: 19.2.3
typescript: 5.9.2
- wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
transitivePeerDependencies:
- '@types/react'
- immer
- use-sync-external-store
- porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)):
+ porto@0.2.35(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)):
dependencies:
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
hono: 4.10.7
@@ -27269,7 +28566,7 @@ snapshots:
'@tanstack/react-query': 5.90.11(react@19.2.3)
react: 19.2.3
typescript: 5.9.2
- wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13)
+ wagmi: 2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
transitivePeerDependencies:
- '@types/react'
- immer
@@ -27356,6 +28653,10 @@ snapshots:
process-warning@1.0.0: {}
+ process-warning@4.0.1: {}
+
+ process-warning@5.0.0: {}
+
progress@2.0.3: {}
promise-inflight@1.0.1: {}
@@ -27490,6 +28791,10 @@ snapshots:
radix3@1.1.2: {}
+ randombytes@2.1.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
range-parser@1.2.1: {}
raw-body@2.5.2:
@@ -27642,6 +28947,8 @@ snapshots:
real-require@0.1.0: {}
+ real-require@0.2.0: {}
+
redaxios@0.5.1: {}
reflect-metadata@0.2.2: {}
@@ -27732,12 +29039,16 @@ snapshots:
onetime: 5.1.2
signal-exit: 3.0.7
+ ret@0.5.0: {}
+
retry@0.12.0: {}
retry@0.13.1: {}
reusify@1.1.0: {}
+ rfdc@1.4.1: {}
+
rimraf@2.6.3:
dependencies:
glob: 7.2.3
@@ -27805,7 +29116,7 @@ snapshots:
buffer: 6.0.3
eventemitter3: 5.0.1
uuid: 8.3.2
- ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)
+ ws: 8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
optionalDependencies:
bufferutil: 4.0.9
utf-8-validate: 5.0.10
@@ -27843,6 +29154,10 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
+ safe-regex2@5.1.0:
+ dependencies:
+ ret: 0.5.0
+
safe-stable-stringify@2.5.0: {}
safer-buffer@2.1.2: {}
@@ -27871,6 +29186,8 @@ snapshots:
node-addon-api: 5.1.0
node-gyp-build: 4.8.4
+ secure-json-parse@4.1.0: {}
+
selderee@0.11.0:
dependencies:
parseley: 0.12.1
@@ -27945,6 +29262,8 @@ snapshots:
set-blocking@2.0.0: {}
+ set-cookie-parser@2.7.2: {}
+
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
@@ -28055,7 +29374,7 @@ snapshots:
simple-update-notifier@2.0.0:
dependencies:
- semver: 7.7.2
+ semver: 7.7.3
simple-wcswidth@1.1.2: {}
@@ -28118,6 +29437,10 @@ snapshots:
dependencies:
atomic-sleep: 1.0.0
+ sonic-boom@4.2.1:
+ dependencies:
+ atomic-sleep: 1.0.0
+
source-map-js@1.2.1: {}
source-map-support@0.5.21:
@@ -28408,6 +29731,10 @@ snapshots:
dependencies:
real-require: 0.1.0
+ thread-stream@4.0.0:
+ dependencies:
+ real-require: 0.2.0
+
tiny-async-pool@1.3.0:
dependencies:
semver: 5.7.2
@@ -28454,8 +29781,12 @@ snapshots:
dependencies:
is-number: 7.0.0
+ toad-cache@3.7.0: {}
+
toidentifier@1.0.1: {}
+ toml@3.0.0: {}
+
tough-cookie@5.1.2:
dependencies:
tldts: 6.1.86
@@ -28771,6 +30102,8 @@ snapshots:
dependencies:
punycode: 2.3.1
+ urijs@1.19.11: {}
+
url-parse@1.5.10:
dependencies:
querystringify: 2.2.0
@@ -29292,10 +30625,10 @@ snapshots:
- ws
- zod
- wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13):
+ wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13):
dependencies:
'@tanstack/react-query': 5.90.11(react@19.2.3)
- '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)
+ '@wagmi/connectors': 6.2.0(26b6de6e2b894d3d8ec3de3fd9d7a34e)
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
react: 19.2.3
use-sync-external-store: 1.4.0(react@19.2.3)
@@ -29337,10 +30670,10 @@ snapshots:
- ws
- zod
- wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13):
+ wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13):
dependencies:
'@tanstack/react-query': 5.90.11(react@19.2.3)
- '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)
+ '@wagmi/connectors': 6.2.0(87f53a8d2b85f9075dbb2195dfb00723)
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
react: 19.2.3
use-sync-external-store: 1.4.0(react@19.2.3)
@@ -29382,10 +30715,10 @@ snapshots:
- ws
- zod
- wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13):
+ wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13):
dependencies:
'@tanstack/react-query': 5.90.11(react@19.2.3)
- '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)
+ '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
react: 19.2.3
use-sync-external-store: 1.4.0(react@19.2.3)
@@ -29427,10 +30760,10 @@ snapshots:
- ws
- zod
- wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13):
+ wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13):
dependencies:
'@tanstack/react-query': 5.90.11(react@19.2.3)
- '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)
+ '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13)
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.1.10)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.43.5(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
react: 19.2.3
use-sync-external-store: 1.4.0(react@19.2.3)
@@ -29472,10 +30805,10 @@ snapshots:
- ws
- zod
- wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13):
+ wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(ws@8.19.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.1.13):
dependencies:
'@tanstack/react-query': 5.90.11(react@19.2.3)
- '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(@wagmi/core@2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(wagmi@2.19.5(@tanstack/query-core@5.90.11)(@tanstack/react-query@5.90.11(react@19.2.3))(@types/react@19.2.1)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.2.3)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))(zod@4.1.13))(zod@4.1.13)
+ '@wagmi/connectors': 6.2.0(1aee367860625464d156864b651beb8c)
'@wagmi/core': 2.22.1(@tanstack/query-core@5.90.11)(@types/react@19.2.1)(react@19.2.3)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.2.3))(viem@2.40.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@4.1.13))
react: 19.2.3
use-sync-external-store: 1.4.0(react@19.2.3)
diff --git a/examples/typescript/pnpm-workspace.yaml b/examples/typescript/pnpm-workspace.yaml
index ef34f7b6d5..c98a039f40 100644
--- a/examples/typescript/pnpm-workspace.yaml
+++ b/examples/typescript/pnpm-workspace.yaml
@@ -2,7 +2,7 @@ packages:
- "clients/*"
- "servers/*"
- "fullstack/*"
- - "facilitator"
+ - "facilitator/*"
- "legacy/facilitator"
- "legacy/servers/*"
- "legacy/clients/*"
diff --git a/examples/typescript/servers/advanced/.env-local b/examples/typescript/servers/advanced/.env-local
index 9ec8cdc305..e16c5a2761 100644
--- a/examples/typescript/servers/advanced/.env-local
+++ b/examples/typescript/servers/advanced/.env-local
@@ -1,2 +1,4 @@
EVM_ADDRESS=
+SVM_ADDRESS=
+STELLAR_ADDRESS=
FACILITATOR_URL=
\ No newline at end of file
diff --git a/examples/typescript/servers/advanced/README.md b/examples/typescript/servers/advanced/README.md
index 8372d128a8..17d98b2314 100644
--- a/examples/typescript/servers/advanced/README.md
+++ b/examples/typescript/servers/advanced/README.md
@@ -5,10 +5,12 @@ Express.js server demonstrating advanced x402 patterns including dynamic pricing
```typescript
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { ExactStellarScheme } from "@x402/stellar/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
const resourceServer = new x402ResourceServer(new HTTPFacilitatorClient({ url: facilitatorUrl }))
.register("eip155:84532", new ExactEvmScheme())
+ .register("stellar:*", new ExactStellarScheme())
.onBeforeVerify(async ctx => console.log("Verifying payment..."))
.onAfterSettle(async ctx => console.log("Settled:", ctx.result.transaction));
@@ -23,6 +25,14 @@ app.use(
payTo: evmAddress,
},
},
+ "GET /weather-stellar": {
+ accepts: {
+ scheme: "exact",
+ price: ctx => (ctx.adapter.getQueryParam?.("tier") === "premium" ? "$0.01" : "$0.001"),
+ network: "stellar:*",
+ payTo: stellarAddress,
+ },
+ },
},
resourceServer,
),
@@ -48,6 +58,7 @@ and fill required environment variables:
- `FACILITATOR_URL` - Facilitator endpoint URL
- `EVM_ADDRESS` - Ethereum address to receive payments
+- `STELLAR_ADDRESS` - Stellar public address (starts with `G`) to receive payments
2. Install and build all packages from the typescript examples root:
@@ -63,6 +74,16 @@ cd servers/advanced
pnpm dev
```
+### Account Setup Instructions
+
+#### Stellar Testnet
+
+Stellar accounts need to be created and funded with both XLM and USDC. Instructions:
+
+1. Go to [Stellar Laboratory](https://lab.stellar.org/account/create) ➡️ Generate keypair ➡️ Fund account with Friendbot, then copy the `Secret` and `Public` keys so you can use them.
+2. Add USDC trustline (required to transact USDC): go to [Fund Account](https://lab.stellar.org/account/fund) ➡️ Paste your `Public Key` ➡️ Add USDC Trustline ➡️ paste your `Secret key` ➡️ Sign transaction ➡️ Add Trustline.
+3. Get testnet USDC from [Circle Faucet](https://faucet.circle.com/) (select Stellar network).
+
## Available Examples
Each example demonstrates a specific advanced pattern:
diff --git a/examples/typescript/servers/advanced/all_networks.ts b/examples/typescript/servers/advanced/all_networks.ts
index e3b474e236..8d7f639642 100644
--- a/examples/typescript/servers/advanced/all_networks.ts
+++ b/examples/typescript/servers/advanced/all_networks.ts
@@ -5,7 +5,7 @@
* optional chain configuration via environment variables.
*
* New chain support should be added here in alphabetic order by network prefix
- * (e.g., "eip155" before "solana").
+ * (e.g., "eip155" before "solana" before "stellar").
*/
import { config } from "dotenv";
@@ -13,6 +13,7 @@ import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { ExactSvmScheme } from "@x402/svm/exact/server";
+import { ExactStellarScheme } from "@x402/stellar/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";
config();
@@ -20,10 +21,11 @@ config();
// Configuration - optional per network
const evmAddress = process.env.EVM_ADDRESS as `0x${string}` | undefined;
const svmAddress = process.env.SVM_ADDRESS as string | undefined;
+const stellarAddress = process.env.STELLAR_ADDRESS as string | undefined;
// Validate at least one address is provided
-if (!evmAddress && !svmAddress) {
- console.error("❌ At least one of EVM_ADDRESS or SVM_ADDRESS is required");
+if (!evmAddress && !svmAddress && !stellarAddress) {
+ console.error("❌ At least one of EVM_ADDRESS, SVM_ADDRESS, or STELLAR_ADDRESS is required");
process.exit(1);
}
@@ -36,6 +38,7 @@ if (!facilitatorUrl) {
// Network configuration
const EVM_NETWORK = "eip155:84532" as const; // Base Sepolia
const SVM_NETWORK = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1" as const; // Solana Devnet
+const STELLAR_NETWORK = "stellar:testnet" as const; // Stellar Testnet
// Build accepts array dynamically based on configured addresses
const accepts: Array<{
@@ -60,6 +63,14 @@ if (svmAddress) {
payTo: svmAddress,
});
}
+if (stellarAddress) {
+ accepts.push({
+ scheme: "exact",
+ price: "$0.001",
+ network: STELLAR_NETWORK,
+ payTo: stellarAddress,
+ });
+}
// Create facilitator client
const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
@@ -72,6 +83,9 @@ if (evmAddress) {
if (svmAddress) {
server.register(SVM_NETWORK, new ExactSvmScheme());
}
+if (stellarAddress) {
+ server.register(STELLAR_NETWORK, new ExactStellarScheme());
+}
// Create Express app
const app = express();
@@ -115,6 +129,9 @@ app.listen(port, () => {
if (svmAddress) {
console.log(` SVM: ${svmAddress} on ${SVM_NETWORK}`);
}
+ if (stellarAddress) {
+ console.log(` Stellar: ${stellarAddress} on ${STELLAR_NETWORK}`);
+ }
console.log(` Facilitator: ${facilitatorUrl}`);
console.log();
});
diff --git a/examples/typescript/servers/advanced/package.json b/examples/typescript/servers/advanced/package.json
index 140935e5c6..a7fa9475ae 100644
--- a/examples/typescript/servers/advanced/package.json
+++ b/examples/typescript/servers/advanced/package.json
@@ -23,6 +23,7 @@
"@x402/express": "workspace:*",
"@x402/evm": "workspace:*",
"@x402/svm": "workspace:*",
+ "@x402/stellar": "workspace:*",
"@x402/extensions": "workspace:*"
},
"devDependencies": {
diff --git a/examples/typescript/servers/bazaar/.env-local b/examples/typescript/servers/bazaar/.env-local
new file mode 100644
index 0000000000..4ddfc506b9
--- /dev/null
+++ b/examples/typescript/servers/bazaar/.env-local
@@ -0,0 +1,3 @@
+EVM_ADDRESS=
+SVM_ADDRESS=
+FACILITATOR_URL=https://x402.org/facilitator
\ No newline at end of file
diff --git a/examples/typescript/servers/bazaar/.prettierignore b/examples/typescript/servers/bazaar/.prettierignore
new file mode 100644
index 0000000000..5bd240ba90
--- /dev/null
+++ b/examples/typescript/servers/bazaar/.prettierignore
@@ -0,0 +1,8 @@
+docs/
+dist/
+node_modules/
+coverage/
+.github/
+src/client
+**/**/*.json
+*.md
\ No newline at end of file
diff --git a/examples/typescript/servers/bazaar/.prettierrc b/examples/typescript/servers/bazaar/.prettierrc
new file mode 100644
index 0000000000..ffb416b74b
--- /dev/null
+++ b/examples/typescript/servers/bazaar/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": false,
+ "trailingComma": "all",
+ "bracketSpacing": true,
+ "arrowParens": "avoid",
+ "printWidth": 100,
+ "proseWrap": "never"
+}
diff --git a/examples/typescript/servers/bazaar/README.md b/examples/typescript/servers/bazaar/README.md
new file mode 100644
index 0000000000..f413de661b
--- /dev/null
+++ b/examples/typescript/servers/bazaar/README.md
@@ -0,0 +1,137 @@
+# Bazaar Discovery Example Server
+
+Express.js server demonstrating how to make a paid API **discoverable** using the Bazaar extension with dynamic route parameters.
+
+The key addition over a basic x402 server is `declareDiscoveryExtension` — it describes your endpoint's inputs, outputs, and path parameters so that facilitators (and agents) can automatically catalog and invoke your API.
+
+## What This Example Shows
+
+**Dynamic route parameters** — the route `GET /weather/:city` uses a `:city` slug. The x402 middleware automatically:
+
+1. Matches `/weather/san-francisco`, `/weather/tokyo`, etc. against the route pattern
+2. Extracts `{ city: "san-francisco" }` as `pathParams` in the discovery extension
+3. Produces `routeTemplate: "/weather/:city"` so all concrete URLs consolidate into **one** catalog entry
+
+```typescript
+import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
+
+app.use(
+ paymentMiddleware(
+ {
+ "GET /weather/:city": {
+ accepts: { scheme: "exact", price: "$0.001", network: "eip155:84532", payTo: evmAddress },
+ description: "Weather data for a city",
+ mimeType: "application/json",
+ extensions: {
+ ...declareDiscoveryExtension({
+ pathParamsSchema: {
+ properties: { city: { type: "string", description: "City name slug" } },
+ required: ["city"],
+ },
+ output: {
+ example: { city: "san-francisco", weather: "foggy", temperature: 60 },
+ },
+ }),
+ },
+ },
+ },
+ resourceServer,
+ ),
+);
+
+app.get("/weather/:city", (req, res) => {
+ const city = req.params.city;
+ // ... return weather for city
+});
+```
+
+## Prerequisites
+
+- Node.js v20+ (install via [nvm](https://github.com/nvm-sh/nvm))
+- pnpm v10 (install via [pnpm.io/installation](https://pnpm.io/installation))
+- Valid EVM and SVM addresses for receiving payments
+- URL of a facilitator supporting the desired payment network, see [facilitator list](https://www.x402.org/ecosystem?category=facilitators)
+
+## Setup
+
+1. Copy `.env-local` to `.env`:
+
+```bash
+cp .env-local .env
+```
+
+and fill required environment variables:
+
+- `FACILITATOR_URL` - Facilitator endpoint URL
+- `EVM_ADDRESS` - Ethereum address to receive payments
+- `SVM_ADDRESS` - Solana address to receive payments
+
+2. Install and build all packages from the typescript examples root:
+```bash
+cd ../../
+pnpm install && pnpm build
+cd servers/bazaar
+```
+
+3. Run the server
+```bash
+pnpm dev
+```
+
+## How Discovery Works
+
+When a client hits `GET /weather/san-francisco` without a payment, the 402 response includes the enriched bazaar extension:
+
+```json
+{
+ "x402Version": 2,
+ "error": "Payment required",
+ "resource": { "url": "http://localhost:4021/weather/san-francisco" },
+ "extensions": {
+ "bazaar": {
+ "routeTemplate": "/weather/:city",
+ "info": {
+ "input": {
+ "type": "http",
+ "method": "GET",
+ "pathParams": { "city": "san-francisco" }
+ },
+ "output": {
+ "type": "json",
+ "example": { "city": "san-francisco", "weather": "foggy", "temperature": 60 }
+ }
+ },
+ "schema": { "..." : "..." }
+ }
+ },
+ "accepts": [{ "..." : "..." }]
+}
+```
+
+The facilitator uses `routeTemplate` as the canonical catalog key, so requests to `/weather/san-francisco`, `/weather/tokyo`, and `/weather/new-york` all map to a single discoverable endpoint: `/weather/:city`.
+
+## Multiple Path Parameters
+
+Routes can have multiple `:param` segments. Param names are matched by **position in the URL**, not by the order they appear in `pathParamsSchema`:
+
+```
+GET /weather/:country/:city
+ ^ ^
+ | └── second URL segment -> "city"
+ └──────────── first URL segment -> "country"
+```
+
+A request to `/weather/us/san-francisco` produces `pathParams: { country: "us", city: "san-francisco" }`. The property order in `pathParamsSchema` does not affect matching -- only the segment position in the URL matters.
+
+## `declareDiscoveryExtension` API
+
+The function accepts a config object describing your endpoint:
+
+| Field | Purpose |
+|-------|---------|
+| `input` | Example query parameter values (for GET/HEAD/DELETE) |
+| `inputSchema` | JSON Schema for query parameters |
+| `pathParamsSchema` | JSON Schema for URL path parameters (`:param` segments) |
+| `output.example` | Example response body (helps agents understand what they'll get) |
+| `output.schema` | JSON Schema for the response body |
+| `bodyType` | For POST/PUT/PATCH: `"json"`, `"form-data"`, or `"text"` |
diff --git a/examples/typescript/servers/bazaar/eslint.config.js b/examples/typescript/servers/bazaar/eslint.config.js
new file mode 100644
index 0000000000..e2fde7b3b8
--- /dev/null
+++ b/examples/typescript/servers/bazaar/eslint.config.js
@@ -0,0 +1,73 @@
+import js from "@eslint/js";
+import ts from "@typescript-eslint/eslint-plugin";
+import tsParser from "@typescript-eslint/parser";
+import prettier from "eslint-plugin-prettier";
+import jsdoc from "eslint-plugin-jsdoc";
+import importPlugin from "eslint-plugin-import";
+
+export default [
+ {
+ ignores: ["dist/**", "node_modules/**"],
+ },
+ {
+ files: ["**/*.ts"],
+ languageOptions: {
+ parser: tsParser,
+ sourceType: "module",
+ ecmaVersion: 2020,
+ globals: {
+ process: "readonly",
+ __dirname: "readonly",
+ module: "readonly",
+ require: "readonly",
+ Buffer: "readonly",
+ console: "readonly",
+ exports: "readonly",
+ setTimeout: "readonly",
+ clearTimeout: "readonly",
+ setInterval: "readonly",
+ clearInterval: "readonly",
+ },
+ },
+ plugins: {
+ "@typescript-eslint": ts,
+ prettier: prettier,
+ jsdoc: jsdoc,
+ import: importPlugin,
+ },
+ rules: {
+ ...ts.configs.recommended.rules,
+ "import/first": "error",
+ "prettier/prettier": "error",
+ "@typescript-eslint/member-ordering": "error",
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }],
+ "jsdoc/tag-lines": ["error", "any", { startLines: 1 }],
+ "jsdoc/check-alignment": "error",
+ "jsdoc/no-undefined-types": "off",
+ "jsdoc/check-param-names": "error",
+ "jsdoc/check-tag-names": "error",
+ "jsdoc/check-types": "error",
+ "jsdoc/implements-on-classes": "error",
+ "jsdoc/require-description": "error",
+ "jsdoc/require-jsdoc": [
+ "error",
+ {
+ require: {
+ FunctionDeclaration: true,
+ MethodDefinition: true,
+ ClassDeclaration: true,
+ ArrowFunctionExpression: false,
+ FunctionExpression: false,
+ },
+ },
+ ],
+ "jsdoc/require-param": "error",
+ "jsdoc/require-param-description": "error",
+ "jsdoc/require-param-type": "off",
+ "jsdoc/require-returns": "error",
+ "jsdoc/require-returns-description": "error",
+ "jsdoc/require-returns-type": "off",
+ "jsdoc/require-hyphen-before-param-description": ["error", "always"],
+ },
+ },
+];
diff --git a/examples/typescript/servers/bazaar/index.ts b/examples/typescript/servers/bazaar/index.ts
new file mode 100644
index 0000000000..35c208b870
--- /dev/null
+++ b/examples/typescript/servers/bazaar/index.ts
@@ -0,0 +1,124 @@
+import { config } from "dotenv";
+import express from "express";
+import { paymentMiddleware, x402ResourceServer } from "@x402/express";
+import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { ExactSvmScheme } from "@x402/svm/exact/server";
+import { HTTPFacilitatorClient } from "@x402/core/server";
+import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
+config();
+
+const evmAddress = process.env.EVM_ADDRESS as `0x${string}`;
+const svmAddress = process.env.SVM_ADDRESS;
+if (!evmAddress || !svmAddress) {
+ console.error("Missing required environment variables");
+ process.exit(1);
+}
+
+const facilitatorUrl = process.env.FACILITATOR_URL;
+if (!facilitatorUrl) {
+ console.error("FACILITATOR_URL environment variable is required");
+ process.exit(1);
+}
+const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+
+const app = express();
+
+const paymentOptions = [
+ {
+ scheme: "exact" as const,
+ price: "$0.001",
+ network: "eip155:84532",
+ payTo: evmAddress,
+ },
+ {
+ scheme: "exact" as const,
+ price: "$0.001",
+ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ payTo: svmAddress,
+ },
+];
+
+app.use(
+ paymentMiddleware(
+ {
+ // Single path param: /weather/:city
+ "GET /weather/:city": {
+ accepts: paymentOptions,
+ description: "Weather data for a city",
+ mimeType: "application/json",
+ extensions: {
+ ...declareDiscoveryExtension({
+ pathParamsSchema: {
+ properties: { city: { type: "string", description: "City name slug" } },
+ required: ["city"],
+ },
+ output: {
+ example: { city: "san-francisco", weather: "foggy", temperature: 60 },
+ },
+ }),
+ },
+ },
+
+ // Multiple path params: /weather/:country/:city
+ // Param names are matched by position in the URL, not by declaration order in the schema.
+ // /weather/us/san-francisco -> { country: "us", city: "san-francisco" }
+ "GET /weather/:country/:city": {
+ accepts: paymentOptions,
+ description: "Weather data for a city in a specific country",
+ mimeType: "application/json",
+ extensions: {
+ ...declareDiscoveryExtension({
+ pathParamsSchema: {
+ properties: {
+ country: { type: "string", description: "Country code" },
+ city: { type: "string", description: "City name slug" },
+ },
+ required: ["country", "city"],
+ },
+ output: {
+ example: { country: "us", city: "san-francisco", weather: "foggy", temperature: 60 },
+ },
+ }),
+ },
+ },
+ },
+ new x402ResourceServer(facilitatorClient)
+ .register("eip155:84532", new ExactEvmScheme())
+ .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()),
+ ),
+);
+
+app.get("/weather/:city", (req, res) => {
+ const { city } = req.params;
+
+ const weatherData: Record = {
+ "san-francisco": { weather: "foggy", temperature: 60 },
+ "new-york": { weather: "cloudy", temperature: 55 },
+ tokyo: { weather: "rainy", temperature: 65 },
+ };
+
+ const data = weatherData[city] || { weather: "sunny", temperature: 70 };
+ res.send({ city, weather: data.weather, temperature: data.temperature });
+});
+
+app.get("/weather/:country/:city", (req, res) => {
+ const { country, city } = req.params;
+
+ const weatherData: Record> = {
+ us: {
+ "san-francisco": { weather: "foggy", temperature: 60 },
+ "new-york": { weather: "cloudy", temperature: 55 },
+ },
+ jp: {
+ tokyo: { weather: "rainy", temperature: 65 },
+ osaka: { weather: "clear", temperature: 72 },
+ },
+ };
+
+ const data = weatherData[country]?.[city] || { weather: "sunny", temperature: 70 };
+ res.send({ country, city, weather: data.weather, temperature: data.temperature });
+});
+
+app.listen(4021, () => {
+ console.log(`Server listening at http://localhost:${4021}`);
+});
diff --git a/examples/typescript/servers/bazaar/package.json b/examples/typescript/servers/bazaar/package.json
new file mode 100644
index 0000000000..7857c989ab
--- /dev/null
+++ b/examples/typescript/servers/bazaar/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "@x402/bazaar-server-example",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "tsx index.ts",
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
+ "lint": "eslint . --ext .ts --fix",
+ "lint:check": "eslint . --ext .ts"
+ },
+ "dependencies": {
+ "@x402/core": "workspace:*",
+ "@x402/evm": "workspace:*",
+ "@x402/express": "workspace:*",
+ "@x402/extensions": "workspace:*",
+ "@x402/svm": "workspace:*",
+ "dotenv": "^16.4.7",
+ "express": "^4.18.2"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.24.0",
+ "@types/express": "^5.0.1",
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
+ "@typescript-eslint/parser": "^8.29.1",
+ "eslint": "^9.24.0",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsdoc": "^50.6.9",
+ "eslint-plugin-prettier": "^5.2.6",
+ "prettier": "3.5.2",
+ "tsup": "^7.2.0",
+ "tsx": "^4.7.0",
+ "typescript": "^5.3.0"
+ }
+}
diff --git a/examples/typescript/servers/bazaar/tsconfig.json b/examples/typescript/servers/bazaar/tsconfig.json
new file mode 100644
index 0000000000..99fe25e8e7
--- /dev/null
+++ b/examples/typescript/servers/bazaar/tsconfig.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ES2020",
+ "moduleResolution": "bundler",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "resolveJsonModule": true,
+ "baseUrl": ".",
+ "types": ["node"]
+ },
+ "include": [
+ "index.ts",
+ "bazaar.ts",
+ "custom-money-definition.ts",
+ "dynamic-pay-to.ts",
+ "dynamic-price.ts",
+ "hooks.ts"
+ ]
+}
diff --git a/examples/typescript/servers/express/package.json b/examples/typescript/servers/express/package.json
index 690dd8ae8f..adcbe1f6a8 100644
--- a/examples/typescript/servers/express/package.json
+++ b/examples/typescript/servers/express/package.json
@@ -10,7 +10,6 @@
"lint:check": "eslint . --ext .ts"
},
"dependencies": {
- "@coinbase/x402": "^2.1.0",
"@x402/core": "workspace:*",
"@x402/evm": "workspace:*",
"@x402/express": "workspace:*",
diff --git a/examples/typescript/servers/fastify/.env-local b/examples/typescript/servers/fastify/.env-local
new file mode 100644
index 0000000000..4ddfc506b9
--- /dev/null
+++ b/examples/typescript/servers/fastify/.env-local
@@ -0,0 +1,3 @@
+EVM_ADDRESS=
+SVM_ADDRESS=
+FACILITATOR_URL=https://x402.org/facilitator
\ No newline at end of file
diff --git a/examples/typescript/servers/fastify/.prettierignore b/examples/typescript/servers/fastify/.prettierignore
new file mode 100644
index 0000000000..3049672b5c
--- /dev/null
+++ b/examples/typescript/servers/fastify/.prettierignore
@@ -0,0 +1,8 @@
+docs/
+dist/
+node_modules/
+coverage/
+.github/
+src/client
+**/**/*.json
+*.md
diff --git a/examples/typescript/servers/fastify/.prettierrc b/examples/typescript/servers/fastify/.prettierrc
new file mode 100644
index 0000000000..ffb416b74b
--- /dev/null
+++ b/examples/typescript/servers/fastify/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": false,
+ "trailingComma": "all",
+ "bracketSpacing": true,
+ "arrowParens": "avoid",
+ "printWidth": 100,
+ "proseWrap": "never"
+}
diff --git a/examples/typescript/servers/fastify/README.md b/examples/typescript/servers/fastify/README.md
new file mode 100644
index 0000000000..dcb087d304
--- /dev/null
+++ b/examples/typescript/servers/fastify/README.md
@@ -0,0 +1,252 @@
+# @x402/fastify Example Server
+
+Fastify server demonstrating how to protect API endpoints with a paywall using the `@x402/fastify` middleware.
+
+```typescript
+import Fastify from "fastify";
+import { paymentMiddleware, x402ResourceServer } from "@x402/fastify";
+import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { HTTPFacilitatorClient } from "@x402/core/server";
+
+const app = Fastify();
+
+paymentMiddleware(
+ app,
+ {
+ "GET /weather": {
+ accepts: { scheme: "exact", price: "$0.001", network: "eip155:84532", payTo: evmAddress },
+ description: "Weather data",
+ mimeType: "application/json",
+ },
+ },
+ new x402ResourceServer(new HTTPFacilitatorClient({ url: facilitatorUrl }))
+ .register("eip155:84532", new ExactEvmScheme()),
+);
+
+app.get("/weather", async () => ({ weather: "sunny", temperature: 70 }));
+```
+
+## Prerequisites
+
+- Node.js v20+ (install via [nvm](https://github.com/nvm-sh/nvm))
+- pnpm v10 (install via [pnpm.io/installation](https://pnpm.io/installation))
+- Valid EVM and SVM addresses for receiving payments
+- URL of a facilitator supporting the desired payment network, see [facilitator list](https://www.x402.org/ecosystem?category=facilitators)
+
+## Setup
+
+1. Copy `.env-local` to `.env`:
+
+```bash
+cp .env-local .env
+```
+
+and fill required environment variables:
+
+- `FACILITATOR_URL` - Facilitator endpoint URL
+- `EVM_ADDRESS` - Ethereum address to receive payments
+- `SVM_ADDRESS` - Solana address to receive payments
+
+2. Install and build all packages from the typescript examples root:
+
+```bash
+cd ../../
+pnpm install && pnpm build
+cd servers/fastify
+```
+
+3. Run the server
+
+```bash
+pnpm dev
+```
+
+## Testing the Server
+
+You can test the server using one of the example clients:
+
+### Using the Fetch Client
+
+```bash
+cd ../clients/fetch
+# Ensure .env is setup
+pnpm dev
+```
+
+### Using the Axios Client
+
+```bash
+cd ../clients/axios
+# Ensure .env is setup
+pnpm dev
+```
+
+These clients will demonstrate how to:
+
+1. Make an initial request to get payment requirements
+2. Process the payment requirements
+3. Make a second request with the payment token
+
+## Example Endpoint
+
+The server includes a single example endpoint at `/weather` that requires a payment of 0.001 USDC on Base Sepolia or Solana Devnet to access. The endpoint returns a simple weather report.
+
+## Response Format
+
+### Payment Required (402)
+
+```
+HTTP/1.1 402 Payment Required
+Content-Type: application/json; charset=utf-8
+PAYMENT-REQUIRED:
+
+{}
+```
+
+The `PAYMENT-REQUIRED` header contains base64-encoded JSON with the payment requirements.
+Note: `amount` is in atomic units (e.g., 1000 = 0.001 USDC, since USDC has 6 decimals):
+
+```json
+{
+ "x402Version": 2,
+ "error": "Payment required",
+ "resource": {
+ "url": "http://localhost:4021/weather",
+ "description": "Weather data",
+ "mimeType": "application/json"
+ },
+ "accepts": [
+ {
+ "scheme": "exact",
+ "network": "eip155:84532",
+ "amount": "1000",
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ "payTo": "0x1c47E9C085c2B7458F5b6C16cCBD65A65255a9f6",
+ "maxTimeoutSeconds": 300,
+ "extra": {
+ "name": "USDC",
+ "version": "2",
+ "resourceUrl": "http://localhost:4021/weather"
+ }
+ },
+ {
+ "scheme": "exact",
+ "network": "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ "amount": "1000",
+ "asset": "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU",
+ "payTo": "FV6JPj6Fy12HG8SYStyHdcecXYmV1oeWERAokrh4GQ1n",
+ "maxTimeoutSeconds": 300,
+ "extra": {
+ "feePayer": "...",
+ "resourceUrl": "http://localhost:4021/weather"
+ }
+ }
+ ]
+}
+```
+
+### Successful Response
+
+```
+HTTP/1.1 200 OK
+Content-Type: application/json; charset=utf-8
+PAYMENT-RESPONSE:
+
+{"report":{"weather":"sunny","temperature":70}}
+```
+
+The `PAYMENT-RESPONSE` header contains base64-encoded JSON with the settlement details:
+
+```json
+{
+ "success": true,
+ "transaction": "0x...",
+ "network": "eip155:84532",
+ "payer": "0x...",
+ "requirements": {
+ "scheme": "exact",
+ "network": "eip155:84532",
+ "amount": "1000",
+ "asset": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
+ "payTo": "0x...",
+ "maxTimeoutSeconds": 300,
+ "extra": {
+ "name": "USDC",
+ "version": "2",
+ "resourceUrl": "http://localhost:4021/weather"
+ }
+ }
+}
+```
+
+## Extending the Example
+
+To add more paid endpoints, follow this pattern:
+
+```typescript
+// First, configure the payment middleware with your routes
+paymentMiddleware(
+ app,
+ {
+ "GET /your-endpoint": {
+ accepts: {
+ scheme: "exact",
+ price: "$0.10",
+ network: "eip155:84532",
+ payTo: evmAddress,
+ },
+ description: "Your endpoint description",
+ mimeType: "application/json",
+ },
+ },
+ resourceServer,
+);
+
+// Then define your routes as normal
+app.get("/your-endpoint", async () => {
+ return {
+ // Your response data
+ };
+});
+```
+
+**Network identifiers** use [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) format, for example:
+
+- `eip155:84532` — Base Sepolia
+- `eip155:8453` — Base Mainnet
+- `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1` — Solana Devnet
+- `solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp` — Solana Mainnet
+
+## x402ResourceServer Config
+
+The `x402ResourceServer` uses a builder pattern to register payment schemes that declare how payments for each network should be processed:
+
+```typescript
+const resourceServer = new x402ResourceServer(facilitatorClient)
+ .register("eip155:*", new ExactEvmScheme()) // All EVM chains
+ .register("solana:*", new ExactSvmScheme()); // All SVM chains
+```
+
+## Facilitator Config
+
+The `HTTPFacilitatorClient` connects to a facilitator service that verifies and settles payments on-chain:
+
+```typescript
+const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+
+// Or use multiple facilitators for redundancy
+const facilitatorClient = [
+ new HTTPFacilitatorClient({ url: primaryFacilitatorUrl }),
+ new HTTPFacilitatorClient({ url: backupFacilitatorUrl }),
+];
+```
+
+## Next Steps
+
+See [Advanced Examples](../advanced/) for:
+
+- **Bazaar discovery** — make your API discoverable
+- **Dynamic pricing** — price based on request context
+- **Dynamic payTo** — route payments to different recipients
+- **Lifecycle hooks** — custom logic on verify/settle
+- **Custom tokens** — accept payments in custom tokens
diff --git a/examples/typescript/servers/fastify/eslint.config.js b/examples/typescript/servers/fastify/eslint.config.js
new file mode 100644
index 0000000000..e2fde7b3b8
--- /dev/null
+++ b/examples/typescript/servers/fastify/eslint.config.js
@@ -0,0 +1,73 @@
+import js from "@eslint/js";
+import ts from "@typescript-eslint/eslint-plugin";
+import tsParser from "@typescript-eslint/parser";
+import prettier from "eslint-plugin-prettier";
+import jsdoc from "eslint-plugin-jsdoc";
+import importPlugin from "eslint-plugin-import";
+
+export default [
+ {
+ ignores: ["dist/**", "node_modules/**"],
+ },
+ {
+ files: ["**/*.ts"],
+ languageOptions: {
+ parser: tsParser,
+ sourceType: "module",
+ ecmaVersion: 2020,
+ globals: {
+ process: "readonly",
+ __dirname: "readonly",
+ module: "readonly",
+ require: "readonly",
+ Buffer: "readonly",
+ console: "readonly",
+ exports: "readonly",
+ setTimeout: "readonly",
+ clearTimeout: "readonly",
+ setInterval: "readonly",
+ clearInterval: "readonly",
+ },
+ },
+ plugins: {
+ "@typescript-eslint": ts,
+ prettier: prettier,
+ jsdoc: jsdoc,
+ import: importPlugin,
+ },
+ rules: {
+ ...ts.configs.recommended.rules,
+ "import/first": "error",
+ "prettier/prettier": "error",
+ "@typescript-eslint/member-ordering": "error",
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }],
+ "jsdoc/tag-lines": ["error", "any", { startLines: 1 }],
+ "jsdoc/check-alignment": "error",
+ "jsdoc/no-undefined-types": "off",
+ "jsdoc/check-param-names": "error",
+ "jsdoc/check-tag-names": "error",
+ "jsdoc/check-types": "error",
+ "jsdoc/implements-on-classes": "error",
+ "jsdoc/require-description": "error",
+ "jsdoc/require-jsdoc": [
+ "error",
+ {
+ require: {
+ FunctionDeclaration: true,
+ MethodDefinition: true,
+ ClassDeclaration: true,
+ ArrowFunctionExpression: false,
+ FunctionExpression: false,
+ },
+ },
+ ],
+ "jsdoc/require-param": "error",
+ "jsdoc/require-param-description": "error",
+ "jsdoc/require-param-type": "off",
+ "jsdoc/require-returns": "error",
+ "jsdoc/require-returns-description": "error",
+ "jsdoc/require-returns-type": "off",
+ "jsdoc/require-hyphen-before-param-description": ["error", "always"],
+ },
+ },
+];
diff --git a/examples/typescript/servers/fastify/index.ts b/examples/typescript/servers/fastify/index.ts
new file mode 100644
index 0000000000..8f1bc079e4
--- /dev/null
+++ b/examples/typescript/servers/fastify/index.ts
@@ -0,0 +1,67 @@
+import { config } from "dotenv";
+import Fastify from "fastify";
+import { paymentMiddleware, x402ResourceServer } from "@x402/fastify";
+import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { ExactSvmScheme } from "@x402/svm/exact/server";
+import { HTTPFacilitatorClient } from "@x402/core/server";
+config();
+
+const evmAddress = process.env.EVM_ADDRESS as `0x${string}`;
+const svmAddress = process.env.SVM_ADDRESS;
+if (!evmAddress || !svmAddress) {
+ console.error("Missing required environment variables");
+ process.exit(1);
+}
+
+const facilitatorUrl = process.env.FACILITATOR_URL;
+if (!facilitatorUrl) {
+ console.error("❌ FACILITATOR_URL environment variable is required");
+ process.exit(1);
+}
+const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+
+const app = Fastify();
+
+paymentMiddleware(
+ app,
+ {
+ "GET /weather": {
+ accepts: [
+ {
+ scheme: "exact",
+ price: "$0.001",
+ network: "eip155:84532",
+ payTo: evmAddress,
+ },
+ {
+ scheme: "exact",
+ price: "$0.001",
+ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ payTo: svmAddress,
+ },
+ ],
+ description: "Weather data",
+ mimeType: "application/json",
+ },
+ },
+ new x402ResourceServer(facilitatorClient)
+ .register("eip155:84532", new ExactEvmScheme())
+ .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme()),
+);
+
+app.get("/weather", async () => {
+ return {
+ report: {
+ weather: "sunny",
+ temperature: 70,
+ },
+ };
+});
+
+app.listen({ port: 4021 }, (err, address) => {
+ if (err) {
+ console.error(err);
+ process.exit(1);
+ }
+ console.log(`Server listening at ${address}`);
+});
diff --git a/examples/typescript/servers/fastify/package.json b/examples/typescript/servers/fastify/package.json
new file mode 100644
index 0000000000..e6474abe6a
--- /dev/null
+++ b/examples/typescript/servers/fastify/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "@x402/fastify-server-example",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "tsx index.ts",
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
+ "lint": "eslint . --ext .ts --fix",
+ "lint:check": "eslint . --ext .ts"
+ },
+ "dependencies": {
+ "@x402/core": "workspace:*",
+ "@x402/evm": "workspace:*",
+ "@x402/fastify": "workspace:*",
+ "@x402/extensions": "workspace:*",
+ "@x402/svm": "workspace:*",
+ "dotenv": "^16.4.7",
+ "fastify": "^5.0.0"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.24.0",
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
+ "@typescript-eslint/parser": "^8.29.1",
+ "eslint": "^9.24.0",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsdoc": "^50.6.9",
+ "eslint-plugin-prettier": "^5.2.6",
+ "prettier": "3.5.2",
+ "tsup": "^7.2.0",
+ "tsx": "^4.7.0",
+ "typescript": "^5.3.0"
+ }
+}
diff --git a/examples/typescript/servers/fastify/tsconfig.json b/examples/typescript/servers/fastify/tsconfig.json
new file mode 100644
index 0000000000..78f9479b1b
--- /dev/null
+++ b/examples/typescript/servers/fastify/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ES2020",
+ "moduleResolution": "bundler",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "resolveJsonModule": true,
+ "baseUrl": ".",
+ "types": ["node"]
+ },
+ "include": ["index.ts"]
+}
diff --git a/examples/typescript/servers/offer-receipt/.env-local b/examples/typescript/servers/offer-receipt/.env-local
new file mode 100644
index 0000000000..c4f6b7e1c6
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/.env-local
@@ -0,0 +1,27 @@
+# Facilitator URL for payment verification and settlement
+FACILITATOR_URL=https://x402.org/facilitator
+
+# EVM address to receive payments
+EVM_ADDRESS=0x1234567890123456789012345678901234567890
+
+# Solana address to receive payments
+SVM_ADDRESS=FV6JPj6Fy12HG8SYStyHdcecXYmV1oeWERAokrh4GQ1n
+
+# Signing format: "jws" (default) or "eip712"
+# - JWS: Base64-encoded PKCS#8 private key (P-256, secp256k1, or Ed25519)
+# - EIP-712: Hex-encoded secp256k1 private key (Ethereum's curve)
+SIGNING_FORMAT=jws
+
+# Private key for signing offers/receipts (format depends on SIGNING_FORMAT above)
+#
+# For JWS with ES256 (P-256):
+# openssl ecparam -genkey -name prime256v1 -noout | openssl pkcs8 -topk8 -nocrypt | base64 | tr -d '\n'
+#
+# For EIP-712 (secp256k1 hex):
+# openssl ecparam -genkey -name secp256k1 -noout | openssl ec -text -noout 2>/dev/null | grep -A3 'priv:' | tail -3 | tr -d ' :\n'
+SIGNING_PRIVATE_KEY=
+
+# Server domain for DID (only required for JWS format)
+# Used to construct the key identifier: did:web:#key-1
+# For localhost with port, use percent-encoding: localhost%3A4021
+SERVER_DOMAIN=localhost%3A4021
diff --git a/examples/typescript/servers/offer-receipt/.prettierignore b/examples/typescript/servers/offer-receipt/.prettierignore
new file mode 100644
index 0000000000..3049672b5c
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/.prettierignore
@@ -0,0 +1,8 @@
+docs/
+dist/
+node_modules/
+coverage/
+.github/
+src/client
+**/**/*.json
+*.md
diff --git a/examples/typescript/servers/offer-receipt/.prettierrc b/examples/typescript/servers/offer-receipt/.prettierrc
new file mode 100644
index 0000000000..ffb416b74b
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": false,
+ "trailingComma": "all",
+ "bracketSpacing": true,
+ "arrowParens": "avoid",
+ "printWidth": 100,
+ "proseWrap": "never"
+}
diff --git a/examples/typescript/servers/offer-receipt/README.md b/examples/typescript/servers/offer-receipt/README.md
new file mode 100644
index 0000000000..07d8b7036f
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/README.md
@@ -0,0 +1,72 @@
+# Offer-Receipt Extension Server Example
+
+Express.js server demonstrating the offer-receipt extension for x402. This extension adds signed offers and receipts to payment flows, enabling:
+
+- **Signed offers** — cryptographic proof of payment terms from the server
+- **Signed receipts** — proof of service delivery after payment
+
+## Signing Formats
+
+The server supports two signing formats:
+
+| Format | Key Type | Verification |
+| ----------------- | -------------------------- | ----------------------------------- |
+| **JWS** (default) | P-256, secp256k1, Ed25519 | Resolve `did:web` to get public key |
+| **EIP-712** | secp256k1 only | Recover address from signature |
+
+## Setup
+
+1. Copy `.env-local` to `.env`:
+
+```bash
+cp .env-local .env
+```
+
+2. Generate a signing key:
+
+**For JWS (ES256/P-256):**
+```bash
+openssl ecparam -genkey -name prime256v1 -noout | openssl pkcs8 -topk8 -nocrypt | base64 | tr -d '\n'
+```
+
+**For EIP-712 (secp256k1 hex):**
+```bash
+openssl ecparam -genkey -name secp256k1 -noout | openssl ec -text -noout 2>/dev/null | grep -A3 'priv:' | tail -3 | tr -d ' :\n'
+```
+
+3. Configure `.env`:
+ - `SIGNING_FORMAT` — `jws` or `eip712`
+ - `SIGNING_PRIVATE_KEY` — Key in the appropriate format
+ - `SERVER_DOMAIN` — Required for JWS (e.g., `localhost%3A4021`)
+ - `FACILITATOR_URL`, `EVM_ADDRESS`, `SVM_ADDRESS`
+
+4. Install and run:
+
+```bash
+cd ../../
+pnpm install && pnpm build
+cd servers/offer-receipt
+pnpm dev
+```
+
+## DID Document (JWS only)
+
+For JWS signing, the server exposes `/.well-known/did.json` for signature verification:
+
+```bash
+curl http://localhost:4021/.well-known/did.json
+```
+
+The library's `resolveDidWeb` automatically uses HTTP for `localhost` and `127.0.0.1`.
+
+## Configuration Options
+
+`declareOfferReceiptExtension()` accepts:
+
+- `includeTxHash` — Include transaction hash in receipt (default: `false` for privacy)
+- `offerValiditySeconds` — How long offers remain valid (default: 300)
+
+## Related
+
+- [Extension Specification](../../../../typescript/packages/extensions/src/offer-receipt/README.md)
+- [Offer/Receipt Client Example](../../clients/offer-receipt/)
diff --git a/examples/typescript/servers/offer-receipt/eip712-signer.ts b/examples/typescript/servers/offer-receipt/eip712-signer.ts
new file mode 100644
index 0000000000..f757ce9a4f
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/eip712-signer.ts
@@ -0,0 +1,31 @@
+import { privateKeyToAccount } from "viem/accounts";
+import type { SignTypedDataFn } from "@x402/extensions/offer-receipt";
+
+export interface EIP712SignerResult {
+ signTypedData: SignTypedDataFn;
+ address: `0x${string}`;
+}
+
+/**
+ * Create an EIP-712 signer from a hex private key
+ *
+ * For production, use a proper key management solution (HSM, KMS, etc.)
+ * This is a simple implementation for demonstration purposes.
+ *
+ * @param privateKeyHex - Hex-encoded secp256k1 private key (with or without 0x prefix).
+ * EIP-712 only supports secp256k1 keys (Ethereum's curve).
+ * @returns EIP712SignerResult containing the signTypedData function and address
+ */
+export function createEIP712SignerFromPrivateKey(privateKeyHex: string): EIP712SignerResult {
+ // Ensure 0x prefix
+ const normalizedKey = privateKeyHex.startsWith("0x")
+ ? (privateKeyHex as `0x${string}`)
+ : (`0x${privateKeyHex}` as `0x${string}`);
+
+ const account = privateKeyToAccount(normalizedKey);
+
+ return {
+ signTypedData: account.signTypedData.bind(account) as SignTypedDataFn,
+ address: account.address,
+ };
+}
diff --git a/examples/typescript/servers/offer-receipt/eslint.config.js b/examples/typescript/servers/offer-receipt/eslint.config.js
new file mode 100644
index 0000000000..e2fde7b3b8
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/eslint.config.js
@@ -0,0 +1,73 @@
+import js from "@eslint/js";
+import ts from "@typescript-eslint/eslint-plugin";
+import tsParser from "@typescript-eslint/parser";
+import prettier from "eslint-plugin-prettier";
+import jsdoc from "eslint-plugin-jsdoc";
+import importPlugin from "eslint-plugin-import";
+
+export default [
+ {
+ ignores: ["dist/**", "node_modules/**"],
+ },
+ {
+ files: ["**/*.ts"],
+ languageOptions: {
+ parser: tsParser,
+ sourceType: "module",
+ ecmaVersion: 2020,
+ globals: {
+ process: "readonly",
+ __dirname: "readonly",
+ module: "readonly",
+ require: "readonly",
+ Buffer: "readonly",
+ console: "readonly",
+ exports: "readonly",
+ setTimeout: "readonly",
+ clearTimeout: "readonly",
+ setInterval: "readonly",
+ clearInterval: "readonly",
+ },
+ },
+ plugins: {
+ "@typescript-eslint": ts,
+ prettier: prettier,
+ jsdoc: jsdoc,
+ import: importPlugin,
+ },
+ rules: {
+ ...ts.configs.recommended.rules,
+ "import/first": "error",
+ "prettier/prettier": "error",
+ "@typescript-eslint/member-ordering": "error",
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }],
+ "jsdoc/tag-lines": ["error", "any", { startLines: 1 }],
+ "jsdoc/check-alignment": "error",
+ "jsdoc/no-undefined-types": "off",
+ "jsdoc/check-param-names": "error",
+ "jsdoc/check-tag-names": "error",
+ "jsdoc/check-types": "error",
+ "jsdoc/implements-on-classes": "error",
+ "jsdoc/require-description": "error",
+ "jsdoc/require-jsdoc": [
+ "error",
+ {
+ require: {
+ FunctionDeclaration: true,
+ MethodDefinition: true,
+ ClassDeclaration: true,
+ ArrowFunctionExpression: false,
+ FunctionExpression: false,
+ },
+ },
+ ],
+ "jsdoc/require-param": "error",
+ "jsdoc/require-param-description": "error",
+ "jsdoc/require-param-type": "off",
+ "jsdoc/require-returns": "error",
+ "jsdoc/require-returns-description": "error",
+ "jsdoc/require-returns-type": "off",
+ "jsdoc/require-hyphen-before-param-description": ["error", "always"],
+ },
+ },
+];
diff --git a/examples/typescript/servers/offer-receipt/index.ts b/examples/typescript/servers/offer-receipt/index.ts
new file mode 100644
index 0000000000..71f18b1532
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/index.ts
@@ -0,0 +1,163 @@
+import { config } from "dotenv";
+import express from "express";
+import { paymentMiddleware, x402ResourceServer } from "@x402/express";
+import { ExactEvmScheme } from "@x402/evm/exact/server";
+import { ExactSvmScheme } from "@x402/svm/exact/server";
+import { HTTPFacilitatorClient } from "@x402/core/server";
+import {
+ createOfferReceiptExtension,
+ createJWSOfferReceiptIssuer,
+ createEIP712OfferReceiptIssuer,
+ declareOfferReceiptExtension,
+} from "@x402/extensions/offer-receipt";
+import { createJWSSignerFromPrivateKey } from "./jws-signer";
+import { createEIP712SignerFromPrivateKey } from "./eip712-signer";
+config();
+
+const evmAddress = process.env.EVM_ADDRESS as `0x${string}`;
+const svmAddress = process.env.SVM_ADDRESS;
+if (!evmAddress || !svmAddress) {
+ console.error("Missing EVM_ADDRESS or SVM_ADDRESS environment variable");
+ process.exit(1);
+}
+
+const facilitatorUrl = process.env.FACILITATOR_URL;
+if (!facilitatorUrl) {
+ console.error("❌ FACILITATOR_URL environment variable is required");
+ process.exit(1);
+}
+
+// For production, use a proper key management solution (HSM, KMS, etc.)
+// This example uses a simple private key for demonstration
+const signingPrivateKey = process.env.SIGNING_PRIVATE_KEY;
+if (!signingPrivateKey) {
+ console.error("❌ SIGNING_PRIVATE_KEY environment variable is required");
+ process.exit(1);
+}
+
+// Signing format: "jws" (default) or "eip712"
+const signingFormat = (process.env.SIGNING_FORMAT || "jws").toLowerCase();
+if (signingFormat !== "jws" && signingFormat !== "eip712") {
+ console.error('❌ SIGNING_FORMAT must be "jws" or "eip712"');
+ process.exit(1);
+}
+
+const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+
+// Create the appropriate issuer based on signing format
+let offerReceiptIssuer;
+let kid: string;
+let didDocument: object | null = null;
+
+if (signingFormat === "eip712") {
+ // EIP-712 signing using Ethereum private key
+ const { signTypedData, address } = createEIP712SignerFromPrivateKey(signingPrivateKey);
+
+ // Use did:pkh for EIP-712 (identifies the Ethereum address)
+ // Format: did:pkh:eip155::
+ // Using chainId 1 (mainnet) as the canonical identifier
+ kid = `did:pkh:eip155:1:${address}#key-1`;
+ offerReceiptIssuer = createEIP712OfferReceiptIssuer(kid, signTypedData);
+
+ console.log(`Using EIP-712 signing with address: ${address}`);
+} else {
+ // JWS signing using PKCS#8 private key
+ const serverDomain = process.env.SERVER_DOMAIN;
+ if (!serverDomain) {
+ console.error(
+ "❌ SERVER_DOMAIN environment variable is required for JWS signing (e.g., localhost%3A4021)",
+ );
+ process.exit(1);
+ }
+
+ const did = `did:web:${serverDomain}`;
+ kid = `${did}#key-1`;
+ const { signer: jwsSigner, publicKeyJwk } = createJWSSignerFromPrivateKey(signingPrivateKey, kid);
+ offerReceiptIssuer = createJWSOfferReceiptIssuer(kid, jwsSigner);
+
+ // Build DID document for /.well-known/did.json (only needed for JWS)
+ didDocument = {
+ "@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1"],
+ id: did,
+ verificationMethod: [
+ {
+ id: kid,
+ type: "JsonWebKey2020",
+ controller: did,
+ publicKeyJwk,
+ },
+ ],
+ assertionMethod: [kid],
+ };
+
+ console.log(`Using JWS signing with did:web: ${did}`);
+}
+
+const app = express();
+
+// Create the resource server with the offer-receipt extension registered
+const resourceServer = new x402ResourceServer(facilitatorClient)
+ .register("eip155:84532", new ExactEvmScheme())
+ .register("solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", new ExactSvmScheme())
+ .registerExtension(createOfferReceiptExtension(offerReceiptIssuer));
+
+app.use(
+ paymentMiddleware(
+ {
+ "GET /weather": {
+ accepts: [
+ {
+ scheme: "exact",
+ // Note: "price" is SDK syntactic sugar that converts to "amount" in atomic units
+ // The wire protocol uses "amount" per the x402 spec
+ price: "$0.001",
+ network: "eip155:84532",
+ payTo: evmAddress,
+ },
+ {
+ scheme: "exact",
+ price: "$0.001",
+ network: "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
+ payTo: svmAddress,
+ },
+ ],
+ description: "Weather data",
+ mimeType: "application/json",
+ extensions: {
+ // Declare the offer-receipt extension for this route
+ // includeTxHash: false (default) for privacy, true for verifiability
+ ...declareOfferReceiptExtension({ includeTxHash: false }),
+ },
+ },
+ },
+ resourceServer,
+ ),
+);
+
+app.get("/weather", (req, res) => {
+ res.send({
+ report: {
+ weather: "sunny",
+ temperature: 70,
+ },
+ });
+});
+
+// Serve DID document for JWS verification (only needed for JWS format)
+// did:web resolves to /.well-known/did.json
+if (didDocument) {
+ app.get("/.well-known/did.json", (req, res) => {
+ res.setHeader("Content-Type", "application/did+json");
+ res.json(didDocument);
+ });
+}
+
+app.listen(4021, () => {
+ console.log(`Server listening at http://localhost:${4021}`);
+ console.log("Offer-receipt extension enabled - responses will include signed offers/receipts");
+ console.log(`Signing format: ${signingFormat.toUpperCase()}`);
+ console.log(`Key ID: ${kid}`);
+ if (didDocument) {
+ console.log(`DID document available at http://localhost:4021/.well-known/did.json`);
+ }
+});
diff --git a/examples/typescript/servers/offer-receipt/jws-signer.ts b/examples/typescript/servers/offer-receipt/jws-signer.ts
new file mode 100644
index 0000000000..49c6fb481f
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/jws-signer.ts
@@ -0,0 +1,92 @@
+import * as crypto from "crypto";
+import type { JWSSigner } from "@x402/extensions/offer-receipt";
+
+export interface SignerWithPublicKey {
+ signer: JWSSigner;
+ publicKeyJwk: JsonWebKey;
+}
+
+/**
+ * Create a JWS signer from a base64-encoded PKCS#8 private key
+ *
+ * For production, use a proper key management solution (HSM, KMS, etc.)
+ * This is a simple implementation for demonstration purposes.
+ *
+ * @param privateKeyBase64 - Base64-encoded PKCS#8 private key
+ * @param kid - Key identifier DID URL
+ * @returns SignerWithPublicKey containing the signer and public key JWK
+ */
+export function createJWSSignerFromPrivateKey(
+ privateKeyBase64: string,
+ kid: string,
+): SignerWithPublicKey {
+ // Decode base64 and check if it's PEM or DER format
+ const decoded = Buffer.from(privateKeyBase64, "base64").toString("utf8");
+ const isPem = decoded.includes("-----BEGIN");
+
+ let privateKeyPem: string;
+ if (isPem) {
+ // Already PEM format (base64-encoded PEM)
+ privateKeyPem = decoded;
+ } else {
+ // Raw DER format, wrap in PEM headers
+ privateKeyPem = `-----BEGIN PRIVATE KEY-----\n${privateKeyBase64}\n-----END PRIVATE KEY-----`;
+ }
+
+ const keyObject = crypto.createPrivateKey(privateKeyPem);
+ const publicKeyJwk = keyObject.export({ format: "jwk" }) as JsonWebKey;
+ // Remove private key component
+ delete (publicKeyJwk as Record).d;
+
+ const signer: JWSSigner = {
+ kid,
+ format: "jws",
+ algorithm: "ES256",
+ async sign(payload: Uint8Array): Promise {
+ const sign = crypto.createSign("SHA256");
+ sign.update(payload);
+ const signature = sign.sign(privateKeyPem);
+ // Convert DER signature to raw r||s format for JWS
+ const rawSignature = derToRaw(signature);
+ return Buffer.from(rawSignature).toString("base64url");
+ },
+ };
+
+ return { signer, publicKeyJwk };
+}
+
+/**
+ * Convert DER-encoded ECDSA signature to raw r||s format
+ *
+ * @param derSignature - DER-encoded signature buffer
+ * @returns Raw signature as Uint8Array (64 bytes for P-256)
+ */
+function derToRaw(derSignature: Buffer): Uint8Array {
+ // DER format: 0x30 [total-length] 0x02 [r-length] [r] 0x02 [s-length] [s]
+ let offset = 2; // Skip 0x30 and total length
+
+ // Read r
+ if (derSignature[offset] !== 0x02) throw new Error("Invalid DER signature");
+ offset++;
+ const rLength = derSignature[offset];
+ offset++;
+ let r = derSignature.subarray(offset, offset + rLength);
+ offset += rLength;
+
+ // Read s
+ if (derSignature[offset] !== 0x02) throw new Error("Invalid DER signature");
+ offset++;
+ const sLength = derSignature[offset];
+ offset++;
+ let s = derSignature.subarray(offset, offset + sLength);
+
+ // Remove leading zeros and pad to 32 bytes
+ if (r.length > 32) r = r.subarray(r.length - 32);
+ if (s.length > 32) s = s.subarray(s.length - 32);
+
+ const raw = new Uint8Array(64);
+ raw.set(r, 32 - r.length);
+ raw.set(s, 64 - s.length);
+
+ return raw;
+}
diff --git a/examples/typescript/servers/offer-receipt/package.json b/examples/typescript/servers/offer-receipt/package.json
new file mode 100644
index 0000000000..2ecb0909e6
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "@x402/offer-receipt-server-example",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "tsx index.ts",
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
+ "lint": "eslint . --ext .ts --fix",
+ "lint:check": "eslint . --ext .ts"
+ },
+ "dependencies": {
+ "dotenv": "^16.4.7",
+ "express": "^4.18.2",
+ "viem": "^2.21.54",
+ "@x402/core": "workspace:*",
+ "@x402/express": "workspace:*",
+ "@x402/evm": "workspace:*",
+ "@x402/svm": "workspace:*",
+ "@x402/extensions": "workspace:*"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.24.0",
+ "@types/express": "^5.0.1",
+ "@types/node": "^22.0.0",
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
+ "@typescript-eslint/parser": "^8.29.1",
+ "eslint": "^9.24.0",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsdoc": "^50.6.9",
+ "eslint-plugin-prettier": "^5.2.6",
+ "prettier": "3.5.2",
+ "tsx": "^4.7.0",
+ "typescript": "^5.3.0"
+ }
+}
diff --git a/examples/typescript/servers/offer-receipt/tsconfig.json b/examples/typescript/servers/offer-receipt/tsconfig.json
new file mode 100644
index 0000000000..828d934ed6
--- /dev/null
+++ b/examples/typescript/servers/offer-receipt/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ES2020",
+ "moduleResolution": "bundler",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "resolveJsonModule": true,
+ "baseUrl": ".",
+ "types": ["node"]
+ },
+ "include": ["index.ts", "jws-signer.ts"]
+}
diff --git a/examples/typescript/servers/sign-in-with-x/README.md b/examples/typescript/servers/sign-in-with-x/README.md
index 61cf87879f..dc37bc16af 100644
--- a/examples/typescript/servers/sign-in-with-x/README.md
+++ b/examples/typescript/servers/sign-in-with-x/README.md
@@ -1,6 +1,8 @@
# Sign-In-With-X (SIWX) Server Example
-Express.js server demonstrating how to implement Sign-In-With-X authentication, allowing clients to prove prior payment via wallet signatures instead of paying again on subsequent requests.
+Express.js server demonstrating both SIWX patterns supported by x402:
+- Auth-only routes that require a wallet signature but no payment
+- Paid routes where a wallet can pay once, then authenticate with SIWX on later requests
```typescript
import express from "express";
@@ -31,16 +33,16 @@ app.use(paymentMiddlewareFromHTTPServer(httpServer));
## How It Works
-1. **Client pays** — First request requires payment
-2. **Server records** — Payment recorded against wallet address in storage
-3. **Client signs** — Subsequent requests include SIWX signature
-4. **Server verifies** — Signature proves wallet ownership, grants access without payment
+1. **Auth-only route** — Server returns a SIWX challenge and grants access on a valid signature alone
+2. **Paid route** — First request requires payment
+3. **Server records** — Payment is recorded against the wallet address in storage
+4. **Later paid-route request** — Signature proves wallet ownership and grants access without re-payment
## Prerequisites
- Node.js v20+ (install via [nvm](https://github.com/nvm-sh/nvm))
- pnpm v10 (install via [pnpm.io/installation](https://pnpm.io/installation))
-- Valid EVM address (SVM optional)
+- At least one payout address: EVM, SVM, or both
- Facilitator URL (see [facilitator list](https://www.x402.org/ecosystem?category=facilitators))
## Setup
@@ -54,9 +56,11 @@ cp .env-local .env
and fill required environment variables:
- `FACILITATOR_URL` - Facilitator endpoint URL
-- `EVM_ADDRESS` - Ethereum address to receive payments
+- `EVM_ADDRESS` - (Optional) Ethereum address to receive payments
- `SVM_ADDRESS` - (Optional) Solana address for SVM payments
+At least one of `EVM_ADDRESS` or `SVM_ADDRESS` is required.
+
2. Install and build from typescript examples root:
```bash
@@ -77,22 +81,24 @@ Start the SIWX client to test:
```bash
cd ../../clients/sign-in-with-x
-# Ensure .env is setup with EVM_PRIVATE_KEY
+# Ensure .env is setup with EVM_PRIVATE_KEY or SVM_PRIVATE_KEY
pnpm start
```
The client will:
-1. Make first request and pay for `/weather`
-2. Make second request with SIWX signature (no payment)
-3. Make first request and pay for `/joke`
-4. Make second request with SIWX signature (no payment)
+1. Access `/profile` with SIWX and no payment
+2. Make first request and pay for `/weather`
+3. Make second request to `/weather` with SIWX instead of payment
+4. Make first request and pay for `/joke`
+5. Make second request to `/joke` with SIWX instead of payment
## Example Endpoints
+- `GET /profile` — Auth-only wallet-gated profile data (no payment)
- `GET /weather` — Weather data ($0.001 USDC)
- `GET /joke` — Joke content ($0.001 USDC)
-Each endpoint requires payment once per wallet address. Subsequent requests from the same wallet authenticate via SIWX signature.
+`/profile` requires only a valid SIWX signature. `/weather` and `/joke` require payment once per wallet address, then accept SIWX on later requests.
## SIWX Extension Configuration
@@ -108,6 +114,15 @@ const routes = {
mimeType: "application/json",
extensions: declareSIWxExtension(), // Announces SIWX support
},
+ "GET /profile": {
+ accepts: [],
+ description: "Auth-only: wallet signature required",
+ extensions: declareSIWxExtension({
+ network: ["eip155:84532", "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1"],
+ statement: "Sign in to view your profile",
+ expirationSeconds: 300,
+ }),
+ },
};
```
@@ -127,6 +142,8 @@ const httpServer = new x402HTTPResourceServer(resourceServer, routes)
.onProtectedRequest(createSIWxRequestHook({ storage })); // Checks SIWX auth
```
+For routes declared with `accepts: []`, the request hook grants access on valid SIWX alone. For paid routes, it also checks whether that wallet has already paid.
+
## Storage Backend
This example uses in-memory storage (`InMemorySIWxStorage`). For production, implement persistent storage:
@@ -135,11 +152,11 @@ This example uses in-memory storage (`InMemorySIWxStorage`). For production, imp
import { SIWxStorage } from "@x402/extensions/sign-in-with-x";
class RedisSIWxStorage implements SIWxStorage {
- async recordPayment(address: string, resource: string): Promise {
+ async recordPayment(resource: string, address: string): Promise {
// Store in Redis/database
}
- async hasAccess(address: string, resource: string): Promise {
+ async hasPaid(resource: string, address: string): Promise {
// Check Redis/database
}
}
@@ -171,5 +188,6 @@ createSIWxRequestHook({ storage, onEvent });
Event types:
- `payment_recorded` — Wallet paid for resource
-- `access_granted` — SIWX signature verified
-- `access_denied` — Invalid or missing signature
\ No newline at end of file
+- `access_granted` — SIWX signature verified and access granted
+- `validation_failed` — Header parsing, message validation, or signature verification failed
+- `nonce_reused` — A previously used SIWX nonce was replayed
diff --git a/examples/typescript/servers/sign-in-with-x/index.ts b/examples/typescript/servers/sign-in-with-x/index.ts
index 2801fa8728..0bfdaa6d91 100644
--- a/examples/typescript/servers/sign-in-with-x/index.ts
+++ b/examples/typescript/servers/sign-in-with-x/index.ts
@@ -14,6 +14,7 @@ import {
createSIWxSettleHook,
createSIWxRequestHook,
InMemorySIWxStorage,
+ parseSIWxHeader,
} from "@x402/extensions/sign-in-with-x";
config();
@@ -95,6 +96,15 @@ function routeConfig(path: string) {
const routes = {
"GET /weather": routeConfig("/weather"),
"GET /joke": routeConfig("/joke"),
+ "GET /profile": {
+ accepts: [] as [],
+ description: "Auth-only: wallet signature required",
+ extensions: declareSIWxExtension({
+ network: [EVM_NETWORK, SVM_NETWORK], // Required for auth-only routes (no payment to infer from)
+ statement: "Sign in to view your profile",
+ expirationSeconds: 300,
+ }),
+ },
};
// Configure resource server with SIWX extension and settle hook
@@ -114,14 +124,20 @@ const httpServer = new x402HTTPResourceServer(resourceServer, routes).onProtecte
);
const app = express();
+
app.use(paymentMiddlewareFromHTTPServer(httpServer));
app.get("/weather", (_req, res) => res.json({ weather: "sunny", temperature: 72 }));
app.get("/joke", (_req, res) =>
res.json({ joke: "Why do programmers prefer dark mode? Because light attracts bugs." }),
);
+app.get("/profile", (req, res) => {
+ // SIWX hook already verified the signature — just parse to extract the address
+ const { address } = parseSIWxHeader(req.headers["sign-in-with-x"] as string);
+ res.json({ address, data: "Your profile data" });
+});
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
- console.log(`Routes: GET /weather, GET /joke`);
+ console.log(`Routes: GET /weather, GET /joke, GET /profile (auth-only)`);
});
diff --git a/examples/typescript/servers/upto/.env-local b/examples/typescript/servers/upto/.env-local
new file mode 100644
index 0000000000..7c000b6d83
--- /dev/null
+++ b/examples/typescript/servers/upto/.env-local
@@ -0,0 +1,2 @@
+EVM_ADDRESS=
+FACILITATOR_URL=https://x402.org/facilitator
diff --git a/examples/typescript/servers/upto/.prettierignore b/examples/typescript/servers/upto/.prettierignore
new file mode 100644
index 0000000000..5bd240ba90
--- /dev/null
+++ b/examples/typescript/servers/upto/.prettierignore
@@ -0,0 +1,8 @@
+docs/
+dist/
+node_modules/
+coverage/
+.github/
+src/client
+**/**/*.json
+*.md
\ No newline at end of file
diff --git a/examples/typescript/servers/upto/.prettierrc b/examples/typescript/servers/upto/.prettierrc
new file mode 100644
index 0000000000..ffb416b74b
--- /dev/null
+++ b/examples/typescript/servers/upto/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "singleQuote": false,
+ "trailingComma": "all",
+ "bracketSpacing": true,
+ "arrowParens": "avoid",
+ "printWidth": 100,
+ "proseWrap": "never"
+}
diff --git a/examples/typescript/servers/upto/eslint.config.js b/examples/typescript/servers/upto/eslint.config.js
new file mode 100644
index 0000000000..e2fde7b3b8
--- /dev/null
+++ b/examples/typescript/servers/upto/eslint.config.js
@@ -0,0 +1,73 @@
+import js from "@eslint/js";
+import ts from "@typescript-eslint/eslint-plugin";
+import tsParser from "@typescript-eslint/parser";
+import prettier from "eslint-plugin-prettier";
+import jsdoc from "eslint-plugin-jsdoc";
+import importPlugin from "eslint-plugin-import";
+
+export default [
+ {
+ ignores: ["dist/**", "node_modules/**"],
+ },
+ {
+ files: ["**/*.ts"],
+ languageOptions: {
+ parser: tsParser,
+ sourceType: "module",
+ ecmaVersion: 2020,
+ globals: {
+ process: "readonly",
+ __dirname: "readonly",
+ module: "readonly",
+ require: "readonly",
+ Buffer: "readonly",
+ console: "readonly",
+ exports: "readonly",
+ setTimeout: "readonly",
+ clearTimeout: "readonly",
+ setInterval: "readonly",
+ clearInterval: "readonly",
+ },
+ },
+ plugins: {
+ "@typescript-eslint": ts,
+ prettier: prettier,
+ jsdoc: jsdoc,
+ import: importPlugin,
+ },
+ rules: {
+ ...ts.configs.recommended.rules,
+ "import/first": "error",
+ "prettier/prettier": "error",
+ "@typescript-eslint/member-ordering": "error",
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_$" }],
+ "jsdoc/tag-lines": ["error", "any", { startLines: 1 }],
+ "jsdoc/check-alignment": "error",
+ "jsdoc/no-undefined-types": "off",
+ "jsdoc/check-param-names": "error",
+ "jsdoc/check-tag-names": "error",
+ "jsdoc/check-types": "error",
+ "jsdoc/implements-on-classes": "error",
+ "jsdoc/require-description": "error",
+ "jsdoc/require-jsdoc": [
+ "error",
+ {
+ require: {
+ FunctionDeclaration: true,
+ MethodDefinition: true,
+ ClassDeclaration: true,
+ ArrowFunctionExpression: false,
+ FunctionExpression: false,
+ },
+ },
+ ],
+ "jsdoc/require-param": "error",
+ "jsdoc/require-param-description": "error",
+ "jsdoc/require-param-type": "off",
+ "jsdoc/require-returns": "error",
+ "jsdoc/require-returns-description": "error",
+ "jsdoc/require-returns-type": "off",
+ "jsdoc/require-hyphen-before-param-description": ["error", "always"],
+ },
+ },
+];
diff --git a/examples/typescript/servers/upto/index.ts b/examples/typescript/servers/upto/index.ts
new file mode 100644
index 0000000000..f75071bc9c
--- /dev/null
+++ b/examples/typescript/servers/upto/index.ts
@@ -0,0 +1,70 @@
+import { config } from "dotenv";
+import express from "express";
+import { paymentMiddleware, setSettlementOverrides, x402ResourceServer } from "@x402/express";
+import { UptoEvmScheme } from "@x402/evm/upto/server";
+import { HTTPFacilitatorClient } from "@x402/core/server";
+import { declareEip2612GasSponsoringExtension } from "@x402/extensions";
+config();
+
+const evmAddress = process.env.EVM_ADDRESS as `0x${string}`;
+if (!evmAddress) {
+ console.error("Missing required EVM_ADDRESS environment variable");
+ process.exit(1);
+}
+
+const facilitatorUrl = process.env.FACILITATOR_URL;
+if (!facilitatorUrl) {
+ console.error("Missing required FACILITATOR_URL environment variable");
+ process.exit(1);
+}
+const facilitatorClient = new HTTPFacilitatorClient({ url: facilitatorUrl });
+
+const app = express();
+
+// The "upto" scheme authorizes up to a maximum amount but settles only what you specify.
+// This enables usage-based billing: authorize a ceiling, then charge actual usage.
+const maxPrice = "$0.10"; // Maximum the client authorizes (10 cents)
+
+app.use(
+ paymentMiddleware(
+ {
+ "GET /api/generate": {
+ accepts: {
+ scheme: "upto",
+ price: maxPrice,
+ network: "eip155:84532",
+ payTo: evmAddress,
+ },
+ description: "AI text generation — billed by token usage",
+ mimeType: "application/json",
+ extensions: {
+ ...declareEip2612GasSponsoringExtension(),
+ },
+ },
+ },
+ new x402ResourceServer(facilitatorClient).register("eip155:84532", new UptoEvmScheme()),
+ ),
+);
+
+app.get("/api/generate", (req, res) => {
+ // Simulate work that produces a variable cost.
+ // In production this might be LLM token count, bytes served, compute time, etc.
+ const maxAmountAtomic = 100000; // 10 cents in 6-decimal USDC atomic units
+ const actualUsage = Math.floor(Math.random() * (maxAmountAtomic + 1));
+
+ // Tell the middleware to settle only what was actually used.
+ setSettlementOverrides(res, { amount: String(actualUsage) });
+
+ res.json({
+ result: "Here is your generated text...",
+ usage: {
+ authorizedMaxAtomic: String(maxAmountAtomic),
+ actualChargedAtomic: String(actualUsage),
+ },
+ });
+});
+
+app.listen(4021, () => {
+ console.log("Upto server listening at http://localhost:4021");
+ console.log(" GET /api/generate — usage-based billing via upto scheme");
+});
diff --git a/examples/typescript/servers/upto/package.json b/examples/typescript/servers/upto/package.json
new file mode 100644
index 0000000000..8caa658be5
--- /dev/null
+++ b/examples/typescript/servers/upto/package.json
@@ -0,0 +1,34 @@
+{
+ "name": "@x402/upto-server-example",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "tsx index.ts",
+ "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"",
+ "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"",
+ "lint": "eslint . --ext .ts --fix",
+ "lint:check": "eslint . --ext .ts"
+ },
+ "dependencies": {
+ "@x402/core": "workspace:*",
+ "@x402/evm": "workspace:*",
+ "@x402/express": "workspace:*",
+ "@x402/extensions": "workspace:*",
+ "dotenv": "^16.4.7",
+ "express": "^4.18.2"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.24.0",
+ "@types/express": "^5.0.1",
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
+ "@typescript-eslint/parser": "^8.29.1",
+ "eslint": "^9.24.0",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsdoc": "^50.6.9",
+ "eslint-plugin-prettier": "^5.2.6",
+ "prettier": "3.5.2",
+ "tsup": "^7.2.0",
+ "tsx": "^4.7.0",
+ "typescript": "^5.3.0"
+ }
+}
diff --git a/examples/typescript/servers/upto/tsconfig.json b/examples/typescript/servers/upto/tsconfig.json
new file mode 100644
index 0000000000..78f9479b1b
--- /dev/null
+++ b/examples/typescript/servers/upto/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "module": "ES2020",
+ "moduleResolution": "bundler",
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "resolveJsonModule": true,
+ "baseUrl": ".",
+ "types": ["node"]
+ },
+ "include": ["index.ts"]
+}
diff --git a/go/.changes/unreleased/add-arbitrum-one-and-arbitrum-sepolia-default-stablecoin.yaml b/go/.changes/unreleased/add-arbitrum-one-and-arbitrum-sepolia-default-stablecoin.yaml
new file mode 100644
index 0000000000..a4abbc4adc
--- /dev/null
+++ b/go/.changes/unreleased/add-arbitrum-one-and-arbitrum-sepolia-default-stablecoin.yaml
@@ -0,0 +1,2 @@
+kind: added
+body: Add Arbitrum One (chain ID 42161) and Arbitrum Sepolid (chain ID 421614) support with USDC as the default stablecoin
\ No newline at end of file
diff --git a/go/.changes/unreleased/added-20260227-085721.yaml b/go/.changes/unreleased/added-20260227-085721.yaml
deleted file mode 100644
index 838dd8748f..0000000000
--- a/go/.changes/unreleased/added-20260227-085721.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-kind: added
-body: Add route configuration validation during Initialize() to catch scheme/facilitator mismatches at startup
-time: 2026-02-27T08:57:21.873969+09:00
diff --git a/go/.changes/unreleased/added-20260303-121913.yaml b/go/.changes/unreleased/added-20260303-121913.yaml
new file mode 100644
index 0000000000..783a23343b
--- /dev/null
+++ b/go/.changes/unreleased/added-20260303-121913.yaml
@@ -0,0 +1,3 @@
+kind: added
+body: Add net/http standard library adapter for x402 payment middleware (http/nethttp package)
+time: 2026-03-03T12:19:13.785928+09:00
diff --git a/go/.changes/unreleased/added-20260303-144745.yaml b/go/.changes/unreleased/added-20260303-144745.yaml
new file mode 100644
index 0000000000..58b54c2218
--- /dev/null
+++ b/go/.changes/unreleased/added-20260303-144745.yaml
@@ -0,0 +1,3 @@
+kind: added
+body: Add Echo framework middleware adapter for x402 payment handling in go/http/echo package
+time: 2026-03-03T14:47:45.605889+09:00
diff --git a/go/.changes/unreleased/changed-20260225-174750.yaml b/go/.changes/unreleased/changed-20260225-174750.yaml
deleted file mode 100644
index 37b6385c4b..0000000000
--- a/go/.changes/unreleased/changed-20260225-174750.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-kind: changed
-body: Separated v1 legacy network name resolution from v2 CAIP-2 resolution; v1 code now uses evm/v1 package, shared utils only accept eip155:CHAIN_ID format
-time: 2026-02-25T17:47:50.706805-08:00
diff --git a/go/.changes/unreleased/changed-20260226-175344.yaml b/go/.changes/unreleased/changed-20260226-175344.yaml
deleted file mode 100644
index 921f0987cb..0000000000
--- a/go/.changes/unreleased/changed-20260226-175344.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
-kind: changed
-body: GetSupported retries up to 3 times with exponential backoff on 429 rate limit responses
-time: 2026-02-26T17:53:44.305222+09:00
diff --git a/go/.changes/unreleased/changed-20260330-155601.yaml b/go/.changes/unreleased/changed-20260330-155601.yaml
new file mode 100644
index 0000000000..f565b5064e
--- /dev/null
+++ b/go/.changes/unreleased/changed-20260330-155601.yaml
@@ -0,0 +1,3 @@
+kind: changed
+body: Updated x402UptoPermit2Proxy canonical address to 0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002, deployed with deterministic bytecode for reproducible cross-chain CREATE2 addresses
+time: 2026-03-30T15:56:01.761933-07:00
diff --git a/go/.changes/unreleased/fixed-20260324-164854.yaml b/go/.changes/unreleased/fixed-20260324-164854.yaml
new file mode 100644
index 0000000000..5f8d2a2509
--- /dev/null
+++ b/go/.changes/unreleased/fixed-20260324-164854.yaml
@@ -0,0 +1,3 @@
+kind: fixed
+body: 'Fix gin streaming content leak and echo panic on flush '
+time: 2026-03-24T16:48:54.420564+09:00
diff --git a/go/.changes/unreleased/mezo-testnet-default-asset.yaml b/go/.changes/unreleased/mezo-testnet-default-asset.yaml
new file mode 100644
index 0000000000..78afc2f626
--- /dev/null
+++ b/go/.changes/unreleased/mezo-testnet-default-asset.yaml
@@ -0,0 +1,2 @@
+kind: added
+body: Add Mezo Testnet (chain ID 31611) support with mUSD as the default stablecoin
diff --git a/go/.changes/unreleased/polygon-support.yaml b/go/.changes/unreleased/polygon-support.yaml
new file mode 100644
index 0000000000..6aaaeb62be
--- /dev/null
+++ b/go/.changes/unreleased/polygon-support.yaml
@@ -0,0 +1,2 @@
+kind: added
+body: Add Polygon mainnet (chain ID 137) support with USDC as the default stablecoin
diff --git a/go/.changes/unreleased/stable-support.yaml b/go/.changes/unreleased/stable-support.yaml
new file mode 100644
index 0000000000..ea6a24c24c
--- /dev/null
+++ b/go/.changes/unreleased/stable-support.yaml
@@ -0,0 +1,2 @@
+kind: added
+body: Add Stable mainnet (chain ID 988) support with USDT0 as the default stablecoin
diff --git a/go/.changes/unreleased/stable-testnet-support.yaml b/go/.changes/unreleased/stable-testnet-support.yaml
new file mode 100644
index 0000000000..b924cd3c12
--- /dev/null
+++ b/go/.changes/unreleased/stable-testnet-support.yaml
@@ -0,0 +1,2 @@
+kind: added
+body: Add Stable testnet (chain ID 2201) support with USDT0 as the default stablecoin
diff --git a/go/.changes/unreleased/upto-evm-scheme.yaml b/go/.changes/unreleased/upto-evm-scheme.yaml
new file mode 100644
index 0000000000..fec62091f4
--- /dev/null
+++ b/go/.changes/unreleased/upto-evm-scheme.yaml
@@ -0,0 +1,3 @@
+kind: added
+body: Add upto EVM payment scheme with client, facilitator, and server support for permit2-based partial settlement on EVM chains
+time: 2026-03-26T00:00:00.000000+00:00
diff --git a/go/.changeset/facilitator-response-errors.md b/go/.changeset/facilitator-response-errors.md
new file mode 100644
index 0000000000..d6a01891c5
--- /dev/null
+++ b/go/.changeset/facilitator-response-errors.md
@@ -0,0 +1,5 @@
+---
+'github.com/coinbase/x402/go': patch
+---
+
+Treat malformed facilitator success payloads as facilitator boundary errors in the Go HTTP client instead of surfacing them as verification or settlement failures.
diff --git a/go/CHANGELOG.md b/go/CHANGELOG.md
index 402d4af069..6e218768ad 100644
--- a/go/CHANGELOG.md
+++ b/go/CHANGELOG.md
@@ -1,3 +1,34 @@
+## v2.7.0 - 2026-03-23
+### Changed
+- Changed Bazaar discovery extension to support dynamic route patterns. EnrichDeclaration now
+translates [param] route segments to :param-style routeTemplate and populates pathParams with
+concrete values from each request. The EnrichExtensions call in go/http/server.go, previously
+disabled (commented out) in all prior Go releases, is now active: ALL existing Go routes that
+declare extensions will have their extensions enriched at request time. Added RouteTemplate field
+to DiscoveryExtension so callers can read it without a type assertion.
+
+## v2.6.0 - 2026-03-17
+### Added
+- Added simulation to permit2 verify and (optional) settle
+### Changed
+- Replaced SendRawApprovalAndSettle with a generic SendTransactions signer method that accepts an array of transaction requests (pre-signed or unsigned intents). Closed fail-open verification paths, aligned Permit2 amount check to exact match, and improved client extension fallback error handling
+- Simulate transaction in verify and (optional) settle; Added multicall utility for efficient rpc calls; Fixed undeployed smart wallet handling
+### Fixed
+- Fixed paywall config injection targeting `
\n \n \n \n ` causing SVG parse errors in the browser
+
+## v2.5.0 - 2026-03-06
+### Added
+- Add route configuration validation during Initialize() to catch scheme/facilitator mismatches at startup
+- Added assetTransferMethod and supportsEip2612 flag to defaultAssets
+- Added `onProtectedRequest` hook to HTTP resource server
+- Add WithBazaar facilitator client decorator for querying /discovery/resources endpoint from bazaar in go
+- Added dynamic function for servers to generate custom response for settlement failures defaulting to empty
+- Add in-memory SettlementCache to prevent duplicate SVM transaction settlement during on-chain confirmation window
+### Changed
+- Separated v1 legacy network name resolution from v2 CAIP-2 resolution; v1 code now uses evm/v1 package, shared utils only accept eip155:CHAIN_ID format
+- GetSupported retries up to 3 times with exponential backoff on 429 rate limit responses
+- Add pluggable PaywallProvider interface for custom paywall HTML generation with PaywallBuilder pattern
+
## 2.4.1 - 2026-02-25
### Fixed
- Fixed changelog generation to include version extension and eliminate trailing dots which prevent go from importing
diff --git a/go/CLIENT.md b/go/CLIENT.md
index c78fa1d96a..90bbfbee00 100644
--- a/go/CLIENT.md
+++ b/go/CLIENT.md
@@ -39,7 +39,7 @@ func main() {
// 2. Create x402 client and register schemes
client := x402.Newx402Client().
- Register("eip155:*", evm.NewExactEvmScheme(signer))
+ Register("eip155:*", evm.NewExactEvmScheme(signer, nil))
// 3. Wrap HTTP client
httpClient := x402http.WrapHTTPClientWithPayment(
@@ -109,7 +109,7 @@ Register mechanisms to enable payment creation for different networks.
```go
// All EVM networks
-client.Register("eip155:*", evm.NewExactEvmScheme(evmSigner))
+client.Register("eip155:*", evm.NewExactEvmScheme(evmSigner, nil))
// All Solana networks
client.Register("solana:*", svm.NewExactSvmScheme(svmSigner))
@@ -119,13 +119,13 @@ client.Register("solana:*", svm.NewExactSvmScheme(svmSigner))
```go
// Ethereum Mainnet
-client.Register("eip155:1", evm.NewExactEvmScheme(mainnetSigner))
+client.Register("eip155:1", evm.NewExactEvmScheme(mainnetSigner, nil))
// Base Mainnet
-client.Register("eip155:8453", evm.NewExactEvmScheme(baseSigner))
+client.Register("eip155:8453", evm.NewExactEvmScheme(baseSigner, nil))
// Base Sepolia
-client.Register("eip155:84532", evm.NewExactEvmScheme(testnetSigner))
+client.Register("eip155:84532", evm.NewExactEvmScheme(testnetSigner, nil))
```
#### Registration Precedence
@@ -134,8 +134,34 @@ More specific registrations override wildcards:
```go
client.
- Register("eip155:*", evm.NewExactEvmScheme(defaultSigner)). // Fallback
- Register("eip155:1", evm.NewExactEvmScheme(mainnetSigner)) // Override for mainnet
+ Register("eip155:*", evm.NewExactEvmScheme(defaultSigner, nil)). // Fallback
+ Register("eip155:1", evm.NewExactEvmScheme(mainnetSigner, nil)) // Override for mainnet
+```
+
+#### Optional Extension RPC Config (Explicit-Only)
+
+`NewExactEvmScheme` supports optional RPC config used only for extension
+enrichment when signer read/fee capabilities are unavailable.
+
+No chain-default RPC fallback is applied by the SDK.
+
+```go
+// Per-network explicit registration
+client.
+ Register("eip155:137", evm.NewExactEvmScheme(polygonSigner, &evm.ExactEvmSchemeConfig{
+ RPCURL: "https://polygon.example",
+ })).
+ Register("eip155:8453", evm.NewExactEvmScheme(baseSigner, &evm.ExactEvmSchemeConfig{
+ RPCURL: "https://base.example",
+ }))
+
+// Wildcard registration with chain-id map
+client.Register("eip155:*", evm.NewExactEvmScheme(defaultSigner, &evm.ExactEvmSchemeConfig{
+ RPCByChainID: map[int64]evm.ExactEvmChainConfig{
+ 137: {RPCURL: "https://polygon.example"},
+ 8453: {RPCURL: "https://base.example"},
+ },
+}))
```
### 4. HTTP Integration
diff --git a/go/FACILITATOR.md b/go/FACILITATOR.md
index a45222c8d4..c45409332d 100644
--- a/go/FACILITATOR.md
+++ b/go/FACILITATOR.md
@@ -382,6 +382,24 @@ Facilitator signers need to:
- Monitor for unusual patterns
- Set transaction value limits
+#### Duplicate Settlement (Solana / SVM)
+
+A race condition exists on Solana where the same payment transaction can be submitted to the `/settle` endpoint multiple times before the first submission is confirmed on-chain. Because Solana's RPC returns "success" for duplicate transaction submissions (the network deduplicates at the consensus level), the facilitator could return `success` to each caller. A malicious client can exploit this to obtain access to multiple resources while only paying once.
+
+The SVM mechanism packages include a built-in `SettlementCache` that mitigates this. When registering SVM facilitator schemes, pass a shared cache instance to both V1 and V2 schemes:
+
+```go
+import svm "github.com/coinbase/x402/go/mechanisms/svm"
+
+cache := svm.NewSettlementCache()
+v2Scheme := facilitator.NewExactSvmScheme(signer, cache)
+v1Scheme := v1facilitator.NewExactSvmSchemeV1(signer, cache)
+```
+
+The cache rejects concurrent settlement attempts for the same transaction payload with a `duplicate_settlement` error. Entries are evicted after 120 seconds (approximately twice the Solana blockhash lifetime).
+
+See the [Exact SVM Scheme Specification](../specs/schemes/exact/scheme_exact_svm.md#duplicate-settlement-mitigation-recommended) for full details.
+
### High Availability
- Run multiple facilitator instances
diff --git a/go/SERVER.md b/go/SERVER.md
index f6a2495f6d..f34d3eec01 100644
--- a/go/SERVER.md
+++ b/go/SERVER.md
@@ -147,8 +147,12 @@ httpServer := x402http.Newx402HTTPResourceServer(
// Process HTTP requests
result := httpServer.ProcessHTTPRequest(ctx, reqCtx, nil)
-// Handle settlement
-headers, _ := httpServer.ProcessSettlement(ctx, payload, requirements, statusCode)
+// Handle settlement with transport context
+settleResult := httpServer.ProcessSettlement(ctx, payload, requirements, nil, &x402http.HTTPTransportContext{
+ Request: &reqCtx,
+ ResponseBody: responseBody,
+ ResponseHeaders: responseHeaders,
+})
```
### 4. Facilitator Client
diff --git a/go/constants.go b/go/constants.go
index 27cda41c8f..3640ab3085 100644
--- a/go/constants.go
+++ b/go/constants.go
@@ -3,7 +3,7 @@ package x402
// Version constants
const (
// Version is the SDK version
- Version = "2.4.0"
+ Version = "2.7.0"
// ProtocolVersion is the current x402 protocol version
ProtocolVersion = 2
diff --git a/go/extensions/bazaar/bazaar_test.go b/go/extensions/bazaar/bazaar_test.go
index fac691b810..8eb7591269 100644
--- a/go/extensions/bazaar/bazaar_test.go
+++ b/go/extensions/bazaar/bazaar_test.go
@@ -1615,6 +1615,47 @@ func TestExtractDiscoveredResourceFromPaymentRequired(t *testing.T) {
require.NotNil(t, info)
assert.Equal(t, "GET", info.Method)
})
+
+ t.Run("v2: should use routeTemplate as canonical URL for dynamic routes", func(t *testing.T) {
+ // Mirrors the TestBazaarDynamicRoutes/should use routeTemplate for canonical URL test,
+ // but exercises the ExtractDiscoveredResourceFromPaymentRequired code path so that
+ // facilitators consuming payment-required responses also produce stable catalog keys.
+ enrichedExt := map[string]interface{}{
+ "info": map[string]interface{}{
+ "input": map[string]interface{}{
+ "type": "http",
+ "method": "GET",
+ },
+ },
+ "schema": map[string]interface{}{},
+ "routeTemplate": "/products/:productId",
+ }
+
+ paymentRequired := x402.PaymentRequired{
+ X402Version: 2,
+ Resource: &x402.ResourceInfo{
+ URL: "https://api.example.com/products/42",
+ },
+ Accepts: []x402.PaymentRequirements{
+ {
+ Scheme: "exact",
+ Network: "eip155:8453",
+ },
+ },
+ Extensions: map[string]interface{}{
+ bazaar.BAZAAR.Key(): enrichedExt,
+ },
+ }
+
+ paymentRequiredBytes, _ := json.Marshal(paymentRequired)
+
+ info, err := bazaar.ExtractDiscoveredResourceFromPaymentRequired(paymentRequiredBytes, false)
+ require.NoError(t, err)
+ require.NotNil(t, info)
+ // The routeTemplate replaces the concrete URL path as the canonical catalog key
+ assert.Equal(t, "https://api.example.com/products/:productId", info.ResourceURL)
+ assert.Equal(t, "/products/:productId", info.RouteTemplate)
+ })
}
// extractMethodEnum is a test helper that extracts the method enum from a discovery extension schema.
@@ -1775,6 +1816,58 @@ func TestBazaarResourceServerExtension(t *testing.T) {
assert.True(t, hasMethod, "method should be in required array")
})
+ t.Run("should produce a valid extension after enrichment (GET)", func(t *testing.T) {
+ extension, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET,
+ map[string]interface{}{"query": "test"},
+ bazaar.JSONSchema{
+ "properties": map[string]interface{}{
+ "query": map[string]interface{}{"type": "string"},
+ },
+ },
+ "",
+ nil,
+ )
+ require.NoError(t, err)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok)
+
+ result := bazaar.ValidateDiscoveryExtension(enrichedExt)
+ assert.True(t, result.Valid, "enriched GET extension should pass validation: %v", result.Errors)
+ })
+
+ t.Run("should produce a valid extension after enrichment (POST)", func(t *testing.T) {
+ extension, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodPOST,
+ map[string]interface{}{"data": "test"},
+ bazaar.JSONSchema{
+ "properties": map[string]interface{}{
+ "data": map[string]interface{}{"type": "string"},
+ },
+ },
+ bazaar.BodyTypeJSON,
+ nil,
+ )
+ require.NoError(t, err)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "POST",
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok)
+
+ result := bazaar.ValidateDiscoveryExtension(enrichedExt)
+ assert.True(t, result.Valid, "enriched POST extension should pass validation: %v", result.Errors)
+ })
+
t.Run("should return unchanged declaration for non-HTTP context", func(t *testing.T) {
extension, err := bazaar.DeclareDiscoveryExtension(
bazaar.MethodPOST,
@@ -1800,3 +1893,303 @@ func TestBazaarResourceServerExtension(t *testing.T) {
assert.Equal(t, extension.Info, resultExt.Info)
})
}
+
+func declareEmptyGETExtension(t *testing.T) bazaar.DiscoveryExtension {
+ t.Helper()
+ ext, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET,
+ map[string]interface{}{},
+ bazaar.JSONSchema{"properties": map[string]interface{}{}},
+ "",
+ nil,
+ )
+ require.NoError(t, err)
+ return ext
+}
+
+type mockHTTPAdapterForBazaar struct {
+ headers map[string]string
+ method string
+ path string
+ url string
+ accept string
+ agent string
+}
+
+func (m *mockHTTPAdapterForBazaar) GetHeader(name string) string {
+ if m.headers == nil {
+ return ""
+ }
+ return m.headers[name]
+}
+func (m *mockHTTPAdapterForBazaar) GetMethod() string { return m.method }
+func (m *mockHTTPAdapterForBazaar) GetPath() string { return m.path }
+func (m *mockHTTPAdapterForBazaar) GetURL() string { return m.url }
+func (m *mockHTTPAdapterForBazaar) GetAcceptHeader() string { return m.accept }
+func (m *mockHTTPAdapterForBazaar) GetUserAgent() string { return m.agent }
+
+func TestBazaarDynamicRoutes(t *testing.T) {
+ t.Run("should leave static routes unchanged", func(t *testing.T) {
+ extension, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET,
+ map[string]interface{}{"query": "test"},
+ bazaar.JSONSchema{"properties": map[string]interface{}{"query": map[string]interface{}{"type": "string"}}},
+ "",
+ nil,
+ )
+ require.NoError(t, err)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/users",
+ RoutePattern: "/users",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/users"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ // Static route: enriched should be DiscoveryExtension with empty RouteTemplate
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok, "should always produce DiscoveryExtension")
+ assert.Empty(t, enrichedExt.RouteTemplate, "static route should have empty RouteTemplate")
+ })
+
+ t.Run("should produce routeTemplate for dynamic routes", func(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/users/123",
+ RoutePattern: "/users/[userId]",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/users/123"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok, "dynamic route should produce a DiscoveryExtension")
+ assert.Equal(t, "/users/:userId", enrichedExt.RouteTemplate)
+ })
+
+ t.Run("should extract path params from concrete URL", func(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/users/123",
+ RoutePattern: "/users/[userId]",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/users/123"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok)
+
+ queryInput, ok := enrichedExt.Info.Input.(bazaar.QueryInput)
+ require.True(t, ok)
+ require.NotNil(t, queryInput.PathParams)
+ assert.Equal(t, "123", queryInput.PathParams["userId"])
+ })
+
+ t.Run("should extract multiple path params", func(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/users/42/posts/7",
+ RoutePattern: "/users/[userId]/posts/[postId]",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/users/42/posts/7"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok)
+ assert.Equal(t, "/users/:userId/posts/:postId", enrichedExt.RouteTemplate)
+
+ queryInput, ok := enrichedExt.Info.Input.(bazaar.QueryInput)
+ require.True(t, ok)
+ assert.Equal(t, "42", queryInput.PathParams["userId"])
+ assert.Equal(t, "7", queryInput.PathParams["postId"])
+ })
+
+ t.Run("should produce routeTemplate for colon-style dynamic routes", func(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/users/123",
+ RoutePattern: "/users/:userId",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/users/123"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok, "colon-param dynamic route should produce a DiscoveryExtension")
+ assert.Equal(t, "/users/:userId", enrichedExt.RouteTemplate)
+ })
+
+ t.Run("should extract path params from colon-style pattern", func(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/users/42/posts/7",
+ RoutePattern: "/users/:userId/posts/:postId",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/users/42/posts/7"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok)
+ assert.Equal(t, "/users/:userId/posts/:postId", enrichedExt.RouteTemplate)
+
+ queryInput, ok := enrichedExt.Info.Input.(bazaar.QueryInput)
+ require.True(t, ok)
+ assert.Equal(t, "42", queryInput.PathParams["userId"])
+ assert.Equal(t, "7", queryInput.PathParams["postId"])
+ })
+
+ t.Run("should handle mixed [param] and :param patterns", func(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/users/42/posts/7",
+ RoutePattern: "/users/[userId]/posts/:postId",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/users/42/posts/7"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok)
+ assert.Equal(t, "/users/:userId/posts/:postId", enrichedExt.RouteTemplate)
+
+ queryInput, ok := enrichedExt.Info.Input.(bazaar.QueryInput)
+ require.True(t, ok)
+ assert.Equal(t, "42", queryInput.PathParams["userId"])
+ assert.Equal(t, "7", queryInput.PathParams["postId"])
+ })
+
+ t.Run("should auto-convert wildcard * to :varN for discovery", func(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/weather/san-francisco",
+ RoutePattern: "/weather/*",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/weather/san-francisco"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok)
+ assert.Equal(t, "/weather/:var1", enrichedExt.RouteTemplate)
+
+ queryInput, ok := enrichedExt.Info.Input.(bazaar.QueryInput)
+ require.True(t, ok)
+ assert.Equal(t, "san-francisco", queryInput.PathParams["var1"])
+ })
+
+ t.Run("should auto-convert multiple wildcards to :var1 :var2 etc", func(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ httpContext := x402http.HTTPRequestContext{
+ Method: "GET",
+ Path: "/api/users/42/posts/7",
+ RoutePattern: "/api/*/*/posts/*",
+ Adapter: &mockHTTPAdapterForBazaar{path: "/api/users/42/posts/7"},
+ }
+
+ enriched := bazaar.BazaarResourceServerExtension.EnrichDeclaration(extension, httpContext)
+
+ enrichedExt, ok := enriched.(bazaar.DiscoveryExtension)
+ require.True(t, ok)
+ assert.Equal(t, "/api/:var1/:var2/posts/:var3", enrichedExt.RouteTemplate)
+ })
+
+ t.Run("should use concrete URL for static routes", func(t *testing.T) {
+ extension, err := bazaar.DeclareDiscoveryExtension(
+ bazaar.MethodGET,
+ map[string]interface{}{"query": "test"},
+ bazaar.JSONSchema{"properties": map[string]interface{}{"query": map[string]interface{}{"type": "string"}}},
+ "",
+ nil,
+ )
+ require.NoError(t, err)
+
+ extensionJSON, err := json.Marshal(map[string]interface{}{
+ "x402Version": 2,
+ "scheme": "exact",
+ "network": "eip155:8453",
+ "payload": map[string]interface{}{},
+ "accepted": map[string]interface{}{},
+ "resource": map[string]interface{}{"url": "http://example.com/search?q=test"},
+ "extensions": map[string]interface{}{
+ bazaar.BAZAAR.Key(): extension,
+ },
+ })
+ require.NoError(t, err)
+
+ var payload x402.PaymentPayload
+ require.NoError(t, json.Unmarshal(extensionJSON, &payload))
+
+ payloadJSON, err := json.Marshal(payload)
+ require.NoError(t, err)
+
+ discovered, err := bazaar.ExtractDiscoveredResourceFromPaymentPayload(payloadJSON, nil, false)
+ require.NoError(t, err)
+ require.NotNil(t, discovered)
+ assert.Equal(t, "http://example.com/search", discovered.ResourceURL)
+ assert.Empty(t, discovered.RouteTemplate)
+ })
+}
+
+// TestDynamicRoutesCatalogConsolidation verifies that two requests to the same
+// parameterized route produce the same canonical ResourceURL, so a catalog keyed by ResourceURL
+// contains exactly one entry regardless of how many distinct concrete parameter values arrive.
+func TestDynamicRoutesCatalogConsolidation(t *testing.T) {
+ extension := declareEmptyGETExtension(t)
+
+ makePayloadJSON := func(userID string) []byte {
+ enrichedExt := map[string]interface{}{
+ "info": extension.Info,
+ "schema": extension.Schema,
+ "routeTemplate": "/users/:userId",
+ }
+ b, err := json.Marshal(map[string]interface{}{
+ "x402Version": 2,
+ "scheme": "exact",
+ "network": "eip155:8453",
+ "payload": map[string]interface{}{},
+ "accepted": map[string]interface{}{},
+ "resource": map[string]interface{}{"url": "http://api.example.com/users/" + userID},
+ "extensions": map[string]interface{}{
+ bazaar.BAZAAR.Key(): enrichedExt,
+ },
+ })
+ require.NoError(t, err)
+ return b
+ }
+
+ // First request: /users/123
+ discovered1, err := bazaar.ExtractDiscoveredResourceFromPaymentPayload(makePayloadJSON("123"), nil, false)
+ require.NoError(t, err)
+ require.NotNil(t, discovered1)
+
+ // Second request: /users/456 — different concrete ID, same route
+ discovered2, err := bazaar.ExtractDiscoveredResourceFromPaymentPayload(makePayloadJSON("456"), nil, false)
+ require.NoError(t, err)
+ require.NotNil(t, discovered2)
+
+ // Both must produce the same canonical URL so catalog sees a single entry
+ assert.Equal(t, "http://api.example.com/users/:userId", discovered1.ResourceURL)
+ assert.Equal(t, "http://api.example.com/users/:userId", discovered2.ResourceURL)
+ assert.Equal(t, discovered1.ResourceURL, discovered2.ResourceURL,
+ "requests to the same parameterized route should consolidate to one catalog entry")
+}
diff --git a/go/extensions/bazaar/facilitator.go b/go/extensions/bazaar/facilitator.go
index 1e251bc307..ef54483a27 100644
--- a/go/extensions/bazaar/facilitator.go
+++ b/go/extensions/bazaar/facilitator.go
@@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"net/url"
+ "regexp"
+ "strings"
x402 "github.com/coinbase/x402/go"
"github.com/coinbase/x402/go/extensions/types"
@@ -93,6 +95,7 @@ type DiscoveredResource struct {
DiscoveryInfo *types.DiscoveryInfo
Description string
MimeType string
+ RouteTemplate string
}
// ExtractDiscoveredResourceFromPaymentPayload extracts a discovered resource from a client's payment payload and requirements.
@@ -142,6 +145,7 @@ func ExtractDiscoveredResourceFromPaymentPayload(
var resourceURL string
var description string
var mimeType string
+ var routeTemplate string
version := versionCheck.X402Version
switch version {
@@ -162,6 +166,18 @@ func ExtractDiscoveredResourceFromPaymentPayload(
// Extract discovery info from extensions
if payload.Extensions != nil {
if bazaarExt, ok := payload.Extensions[types.BAZAAR.Key()]; ok {
+ // routeTemplate uses :param syntax (e.g. "/users/:userId", "/weather/:country/:city").
+ // Must start with "/", must not contain ".." or "://".
+ var rawTemplate string
+ if m, ok := bazaarExt.(map[string]interface{}); ok {
+ if v, ok := m["routeTemplate"]; ok {
+ rawTemplate, _ = v.(string)
+ }
+ }
+ if isValidRouteTemplate(rawTemplate) {
+ routeTemplate = rawTemplate
+ }
+
extensionJSON, err := json.Marshal(bazaarExt)
if err != nil {
return nil, fmt.Errorf("failed to marshal bazaar extension: %w", err)
@@ -221,8 +237,7 @@ func ExtractDiscoveredResourceFromPaymentPayload(
return nil, fmt.Errorf("failed to extract method from discovery info")
}
- // Strip query params and hash for discovery cataloging
- normalizedURL := stripQueryParams(resourceURL)
+ normalizedURL := normalizeResourceURL(resourceURL, routeTemplate)
return &DiscoveredResource{
ResourceURL: normalizedURL,
@@ -231,9 +246,48 @@ func ExtractDiscoveredResourceFromPaymentPayload(
Method: method,
X402Version: version,
DiscoveryInfo: discoveryInfo,
+ RouteTemplate: routeTemplate,
}, nil
}
+// routeTemplateRegex validates the overall shape of a routeTemplate:
+// must start with "/" and contain only safe URL path characters and :param identifiers.
+// Expected format: "/users/:userId", "/weather/:country/:city", "/api/v1/items".
+var routeTemplateRegex = regexp.MustCompile(`^/[a-zA-Z0-9_/:.\-~%]+$`)
+
+// isValidRouteTemplate checks whether a routeTemplate value is structurally valid.
+//
+// Expected format: ":param" segments using colon-prefixed identifiers
+// (e.g. "/users/:userId", "/weather/:country/:city").
+//
+// The facilitator is a trust boundary: the client controls the payment payload and can modify
+// routeTemplate before submission. A malicious value could cause the facilitator to catalog the
+// payment under an arbitrary URL (catalog poisoning). This enforces minimal structural requirements:
+// - Must be a non-empty string starting with "/"
+// - Must match the safe URL path character set (alphanumeric, _, :, /, ., -, ~, %)
+// - Must not contain ".." (path traversal)
+// - Must not contain "://" (URL injection)
+func isValidRouteTemplate(s string) bool {
+ if s == "" {
+ return false
+ }
+ if !routeTemplateRegex.MatchString(s) {
+ return false
+ }
+ // Decode percent-encoding before traversal checks so that %2e%2e is caught.
+ decoded, err := url.PathUnescape(s)
+ if err != nil {
+ return false
+ }
+ if strings.Contains(decoded, "..") {
+ return false
+ }
+ if strings.Contains(decoded, "://") {
+ return false
+ }
+ return true
+}
+
// stripQueryParams removes query parameters and fragments from a URL for cataloging
func stripQueryParams(rawURL string) string {
parsed, err := url.Parse(rawURL)
@@ -245,6 +299,22 @@ func stripQueryParams(rawURL string) string {
return parsed.String()
}
+// normalizeResourceURL returns the canonical URL for discovery cataloging.
+// If routeTemplate is non-empty (dynamic route), it replaces the URL path with the
+// template and strips query/fragment. Otherwise it just strips query/fragment.
+func normalizeResourceURL(rawURL, routeTemplate string) string {
+ if routeTemplate != "" {
+ parsed, err := url.Parse(rawURL)
+ if err == nil {
+ parsed.Path = routeTemplate
+ parsed.RawQuery = ""
+ parsed.Fragment = ""
+ return parsed.String()
+ }
+ }
+ return stripQueryParams(rawURL)
+}
+
// ExtractDiscoveredResourceFromPaymentRequired extracts a discovered resource from a 402 PaymentRequired response.
// This is useful for clients/facilitators that receive a 402 response and want to discover resource capabilities.
//
@@ -293,6 +363,7 @@ func ExtractDiscoveredResourceFromPaymentRequired(
var resourceURL string
var description string
var mimeType string
+ var routeTemplate string
version := versionCheck.X402Version
switch version {
@@ -313,6 +384,18 @@ func ExtractDiscoveredResourceFromPaymentRequired(
// First check PaymentRequired.extensions for bazaar extension
if paymentRequired.Extensions != nil {
if bazaarExt, ok := paymentRequired.Extensions[types.BAZAAR.Key()]; ok {
+ // routeTemplate uses :param syntax (e.g. "/users/:userId", "/weather/:country/:city").
+ // Must start with "/", must not contain ".." or "://".
+ var rawTemplate string
+ if m, ok := bazaarExt.(map[string]interface{}); ok {
+ if v, ok := m["routeTemplate"]; ok {
+ rawTemplate, _ = v.(string)
+ }
+ }
+ if isValidRouteTemplate(rawTemplate) {
+ routeTemplate = rawTemplate
+ }
+
extensionJSON, err := json.Marshal(bazaarExt)
if err != nil {
return nil, fmt.Errorf("failed to marshal bazaar extension: %w", err)
@@ -378,8 +461,7 @@ func ExtractDiscoveredResourceFromPaymentRequired(
return nil, fmt.Errorf("failed to extract method from discovery info")
}
- // Strip query params and hash for discovery cataloging
- normalizedURL := stripQueryParams(resourceURL)
+ normalizedURL := normalizeResourceURL(resourceURL, routeTemplate)
return &DiscoveredResource{
ResourceURL: normalizedURL,
@@ -388,6 +470,7 @@ func ExtractDiscoveredResourceFromPaymentRequired(
Method: method,
X402Version: version,
DiscoveryInfo: discoveryInfo,
+ RouteTemplate: routeTemplate,
}, nil
}
diff --git a/go/extensions/bazaar/facilitator_client.go b/go/extensions/bazaar/facilitator_client.go
new file mode 100644
index 0000000000..90be3b9ab6
--- /dev/null
+++ b/go/extensions/bazaar/facilitator_client.go
@@ -0,0 +1,181 @@
+package bazaar
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+
+ x402http "github.com/coinbase/x402/go/http"
+)
+
+// ListDiscoveryResourcesParams contains optional filtering and pagination parameters
+// for listing discovery resources from a facilitator's bazaar.
+type ListDiscoveryResourcesParams struct {
+ // Type filters by protocol type (e.g., "http", "mcp").
+ Type string
+
+ // Limit is the number of discovered x402 resources to return per page.
+ Limit int
+
+ // Offset is the offset of the first discovered x402 resource to return.
+ Offset int
+}
+
+// DiscoveryResource represents a discovered x402 resource from the bazaar.
+type DiscoveryResource struct {
+ // Resource is the URL or identifier of the discovered resource.
+ Resource string `json:"resource"`
+
+ // Type is the protocol type of the resource (e.g., "http").
+ Type string `json:"type"`
+
+ // X402Version is the x402 protocol version supported by this resource.
+ X402Version int `json:"x402Version"`
+
+ // Accepts is an array of accepted payment methods for this resource.
+ Accepts []json.RawMessage `json:"accepts"`
+
+ // LastUpdated is an ISO 8601 timestamp of when the resource was last updated.
+ LastUpdated string `json:"lastUpdated"`
+
+ // Metadata contains additional metadata about the resource.
+ Metadata map[string]any `json:"metadata,omitempty"`
+}
+
+// Pagination contains pagination information for a discovery resources response.
+type Pagination struct {
+ // Limit is the maximum number of results returned.
+ Limit int `json:"limit"`
+
+ // Offset is the number of results skipped.
+ Offset int `json:"offset"`
+
+ // Total is the total count of resources matching the query.
+ Total int `json:"total"`
+}
+
+// DiscoveryResourcesResponse is the response from listing discovery resources.
+type DiscoveryResourcesResponse struct {
+ // X402Version is the x402 protocol version of this response.
+ X402Version int `json:"x402Version"`
+
+ // Items is the list of discovered resources.
+ Items []DiscoveryResource `json:"items"`
+
+ // Pagination contains pagination information for the response.
+ Pagination Pagination `json:"pagination"`
+}
+
+// BazaarFacilitatorClient wraps an HTTPFacilitatorClient with bazaar discovery
+// query functionality. It preserves all original facilitator client capabilities
+// (Verify, Settle, GetSupported) and adds the ability to list discovered x402
+// resources from the facilitator's bazaar.
+type BazaarFacilitatorClient struct {
+ *x402http.HTTPFacilitatorClient
+}
+
+// WithBazaar extends a facilitator client with bazaar discovery query functionality.
+//
+// Example:
+//
+// client := bazaar.WithBazaar(http.NewHTTPFacilitatorClient(nil))
+// resources, err := client.ListDiscoveryResources(ctx, &bazaar.ListDiscoveryResourcesParams{
+// Type: "http",
+// Limit: 20,
+// })
+func WithBazaar(client *x402http.HTTPFacilitatorClient) *BazaarFacilitatorClient {
+ return &BazaarFacilitatorClient{HTTPFacilitatorClient: client}
+}
+
+// ListDiscoveryResources queries the facilitator's /discovery/resources endpoint
+// to list x402 discovery resources from the bazaar.
+//
+// Params may be nil to list all resources without filtering.
+func (c *BazaarFacilitatorClient) ListDiscoveryResources(
+ ctx context.Context,
+ params *ListDiscoveryResourcesParams,
+) (*DiscoveryResourcesResponse, error) {
+ // Build URL with query parameters
+ endpoint, err := c.buildDiscoveryURL(params)
+ if err != nil {
+ return nil, fmt.Errorf("failed to build discovery URL: %w", err)
+ }
+
+ // Create request
+ req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create discovery request: %w", err)
+ }
+
+ req.Header.Set("Content-Type", "application/json")
+
+ // Add auth headers if available
+ authProvider := c.GetAuthProvider()
+ if authProvider != nil {
+ authHeaders, err := authProvider.GetAuthHeaders(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get auth headers: %w", err)
+ }
+ for k, v := range authHeaders.Discovery {
+ req.Header.Set(k, v)
+ }
+ }
+
+ // Make request
+ resp, err := c.HTTPClient().Do(req)
+ if err != nil {
+ return nil, fmt.Errorf("discovery request failed: %w", err)
+ }
+ defer resp.Body.Close()
+
+ // Read response body
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read response body: %w", err)
+ }
+
+ // Check for error response
+ if resp.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("facilitator listDiscoveryResources failed (%d): %s", resp.StatusCode, string(body))
+ }
+
+ // Parse response
+ var result DiscoveryResourcesResponse
+ if err := json.Unmarshal(body, &result); err != nil {
+ return nil, fmt.Errorf("failed to decode discovery response: %w", err)
+ }
+
+ return &result, nil
+}
+
+// buildDiscoveryURL constructs the full /discovery/resources URL with query parameters.
+func (c *BazaarFacilitatorClient) buildDiscoveryURL(params *ListDiscoveryResourcesParams) (string, error) {
+ base := c.URL() + "/discovery/resources"
+
+ if params == nil {
+ return base, nil
+ }
+
+ u, err := url.Parse(base)
+ if err != nil {
+ return "", err
+ }
+
+ q := u.Query()
+ if params.Type != "" {
+ q.Set("type", params.Type)
+ }
+ if params.Limit > 0 {
+ q.Set("limit", strconv.Itoa(params.Limit))
+ }
+ if params.Offset > 0 {
+ q.Set("offset", strconv.Itoa(params.Offset))
+ }
+
+ u.RawQuery = q.Encode()
+ return u.String(), nil
+}
diff --git a/go/extensions/bazaar/facilitator_client_test.go b/go/extensions/bazaar/facilitator_client_test.go
new file mode 100644
index 0000000000..c23ec4308b
--- /dev/null
+++ b/go/extensions/bazaar/facilitator_client_test.go
@@ -0,0 +1,491 @@
+package bazaar
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ x402http "github.com/coinbase/x402/go/http"
+)
+
+// testAuthProvider is a test helper that returns fixed auth headers.
+type testAuthProvider struct {
+ headers x402http.AuthHeaders
+ err error
+}
+
+func (p *testAuthProvider) GetAuthHeaders(_ context.Context) (x402http.AuthHeaders, error) {
+ return p.headers, p.err
+}
+
+func TestWithBazaar(t *testing.T) {
+ client := x402http.NewHTTPFacilitatorClient(nil)
+ bazaarClient := WithBazaar(client)
+
+ if bazaarClient == nil {
+ t.Fatal("Expected non-nil bazaar client")
+ }
+ if bazaarClient.HTTPFacilitatorClient != client {
+ t.Error("Expected embedded client to match original")
+ }
+}
+
+func TestWithBazaar_PreservesOriginalMethods(t *testing.T) {
+ client := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: "https://example.com/facilitator",
+ })
+ bazaarClient := WithBazaar(client)
+
+ // Verify the wrapped client preserves the URL
+ if bazaarClient.URL() != "https://example.com/facilitator" {
+ t.Errorf("Expected URL https://example.com/facilitator, got %s", bazaarClient.URL())
+ }
+}
+
+func TestListDiscoveryResources_Success(t *testing.T) {
+ ctx := context.Background()
+
+ expectedResponse := DiscoveryResourcesResponse{
+ X402Version: 2,
+ Items: []DiscoveryResource{
+ {
+ Resource: "https://api.example.com/data",
+ Type: "http",
+ X402Version: 2,
+ Accepts: []json.RawMessage{json.RawMessage(`{"scheme":"exact","network":"eip155:1"}`)},
+ LastUpdated: "2026-03-01T00:00:00Z",
+ Metadata: map[string]any{"category": "data"},
+ },
+ },
+ Pagination: Pagination{
+ Limit: 20,
+ Offset: 0,
+ Total: 1,
+ },
+ }
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/discovery/resources" {
+ t.Errorf("Expected path /discovery/resources, got %s", r.URL.Path)
+ }
+ if r.Method != "GET" {
+ t.Errorf("Expected GET method, got %s", r.Method)
+ }
+ if r.Header.Get("Content-Type") != "application/json" {
+ t.Errorf("Expected Content-Type application/json, got %s", r.Header.Get("Content-Type"))
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(expectedResponse)
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ result, err := client.ListDiscoveryResources(ctx, nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ if result.X402Version != 2 {
+ t.Errorf("Expected x402Version 2, got %d", result.X402Version)
+ }
+ if len(result.Items) != 1 {
+ t.Fatalf("Expected 1 item, got %d", len(result.Items))
+ }
+ if result.Items[0].Resource != "https://api.example.com/data" {
+ t.Errorf("Expected resource URL https://api.example.com/data, got %s", result.Items[0].Resource)
+ }
+ if result.Items[0].Type != "http" {
+ t.Errorf("Expected type http, got %s", result.Items[0].Type)
+ }
+ if result.Items[0].X402Version != 2 {
+ t.Errorf("Expected item x402Version 2, got %d", result.Items[0].X402Version)
+ }
+ if result.Items[0].LastUpdated != "2026-03-01T00:00:00Z" {
+ t.Errorf("Expected lastUpdated 2026-03-01T00:00:00Z, got %s", result.Items[0].LastUpdated)
+ }
+ if result.Items[0].Metadata["category"] != "data" {
+ t.Errorf("Expected metadata category=data, got %v", result.Items[0].Metadata["category"])
+ }
+ if result.Pagination.Limit != 20 {
+ t.Errorf("Expected pagination limit 20, got %d", result.Pagination.Limit)
+ }
+ if result.Pagination.Total != 1 {
+ t.Errorf("Expected pagination total 1, got %d", result.Pagination.Total)
+ }
+}
+
+func TestListDiscoveryResources_WithParams(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ query := r.URL.Query()
+
+ if query.Get("type") != "http" {
+ t.Errorf("Expected type=http, got %s", query.Get("type"))
+ }
+ if query.Get("limit") != "10" {
+ t.Errorf("Expected limit=10, got %s", query.Get("limit"))
+ }
+ if query.Get("offset") != "5" {
+ t.Errorf("Expected offset=5, got %s", query.Get("offset"))
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(DiscoveryResourcesResponse{
+ X402Version: 2,
+ Items: []DiscoveryResource{},
+ Pagination: Pagination{Limit: 10, Offset: 5, Total: 0},
+ })
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ result, err := client.ListDiscoveryResources(ctx, &ListDiscoveryResourcesParams{
+ Type: "http",
+ Limit: 10,
+ Offset: 5,
+ })
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+ if result.Pagination.Limit != 10 {
+ t.Errorf("Expected pagination limit 10, got %d", result.Pagination.Limit)
+ }
+ if result.Pagination.Offset != 5 {
+ t.Errorf("Expected pagination offset 5, got %d", result.Pagination.Offset)
+ }
+}
+
+func TestListDiscoveryResources_NoParams(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.RawQuery != "" {
+ t.Errorf("Expected no query params, got %s", r.URL.RawQuery)
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(DiscoveryResourcesResponse{
+ X402Version: 2,
+ Items: []DiscoveryResource{},
+ Pagination: Pagination{Limit: 20, Offset: 0, Total: 0},
+ })
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+}
+
+func TestListDiscoveryResources_PartialParams(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ query := r.URL.Query()
+
+ if query.Get("type") != "mcp" {
+ t.Errorf("Expected type=mcp, got %s", query.Get("type"))
+ }
+ // limit and offset should not be set when zero
+ if query.Has("limit") {
+ t.Errorf("Expected no limit param, got %s", query.Get("limit"))
+ }
+ if query.Has("offset") {
+ t.Errorf("Expected no offset param, got %s", query.Get("offset"))
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(DiscoveryResourcesResponse{
+ X402Version: 2,
+ Items: []DiscoveryResource{},
+ Pagination: Pagination{Limit: 20, Offset: 0, Total: 0},
+ })
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, &ListDiscoveryResourcesParams{
+ Type: "mcp",
+ })
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+}
+
+func TestListDiscoveryResources_ErrorResponse(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.WriteHeader(http.StatusInternalServerError)
+ _, _ = w.Write([]byte("internal server error"))
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err == nil {
+ t.Fatal("Expected error for 500 response")
+ }
+
+ expected := "facilitator listDiscoveryResources failed (500): internal server error"
+ if err.Error() != expected {
+ t.Errorf("Expected error message %q, got %q", expected, err.Error())
+ }
+}
+
+func TestListDiscoveryResources_NotFound(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.WriteHeader(http.StatusNotFound)
+ _, _ = w.Write([]byte("not found"))
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err == nil {
+ t.Fatal("Expected error for 404 response")
+ }
+
+ expected := "facilitator listDiscoveryResources failed (404): not found"
+ if err.Error() != expected {
+ t.Errorf("Expected error message %q, got %q", expected, err.Error())
+ }
+}
+
+func TestListDiscoveryResources_InvalidJSON(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ _, _ = w.Write([]byte(`{"x402Version":`))
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err == nil {
+ t.Fatal("Expected error for invalid JSON response")
+ }
+}
+
+func TestListDiscoveryResources_WithAuthHeaders(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ auth := r.Header.Get("Authorization")
+ if auth != "Bearer test-token" {
+ t.Errorf("Expected Authorization header 'Bearer test-token', got %q", auth)
+ }
+
+ apiKey := r.Header.Get("X-Api-Key")
+ if apiKey != "my-key" {
+ t.Errorf("Expected X-Api-Key header 'my-key', got %q", apiKey)
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(DiscoveryResourcesResponse{
+ X402Version: 2,
+ Items: []DiscoveryResource{},
+ Pagination: Pagination{Limit: 20, Offset: 0, Total: 0},
+ })
+ }))
+ defer server.Close()
+
+ authProvider := &testAuthProvider{
+ headers: x402http.AuthHeaders{
+ Discovery: map[string]string{
+ "Authorization": "Bearer test-token",
+ "X-Api-Key": "my-key",
+ },
+ },
+ }
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ AuthProvider: authProvider,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+}
+
+func TestListDiscoveryResources_NoAuthProvider(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Should not have Authorization header when no auth provider
+ if r.Header.Get("Authorization") != "" {
+ t.Errorf("Expected no Authorization header, got %q", r.Header.Get("Authorization"))
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(DiscoveryResourcesResponse{
+ X402Version: 2,
+ Items: []DiscoveryResource{},
+ Pagination: Pagination{Limit: 20, Offset: 0, Total: 0},
+ })
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+}
+
+func TestListDiscoveryResources_AuthProviderError(t *testing.T) {
+ ctx := context.Background()
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ t.Error("Request should not have been made when auth provider fails")
+ w.WriteHeader(http.StatusOK)
+ }))
+ defer server.Close()
+
+ authProvider := &testAuthProvider{
+ err: http.ErrAbortHandler,
+ }
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ AuthProvider: authProvider,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err == nil {
+ t.Fatal("Expected error when auth provider fails")
+ }
+}
+
+func TestListDiscoveryResources_MultipleItems(t *testing.T) {
+ ctx := context.Background()
+
+ expectedResponse := DiscoveryResourcesResponse{
+ X402Version: 2,
+ Items: []DiscoveryResource{
+ {
+ Resource: "https://api.example.com/endpoint1",
+ Type: "http",
+ X402Version: 2,
+ Accepts: []json.RawMessage{json.RawMessage(`{"scheme":"exact"}`)},
+ LastUpdated: "2026-03-01T00:00:00Z",
+ },
+ {
+ Resource: "https://api.example.com/endpoint2",
+ Type: "http",
+ X402Version: 1,
+ Accepts: []json.RawMessage{json.RawMessage(`{"scheme":"exact"}`)},
+ LastUpdated: "2026-02-28T00:00:00Z",
+ },
+ {
+ Resource: "mcp://tools/search",
+ Type: "mcp",
+ X402Version: 2,
+ Accepts: []json.RawMessage{json.RawMessage(`{"scheme":"exact"}`)},
+ LastUpdated: "2026-03-01T12:00:00Z",
+ },
+ },
+ Pagination: Pagination{
+ Limit: 20,
+ Offset: 0,
+ Total: 3,
+ },
+ }
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(expectedResponse)
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ result, err := client.ListDiscoveryResources(ctx, nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ if len(result.Items) != 3 {
+ t.Fatalf("Expected 3 items, got %d", len(result.Items))
+ }
+ if result.Items[0].Resource != "https://api.example.com/endpoint1" {
+ t.Errorf("Expected first resource https://api.example.com/endpoint1, got %s", result.Items[0].Resource)
+ }
+ if result.Items[2].Type != "mcp" {
+ t.Errorf("Expected third resource type mcp, got %s", result.Items[2].Type)
+ }
+ if result.Pagination.Total != 3 {
+ t.Errorf("Expected total 3, got %d", result.Pagination.Total)
+ }
+}
+
+func TestListDiscoveryResources_ContextCancellation(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel() // Cancel immediately
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+ t.Error("Request should not have been made with cancelled context")
+ w.WriteHeader(http.StatusOK)
+ }))
+ defer server.Close()
+
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: server.URL,
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err == nil {
+ t.Fatal("Expected error with cancelled context")
+ }
+}
+
+func TestListDiscoveryResources_ConnectionError(t *testing.T) {
+ ctx := context.Background()
+
+ // Use a URL that will fail to connect
+ client := WithBazaar(x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: "http://127.0.0.1:1", // Port 1 should fail
+ }))
+
+ _, err := client.ListDiscoveryResources(ctx, nil)
+ if err == nil {
+ t.Fatal("Expected error for connection failure")
+ }
+}
diff --git a/go/extensions/bazaar/facilitator_test.go b/go/extensions/bazaar/facilitator_test.go
new file mode 100644
index 0000000000..d7a8155685
--- /dev/null
+++ b/go/extensions/bazaar/facilitator_test.go
@@ -0,0 +1,108 @@
+package bazaar
+
+// Internal tests for unexported facilitator helpers.
+// Uses package bazaar (not bazaar_test) to access unexported functions.
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestIsValidRouteTemplate(t *testing.T) {
+ t.Run("returns false for empty input", func(t *testing.T) {
+ assert.False(t, isValidRouteTemplate(""))
+ })
+
+ t.Run("returns false for paths not starting with /", func(t *testing.T) {
+ assert.False(t, isValidRouteTemplate("users/123"))
+ assert.False(t, isValidRouteTemplate("relative/path"))
+ assert.False(t, isValidRouteTemplate("no-slash"))
+ })
+
+ t.Run("returns false for paths containing ..", func(t *testing.T) {
+ assert.False(t, isValidRouteTemplate("/users/../admin"))
+ assert.False(t, isValidRouteTemplate("/../etc/passwd"))
+ assert.False(t, isValidRouteTemplate("/users/.."))
+ })
+
+ t.Run("returns false for paths containing ://", func(t *testing.T) {
+ assert.False(t, isValidRouteTemplate("http://evil.com/path"))
+ assert.False(t, isValidRouteTemplate("/users/http://evil"))
+ assert.False(t, isValidRouteTemplate("javascript://foo"))
+ })
+
+ t.Run("returns true for valid paths", func(t *testing.T) {
+ assert.True(t, isValidRouteTemplate("/users/:userId"))
+ assert.True(t, isValidRouteTemplate("/api/v1/items"))
+ assert.True(t, isValidRouteTemplate("/products/:productId/reviews/:reviewId"))
+ assert.True(t, isValidRouteTemplate("/weather/:country/:city"))
+ })
+
+ t.Run("returns false for paths with spaces or invalid characters", func(t *testing.T) {
+ assert.False(t, isValidRouteTemplate("/users/ bad"))
+ assert.False(t, isValidRouteTemplate("/path with spaces"))
+ })
+
+ t.Run("edge case: /users/..hidden is rejected (contains ..)", func(t *testing.T) {
+ assert.False(t, isValidRouteTemplate("/users/..hidden"))
+ })
+
+ t.Run("rejects percent-encoded traversal sequences", func(t *testing.T) {
+ assert.False(t, isValidRouteTemplate("/users/%2e%2e/admin"))
+ assert.False(t, isValidRouteTemplate("/users/%2E%2E/admin"))
+ })
+}
+
+func TestExtractPathParams(t *testing.T) {
+ t.Run("returns empty map when URL path has fewer segments than pattern (bracket)", func(t *testing.T) {
+ result := extractPathParams("/users/[userId]", "/api/other", true)
+ assert.Equal(t, map[string]string{}, result)
+ })
+
+ t.Run("extracts single param from matching path (bracket)", func(t *testing.T) {
+ result := extractPathParams("/users/[userId]", "/users/123", true)
+ assert.Equal(t, map[string]string{"userId": "123"}, result)
+ })
+
+ t.Run("extracts multiple params from matching path (bracket)", func(t *testing.T) {
+ result := extractPathParams("/users/[userId]/posts/[postId]", "/users/42/posts/7", true)
+ assert.Equal(t, map[string]string{"userId": "42", "postId": "7"}, result)
+ })
+
+ t.Run("extracts single param from matching path (colon)", func(t *testing.T) {
+ result := extractPathParams("/users/:userId", "/users/123", false)
+ assert.Equal(t, map[string]string{"userId": "123"}, result)
+ })
+
+ t.Run("extracts multiple params from matching path (colon)", func(t *testing.T) {
+ result := extractPathParams("/users/:userId/posts/:postId", "/users/42/posts/7", false)
+ assert.Equal(t, map[string]string{"userId": "42", "postId": "7"}, result)
+ })
+
+ t.Run("returns empty map when URL path mismatches (colon)", func(t *testing.T) {
+ result := extractPathParams("/users/:userId", "/api/other", false)
+ assert.Equal(t, map[string]string{}, result)
+ })
+}
+
+func TestNormalizeResourceURL(t *testing.T) {
+ t.Run("uses routeTemplate as canonical path when present", func(t *testing.T) {
+ result := normalizeResourceURL("https://api.example.com/users/123?foo=bar#frag", "/users/:userId")
+ assert.Equal(t, "https://api.example.com/users/:userId", result)
+ })
+
+ t.Run("strips query params and fragment when no routeTemplate", func(t *testing.T) {
+ result := normalizeResourceURL("https://api.example.com/search?q=test#section", "")
+ assert.Equal(t, "https://api.example.com/search", result)
+ })
+
+ t.Run("returns original URL on parse error with routeTemplate", func(t *testing.T) {
+ // url.Parse rarely fails but we exercise the fallback branch.
+ result := normalizeResourceURL("://invalid", "/route")
+ // Fallback: stripQueryParams is called, which may also fail on invalid URL,
+ // returning the original.
+ assert.NotEmpty(t, result)
+ })
+
+}
diff --git a/go/extensions/bazaar/resource_service.go b/go/extensions/bazaar/resource_service.go
index 9a70d7d6ed..f56089c051 100644
--- a/go/extensions/bazaar/resource_service.go
+++ b/go/extensions/bazaar/resource_service.go
@@ -53,13 +53,25 @@ import (
// Example: map[string]interface{}{"success": true, "id": "123"},
// },
// )
+//
+// DeclareDiscoveryExtensionOpts holds optional parameters for DeclareDiscoveryExtension.
+type DeclareDiscoveryExtensionOpts struct {
+ PathParamsSchema types.JSONSchema
+}
+
func DeclareDiscoveryExtension(
method interface{}, // QueryParamMethods or BodyMethods
input interface{},
inputSchema types.JSONSchema,
bodyType types.BodyType,
output *types.OutputConfig,
+ opts ...DeclareDiscoveryExtensionOpts,
) (types.DiscoveryExtension, error) {
+ var pathParamsSchema types.JSONSchema
+ if len(opts) > 0 {
+ pathParamsSchema = opts[0].PathParamsSchema
+ }
+
// Convert method to string
var methodStr string
switch m := method.(type) {
@@ -74,12 +86,12 @@ func DeclareDiscoveryExtension(
}
if types.IsQueryMethod(methodStr) {
- return createQueryDiscoveryExtension(types.QueryParamMethods(methodStr), input, inputSchema, output)
+ return createQueryDiscoveryExtension(types.QueryParamMethods(methodStr), input, inputSchema, pathParamsSchema, output)
} else if types.IsBodyMethod(methodStr) {
if bodyType == "" {
bodyType = types.BodyTypeJSON
}
- return createBodyDiscoveryExtension(types.BodyMethods(methodStr), input, inputSchema, bodyType, output)
+ return createBodyDiscoveryExtension(types.BodyMethods(methodStr), input, inputSchema, pathParamsSchema, bodyType, output)
}
return types.DiscoveryExtension{}, fmt.Errorf("unsupported HTTP method: %s", methodStr)
@@ -90,6 +102,7 @@ func createQueryDiscoveryExtension(
method types.QueryParamMethods,
input interface{},
inputSchema types.JSONSchema,
+ pathParamsSchema types.JSONSchema,
output *types.OutputConfig,
) (types.DiscoveryExtension, error) {
// Convert input to map if provided
@@ -135,7 +148,10 @@ func createQueryDiscoveryExtension(
"enum": []string{string(method)},
},
},
- "required": []string{"type", "method"},
+ "required": []string{"type", "method"},
+ // pathParams and method are not declared here at schema build time —
+ // the server extension's EnrichDeclaration adds them to both info and schema
+ // atomically at request time, keeping data and schema consistent.
"additionalProperties": false,
},
}
@@ -153,6 +169,16 @@ func createQueryDiscoveryExtension(
}
}
+ if len(pathParamsSchema) > 0 {
+ inputProps := schemaProperties["input"].(map[string]interface{})
+ props := inputProps["properties"].(map[string]interface{})
+ pp := map[string]interface{}{"type": "object"}
+ for k, v := range pathParamsSchema {
+ pp[k] = v
+ }
+ props["pathParams"] = pp
+ }
+
// Add output schema if provided
if output != nil && output.Example != nil {
outputSchema := map[string]interface{}{
@@ -199,6 +225,7 @@ func createBodyDiscoveryExtension(
method types.BodyMethods,
input interface{},
inputSchema types.JSONSchema,
+ pathParamsSchema types.JSONSchema,
bodyType types.BodyType,
output *types.OutputConfig,
) (types.DiscoveryExtension, error) {
@@ -243,11 +270,24 @@ func createBodyDiscoveryExtension(
},
"body": inputSchema,
},
- "required": []string{"type", "method", "bodyType", "body"},
+ "required": []string{"type", "method", "bodyType", "body"},
+ // pathParams and method are not declared here at schema build time —
+ // the server extension's EnrichDeclaration adds them to both info and schema
+ // atomically at request time, keeping data and schema consistent.
"additionalProperties": false,
},
}
+ if len(pathParamsSchema) > 0 {
+ inputProps := schemaProperties["input"].(map[string]interface{})
+ props := inputProps["properties"].(map[string]interface{})
+ pp := map[string]interface{}{"type": "object"}
+ for k, v := range pathParamsSchema {
+ pp[k] = v
+ }
+ props["pathParams"] = pp
+ }
+
// Add output schema if provided
if output != nil && output.Example != nil {
outputSchema := map[string]interface{}{
diff --git a/go/extensions/bazaar/server.go b/go/extensions/bazaar/server.go
index 2a24a9c8b1..863b808b18 100644
--- a/go/extensions/bazaar/server.go
+++ b/go/extensions/bazaar/server.go
@@ -1,16 +1,131 @@
package bazaar
import (
+ "fmt"
+ "regexp"
+ "strings"
+ "sync"
+
"github.com/coinbase/x402/go/extensions/types"
"github.com/coinbase/x402/go/http"
)
+// bracketParamRegex matches [paramName] route segments (Next.js style).
+var bracketParamRegex = regexp.MustCompile(`\[([^\]]+)\]`)
+
+// colonParamRegex is a package-local alias for the shared regex in extensions/types.
+var colonParamRegex = types.ColonParamRegex
+
+// patternCache caches compiled capture regexes and param names per route pattern
+// to avoid recompilation on every request.
+type patternCacheEntry struct {
+ regex *regexp.Regexp
+ paramNames []string
+}
+
+var patternCache sync.Map // map[string]*patternCacheEntry
+
+// normalizeWildcardPattern converts wildcard * segments to :var1, :var2, etc.
+func normalizeWildcardPattern(pattern string) string {
+ if !strings.Contains(pattern, "*") {
+ return pattern
+ }
+ segments := strings.Split(pattern, "/")
+ counter := 0
+ for i, seg := range segments {
+ if seg == "*" {
+ counter++
+ segments[i] = fmt.Sprintf(":var%d", counter)
+ }
+ }
+ return strings.Join(segments, "/")
+}
+
type bazaarResourceServerExtension struct{}
func (e *bazaarResourceServerExtension) Key() string {
return types.BAZAAR.Key()
}
+// extractDynamicRouteInfo converts a parameterized route pattern into a :param template
+// and extracts concrete param values from the URL path in a single call.
+// Supports both [param] (Next.js) and :param (Express) syntax. The output routeTemplate
+// always uses :param syntax regardless of input format.
+// Returns an empty routeTemplate and nil pathParams when routePattern has no param segments.
+func extractDynamicRouteInfo(routePattern, urlPath string) (routeTemplate string, pathParams map[string]string) {
+ hasBracket := bracketParamRegex.MatchString(routePattern)
+ hasColon := colonParamRegex.MatchString(routePattern)
+ if !hasBracket && !hasColon {
+ return "", nil
+ }
+ // When both [param] and :param are present, normalize brackets to colons first
+ // so all params are extracted uniformly.
+ normalizedPattern := routePattern
+ if hasBracket {
+ normalizedPattern = bracketParamRegex.ReplaceAllString(routePattern, ":$1")
+ }
+ routeTemplate = normalizedPattern
+ pathParams = extractPathParams(normalizedPattern, urlPath, false)
+ return
+}
+
+// getOrCompilePattern returns a cached capture regex and param names for the given
+// route pattern, compiling and caching on first access.
+func getOrCompilePattern(routePattern string, isBracket bool) *patternCacheEntry {
+ if cached, ok := patternCache.Load(routePattern); ok {
+ return cached.(*patternCacheEntry)
+ }
+
+ paramRegex := colonParamRegex
+ if isBracket {
+ paramRegex = bracketParamRegex
+ }
+ matches := paramRegex.FindAllStringSubmatch(routePattern, -1)
+ paramNames := make([]string, 0, len(matches))
+ for _, m := range matches {
+ paramNames = append(paramNames, m[1])
+ }
+
+ parts := paramRegex.Split(routePattern, -1)
+ regexParts := make([]string, 0, len(parts)+len(paramNames))
+ for i, part := range parts {
+ regexParts = append(regexParts, regexp.QuoteMeta(part))
+ if i < len(paramNames) {
+ regexParts = append(regexParts, "([^/]+)")
+ }
+ }
+ captureRegex, err := regexp.Compile("^" + strings.Join(regexParts, "") + "$")
+ if err != nil {
+ return &patternCacheEntry{paramNames: paramNames}
+ }
+
+ entry := &patternCacheEntry{regex: captureRegex, paramNames: paramNames}
+ patternCache.Store(routePattern, entry)
+ return entry
+}
+
+// extractPathParams extracts concrete path parameter values by matching a URL path
+// against a route pattern containing [paramName] or :paramName segments.
+func extractPathParams(routePattern, urlPath string, isBracket bool) map[string]string {
+ entry := getOrCompilePattern(routePattern, isBracket)
+ if entry.regex == nil {
+ return map[string]string{}
+ }
+
+ submatches := entry.regex.FindStringSubmatch(urlPath)
+ if submatches == nil {
+ return map[string]string{}
+ }
+
+ result := make(map[string]string, len(entry.paramNames))
+ for i, name := range entry.paramNames {
+ if i+1 < len(submatches) {
+ result[name] = submatches[i+1]
+ }
+ }
+ return result
+}
+
func (e *bazaarResourceServerExtension) EnrichDeclaration(
declaration interface{},
transportContext interface{},
@@ -52,6 +167,46 @@ func (e *bazaarResourceServerExtension) EnrichDeclaration(
}
}
+ // Dynamic routes: translate [param]/:param → :param for the routeTemplate catalog key;
+ // pathParams carries runtime values (distinct from pathParamsSchema in the declaration).
+ // Wildcard * segments are auto-converted to :var1, :var2, etc. for catalog normalization.
+ var urlPath string
+ if httpContext.Adapter != nil {
+ urlPath = httpContext.Adapter.GetPath()
+ }
+ normalizedPattern := normalizeWildcardPattern(httpContext.RoutePattern)
+ routeTemplate, pathParams := extractDynamicRouteInfo(normalizedPattern, urlPath)
+ if routeTemplate != "" {
+ // Widen map[string]string to map[string]interface{} for the wire-level PathParams field
+ pathParamsIface := make(map[string]interface{}, len(pathParams))
+ for k, v := range pathParams {
+ pathParamsIface[k] = v
+ }
+
+ // Update input with pathParams
+ if queryInput, ok := extension.Info.Input.(types.QueryInput); ok {
+ queryInput.PathParams = pathParamsIface
+ extension.Info.Input = queryInput
+ } else if bodyInput, ok := extension.Info.Input.(types.BodyInput); ok {
+ bodyInput.PathParams = pathParamsIface
+ extension.Info.Input = bodyInput
+ }
+
+ // Ensure pathParams is allowed in the schema (additionalProperties: false would reject it)
+ if schemaProps, ok := extension.Schema["properties"].(map[string]interface{}); ok {
+ if inputSchema, ok := schemaProps["input"].(map[string]interface{}); ok {
+ if props, ok := inputSchema["properties"].(map[string]interface{}); ok {
+ if _, hasPathParams := props["pathParams"]; !hasPathParams {
+ props["pathParams"] = map[string]interface{}{"type": "object"}
+ }
+ }
+ }
+ }
+
+ extension.RouteTemplate = routeTemplate
+ return extension
+ }
+
return extension
}
diff --git a/go/extensions/erc20approvalgassponsor/resolve_signer_test.go b/go/extensions/erc20approvalgassponsor/resolve_signer_test.go
new file mode 100644
index 0000000000..f455a59db5
--- /dev/null
+++ b/go/extensions/erc20approvalgassponsor/resolve_signer_test.go
@@ -0,0 +1,68 @@
+package erc20approvalgassponsor
+
+import (
+ "context"
+ "math/big"
+ "testing"
+
+ evm "github.com/coinbase/x402/go/mechanisms/evm"
+)
+
+type mockApprovalSigner struct {
+ id string
+}
+
+func (m *mockApprovalSigner) GetAddresses() []string {
+ return []string{"0x0000000000000000000000000000000000000001"}
+}
+func (m *mockApprovalSigner) ReadContract(_ context.Context, _ string, _ []byte, _ string, _ ...interface{}) (interface{}, error) {
+ return big.NewInt(0), nil
+}
+func (m *mockApprovalSigner) VerifyTypedData(_ context.Context, _ string, _ evm.TypedDataDomain, _ map[string][]evm.TypedDataField, _ string, _ map[string]interface{}, _ []byte) (bool, error) {
+ return true, nil
+}
+func (m *mockApprovalSigner) WriteContract(_ context.Context, _ string, _ []byte, _ string, _ ...interface{}) (string, error) {
+ return "0xtx", nil
+}
+func (m *mockApprovalSigner) SendTransaction(_ context.Context, _ string, _ []byte) (string, error) {
+ return "0xtx", nil
+}
+func (m *mockApprovalSigner) WaitForTransactionReceipt(_ context.Context, _ string) (*evm.TransactionReceipt, error) {
+ return &evm.TransactionReceipt{Status: evm.TxStatusSuccess}, nil
+}
+func (m *mockApprovalSigner) GetBalance(_ context.Context, _ string, _ string) (*big.Int, error) {
+ return big.NewInt(0), nil
+}
+func (m *mockApprovalSigner) GetChainID(_ context.Context) (*big.Int, error) {
+ return big.NewInt(8453), nil
+}
+func (m *mockApprovalSigner) GetCode(_ context.Context, _ string) ([]byte, error) {
+ return []byte{}, nil
+}
+func (m *mockApprovalSigner) SendTransactions(_ context.Context, _ []TransactionRequest) ([]string, error) {
+ return []string{"0xtx"}, nil
+}
+
+func TestResolveSigner_UsesNetworkResolverFirst(t *testing.T) {
+ defaultSigner := &mockApprovalSigner{id: "default"}
+ baseSigner := &mockApprovalSigner{id: "base"}
+ ext := &Erc20ApprovalFacilitatorExtension{
+ Signer: baseSigner,
+ SignerForNetwork: func(network string) Erc20ApprovalGasSponsoringSigner {
+ if network == "eip155:8453" {
+ return defaultSigner
+ }
+ return nil
+ },
+ }
+
+ resolved := ext.ResolveSigner("eip155:8453")
+ if resolved == nil || resolved.(*mockApprovalSigner).id != "default" {
+ t.Fatalf("expected network-specific signer, got %#v", resolved)
+ }
+
+ resolved = ext.ResolveSigner("eip155:1")
+ if resolved == nil || resolved.(*mockApprovalSigner).id != "base" {
+ t.Fatalf("expected fallback base signer, got %#v", resolved)
+ }
+}
diff --git a/go/extensions/erc20approvalgassponsor/types.go b/go/extensions/erc20approvalgassponsor/types.go
index b60a4e32db..a62d049f51 100644
--- a/go/extensions/erc20approvalgassponsor/types.go
+++ b/go/extensions/erc20approvalgassponsor/types.go
@@ -50,19 +50,65 @@ type Extension struct {
Schema map[string]interface{} `json:"schema"`
}
-// Erc20ApprovalGasSponsoringSigner extends FacilitatorEvmSigner with raw transaction broadcasting.
+// WriteContractCall encapsulates arguments for a WriteContract call,
+// used by SendTransactions to describe an unsigned contract call operation.
+type WriteContractCall struct {
+ Address string
+ ABI []byte
+ Function string
+ Args []interface{}
+}
+
+// TransactionRequest represents a single transaction to be executed by the signer.
+// Either Serialized (pre-signed raw transaction) or Call (unsigned intent) must be set.
+type TransactionRequest struct {
+ // Serialized is a pre-signed raw transaction hex (0x-prefixed).
+ // When non-empty, the signer broadcasts it as-is via sendRawTransaction.
+ Serialized string
+ // Call is an unsigned contract write for the signer to sign and execute.
+ // Used when Serialized is empty.
+ Call *WriteContractCall
+}
+
+// Erc20ApprovalGasSponsoringSigner extends FacilitatorEvmSigner with multi-transaction execution.
+// The signer owns the execution strategy (sequential, batched, or atomic bundling via
+// Flashbots, multicall, or smart account batching).
type Erc20ApprovalGasSponsoringSigner interface {
evm.FacilitatorEvmSigner
- SendRawTransaction(ctx context.Context, signedTx string) (string, error)
+ SendTransactions(ctx context.Context, transactions []TransactionRequest) ([]string, error)
+}
+
+// Erc20ApprovalGasSponsoringSimulator is an optional extension of Erc20ApprovalGasSponsoringSigner with multi-transaction simulation.
+// The signer owns the simulation strategy.
+type Erc20ApprovalGasSponsoringSimulator interface {
+ Erc20ApprovalGasSponsoringSigner
+ SimulateTransactions(ctx context.Context, transactions []TransactionRequest) (bool, error)
}
// Erc20ApprovalFacilitatorExtension carries the signer; registered with the facilitator.
// It implements x402.FacilitatorExtension so it can be registered and retrieved via FacilitatorContext.
type Erc20ApprovalFacilitatorExtension struct {
Signer Erc20ApprovalGasSponsoringSigner
+ // Optional network-aware signer resolver. When provided, this takes precedence
+ // over Signer and allows different settlement signers per network.
+ SignerForNetwork func(network string) Erc20ApprovalGasSponsoringSigner
}
// Key returns the extension identifier.
func (e *Erc20ApprovalFacilitatorExtension) Key() string {
return ERC20ApprovalGasSponsoring.Key()
}
+
+// ResolveSigner returns the signer to use for a given network.
+// SignerForNetwork takes precedence when configured.
+func (e *Erc20ApprovalFacilitatorExtension) ResolveSigner(network string) Erc20ApprovalGasSponsoringSigner {
+ if e == nil {
+ return nil
+ }
+ if e.SignerForNetwork != nil {
+ if signer := e.SignerForNetwork(network); signer != nil {
+ return signer
+ }
+ }
+ return e.Signer
+}
diff --git a/go/extensions/types/types.go b/go/extensions/types/types.go
index 11b175c266..d445732067 100644
--- a/go/extensions/types/types.go
+++ b/go/extensions/types/types.go
@@ -10,6 +10,10 @@ import (
// BAZAAR is the extension identifier for the Bazaar discovery extension.
var BAZAAR = x402.NewFacilitatorExtension("bazaar")
+// ColonParamRegex matches :paramName route segments (Express style).
+// Shared across http/server.go and extensions/bazaar/server.go to avoid drift.
+var ColonParamRegex = regexp.MustCompile(`:([a-zA-Z_][a-zA-Z0-9_]*)`)
+
// Extension identifier constant for the Payment Identifier extension
const PAYMENT_IDENTIFIER = "payment-identifier"
@@ -60,6 +64,7 @@ type QueryInput struct {
Type string `json:"type"` // "http"
Method QueryParamMethods `json:"method"`
QueryParams map[string]interface{} `json:"queryParams,omitempty"`
+ PathParams map[string]interface{} `json:"pathParams,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
}
@@ -76,6 +81,7 @@ type BodyInput struct {
BodyType BodyType `json:"bodyType"`
Body interface{} `json:"body"`
QueryParams map[string]interface{} `json:"queryParams,omitempty"`
+ PathParams map[string]interface{} `json:"pathParams,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
}
@@ -145,8 +151,9 @@ type BodyDiscoveryExtension struct {
// DiscoveryExtension is a union type that can be either Query or Body discovery extension
type DiscoveryExtension struct {
- Info DiscoveryInfo `json:"info"`
- Schema JSONSchema `json:"schema"`
+ Info DiscoveryInfo `json:"info"`
+ Schema JSONSchema `json:"schema"`
+ RouteTemplate string `json:"routeTemplate,omitempty"`
}
// DeclareQueryDiscoveryConfig is the configuration for declaring a query discovery extension
diff --git a/go/facilitator.go b/go/facilitator.go
index d4fb4728c0..fdb5014555 100644
--- a/go/facilitator.go
+++ b/go/facilitator.go
@@ -435,7 +435,8 @@ func (f *x402Facilitator) verifyV1(ctx context.Context, payload types.PaymentPay
}
}
- return nil, NewVerifyError(ErrNoFacilitatorForNetwork, "", fmt.Sprintf("no facilitator for scheme %s on network %s", scheme, network))
+ registered := f.registeredV1Summary()
+ return nil, NewVerifyError(ErrNoFacilitatorForNetwork, "", fmt.Sprintf("no facilitator for scheme %q on network %q; registered: %s", scheme, network, registered))
}
// verifyV2 verifies a V2 payment (internal, typed)
@@ -460,7 +461,8 @@ func (f *x402Facilitator) verifyV2(ctx context.Context, payload types.PaymentPay
}
}
- return nil, NewVerifyError(ErrNoFacilitatorForNetwork, "", fmt.Sprintf("no facilitator for scheme %s on network %s", scheme, network))
+ registered := f.registeredV2Summary()
+ return nil, NewVerifyError(ErrNoFacilitatorForNetwork, "", fmt.Sprintf("no facilitator for scheme %q on network %q; registered: %s", scheme, network, registered))
}
// settleV1 settles a V1 payment (internal, typed)
@@ -485,7 +487,8 @@ func (f *x402Facilitator) settleV1(ctx context.Context, payload types.PaymentPay
}
}
- return nil, NewSettleError(ErrNoFacilitatorForNetwork, "", network, "", fmt.Sprintf("no facilitator for scheme %s on network %s", scheme, network))
+ registered := f.registeredV1Summary()
+ return nil, NewSettleError(ErrNoFacilitatorForNetwork, "", network, "", fmt.Sprintf("no facilitator for scheme %q on network %q; registered: %s", scheme, network, registered))
}
// settleV2 settles a V2 payment (internal, typed)
@@ -510,7 +513,38 @@ func (f *x402Facilitator) settleV2(ctx context.Context, payload types.PaymentPay
}
}
- return nil, NewSettleError(ErrNoFacilitatorForNetwork, "", network, "", fmt.Sprintf("no facilitator for scheme %s on network %s", scheme, network))
+ registered := f.registeredV2Summary()
+ return nil, NewSettleError(ErrNoFacilitatorForNetwork, "", network, "", fmt.Sprintf("no facilitator for scheme %q on network %q; registered: %s", scheme, network, registered))
+}
+
+// registeredV1Summary returns a human-readable list of registered V1 scheme/network pairs.
+func (f *x402Facilitator) registeredV1Summary() string {
+ if len(f.schemesV1) == 0 {
+ return "(none)"
+ }
+ var parts []string
+ for _, data := range f.schemesV1 {
+ facilitator := data.facilitator.(SchemeNetworkFacilitatorV1)
+ for network := range data.networks {
+ parts = append(parts, fmt.Sprintf("%s@%s", facilitator.Scheme(), network))
+ }
+ }
+ return strings.Join(parts, ", ")
+}
+
+// registeredV2Summary returns a human-readable list of registered V2 scheme/network pairs.
+func (f *x402Facilitator) registeredV2Summary() string {
+ if len(f.schemes) == 0 {
+ return "(none)"
+ }
+ var parts []string
+ for _, data := range f.schemes {
+ facilitator := data.facilitator.(SchemeNetworkFacilitator)
+ for network := range data.networks {
+ parts = append(parts, fmt.Sprintf("%s@%s", facilitator.Scheme(), network))
+ }
+ }
+ return strings.Join(parts, ", ")
}
// GetSupported returns supported payment kinds
diff --git a/go/facilitator_hooks_test.go b/go/facilitator_hooks_test.go
index 06644dddac..0767a91f52 100644
--- a/go/facilitator_hooks_test.go
+++ b/go/facilitator_hooks_test.go
@@ -23,8 +23,6 @@ func TestFacilitatorBeforeVerifyHook_Abort(t *testing.T) {
})
// Try to verify (should be aborted by hook)
- // Note: Hooks are not fully integrated yet - this test validates hook registration works
- // TODO: Integrate hooks into Verify execution
payload := types.PaymentPayload{X402Version: 2, Payload: map[string]interface{}{}}
requirements := types.PaymentRequirements{Scheme: "exact", Network: "eip155:8453"}
diff --git a/go/go.mod b/go/go.mod
index ee54c97400..7f498071a9 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -15,6 +15,13 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0
)
+require github.com/google/uuid v1.6.0
+
+require (
+ github.com/google/uuid v1.6.0
+ github.com/labstack/echo/v4 v4.15.1
+)
+
require (
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
@@ -45,15 +52,15 @@ require (
github.com/goccy/go-json v0.10.4 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/google/jsonschema-go v0.4.2 // indirect
- github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.16.0 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
+ github.com/labstack/gommon v0.4.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@@ -70,6 +77,8 @@ require (
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
@@ -79,16 +88,16 @@ require (
go.uber.org/ratelimit v0.2.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/arch v0.20.0 // indirect
- golang.org/x/crypto v0.41.0 // indirect
- golang.org/x/mod v0.27.0 // indirect
- golang.org/x/net v0.43.0 // indirect
+ golang.org/x/crypto v0.46.0 // indirect
+ golang.org/x/mod v0.30.0 // indirect
+ golang.org/x/net v0.48.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
- golang.org/x/sync v0.16.0 // indirect
- golang.org/x/sys v0.36.0 // indirect
- golang.org/x/term v0.34.0 // indirect
- golang.org/x/text v0.28.0 // indirect
- golang.org/x/time v0.9.0 // indirect
- golang.org/x/tools v0.36.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/term v0.38.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
+ golang.org/x/time v0.14.0 // indirect
+ golang.org/x/tools v0.39.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go/go.sum b/go/go.sum
index b1a3f1d716..8232f4e582 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -159,15 +159,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/labstack/echo/v4 v4.15.1 h1:S9keusg26gZpjMmPqB5hOEvNKnmd1lNmcHrbbH2lnFs=
+github.com/labstack/echo/v4 v4.15.1/go.mod h1:xmw1clThob0BSVRX1CRQkGQ/vjwcpOMjQZSZa9fKA/c=
+github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
+github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
@@ -266,6 +269,10 @@ github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
+github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
@@ -307,15 +314,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
-golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
+golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
+golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -323,15 +330,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
-golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -342,34 +349,33 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
-golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
-golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
+golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
+golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
-golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
-golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
-golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/go/http/echo/README.md b/go/http/echo/README.md
new file mode 100644
index 0000000000..21cbe52697
--- /dev/null
+++ b/go/http/echo/README.md
@@ -0,0 +1,350 @@
+# x402 Echo Middleware
+
+Echo middleware integration for the x402 Payment Protocol. This package provides middleware for adding x402 payment requirements to your Echo applications.
+
+## Installation
+
+```bash
+go get github.com/coinbase/x402/go
+```
+
+## Quick Start
+
+```go
+package main
+
+import (
+ x402http "github.com/coinbase/x402/go/http"
+ echomw "github.com/coinbase/x402/go/http/echo"
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ "github.com/labstack/echo/v4"
+)
+
+func main() {
+ e := echo.New()
+
+ facilitator := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: "https://facilitator.x402.org",
+ })
+
+ routes := x402http.RoutesConfig{
+ "GET /protected": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xYourAddress",
+ Price: "$0.10",
+ Network: "eip155:84532",
+ },
+ },
+ Description: "Access to premium content",
+ },
+ }
+
+ e.Use(echomw.PaymentMiddlewareFromConfig(routes,
+ echomw.WithFacilitatorClient(facilitator),
+ echomw.WithScheme("eip155:*", evm.NewExactEvmScheme()),
+ ))
+
+ e.GET("/protected", func(c echo.Context) error {
+ return c.JSON(200, map[string]interface{}{"message": "This content is behind a paywall"})
+ })
+
+ e.Logger.Fatal(e.Start(":8080"))
+}
+```
+
+## Configuration
+
+There are two approaches to configuring the middleware:
+
+### 1. PaymentMiddlewareFromConfig (Functional Options)
+
+Use `PaymentMiddlewareFromConfig` with functional options:
+
+```go
+e.Use(echomw.PaymentMiddlewareFromConfig(routes,
+ echomw.WithFacilitatorClient(facilitator),
+ echomw.WithScheme("eip155:*", evm.NewExactEvmScheme()),
+))
+```
+
+### 2. PaymentMiddlewareFromHTTPServer (Pre-configured Server)
+
+Use `PaymentMiddlewareFromHTTPServer` when you need to configure the server separately (e.g., with lifecycle hooks):
+
+```go
+server := x402.Newx402ResourceServer(
+ x402.WithFacilitatorClient(facilitator),
+).
+ Register("eip155:*", evm.NewExactEvmScheme()).
+ OnAfterSettle(func(ctx x402.SettleResultContext) error {
+ log.Printf("Payment settled: %s", ctx.Result.Transaction)
+ return nil
+ })
+
+httpServer := x402http.Wrappedx402HTTPResourceServer(routes, server)
+
+e.Use(echomw.PaymentMiddlewareFromHTTPServer(httpServer))
+```
+
+### Middleware Options
+
+- `WithFacilitatorClient(client)` - Add a facilitator client
+- `WithScheme(network, server)` - Register a payment scheme
+- `WithPaywallConfig(config)` - Configure paywall UI
+- `WithSyncFacilitatorOnStart(bool)` - Sync with facilitator on startup (default: true)
+- `WithTimeout(duration)` - Set payment operation timeout (default: 30s)
+- `WithErrorHandler(handler)` - Custom error handler
+- `WithSettlementHandler(handler)` - Settlement callback
+
+## Route Configuration
+
+Define which routes require payment:
+
+```go
+routes := x402http.RoutesConfig{
+ "GET /api/data": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xYourAddress",
+ Price: "$0.10",
+ Network: "eip155:84532",
+ },
+ },
+ Description: "API data access",
+ },
+ "POST /api/compute": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xYourAddress",
+ Price: "$1.00",
+ Network: "eip155:8453",
+ },
+ },
+ Description: "Compute operation",
+ },
+}
+```
+
+Routes support wildcards:
+- `"GET /api/premium/*"` - Matches all GET requests under `/api/premium/`
+- `"* /api/data"` - Matches all HTTP methods to `/api/data`
+
+## Paywall Configuration
+
+Configure the paywall UI for browser requests:
+
+```go
+paywallConfig := &x402http.PaywallConfig{
+ AppName: "My API Service",
+ AppLogo: "https://myapp.com/logo.svg",
+ Testnet: true,
+}
+
+e.Use(echomw.PaymentMiddlewareFromConfig(routes,
+ echomw.WithFacilitatorClient(facilitator),
+ echomw.WithScheme("eip155:*", evm.NewExactEvmScheme()),
+ echomw.WithPaywallConfig(paywallConfig),
+))
+```
+
+The paywall includes:
+- EVM wallet support (MetaMask, Coinbase Wallet, etc.)
+- Solana wallet support (Phantom, Solflare, etc.)
+- USDC balance checking and chain switching
+- Onramp integration for mainnet
+
+## Advanced Usage
+
+### Multiple Payment Schemes
+
+Register schemes for different networks:
+
+```go
+import (
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ svm "github.com/coinbase/x402/go/mechanisms/svm/exact/server"
+)
+
+e.Use(echomw.PaymentMiddlewareFromConfig(routes,
+ echomw.WithFacilitatorClient(facilitator),
+ echomw.WithScheme("eip155:*", evm.NewExactEvmScheme()),
+ echomw.WithScheme("solana:*", svm.NewExactSvmScheme()),
+))
+```
+
+### Custom Facilitator Client
+
+Configure with custom authentication:
+
+```go
+facilitator := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: "https://your-facilitator.com",
+ CreateAuthHeaders: func() (*x402http.FacilitatorAuthHeaders, error) {
+ return &x402http.FacilitatorAuthHeaders{
+ Verify: map[string]string{"Authorization": "Bearer verify-token"},
+ Settle: map[string]string{"Authorization": "Bearer settle-token"},
+ }, nil
+ },
+})
+```
+
+### Settlement Handler
+
+Track successful payments:
+
+```go
+e.Use(echomw.PaymentMiddlewareFromConfig(routes,
+ echomw.WithFacilitatorClient(facilitator),
+ echomw.WithScheme("eip155:*", evm.NewExactEvmScheme()),
+ echomw.WithSettlementHandler(func(c echo.Context, settlement *x402.SettleResponse) {
+ log.Printf("Payment settled - Payer: %s, Tx: %s",
+ settlement.Payer,
+ settlement.Transaction,
+ )
+ c.Response().Header().Set("X-Payment-Receipt", settlement.Transaction)
+ }),
+))
+```
+
+### Settlement Overrides (Upto Scheme)
+
+For the upto scheme, route handlers specify the actual settlement amount via `SetSettlementOverrides`:
+
+```go
+e.GET("/api/metered", func(c echo.Context) error {
+ usage := calculateUsage(c)
+ echomw.SetSettlementOverrides(c, &x402.SettlementOverrides{Amount: usage})
+
+ return c.JSON(http.StatusOK, map[string]interface{}{"result": "ok"})
+})
+```
+
+### Error Handler
+
+Custom error handling:
+
+```go
+e.Use(echomw.PaymentMiddlewareFromConfig(routes,
+ echomw.WithFacilitatorClient(facilitator),
+ echomw.WithScheme("eip155:*", evm.NewExactEvmScheme()),
+ echomw.WithErrorHandler(func(c echo.Context, err error) {
+ log.Printf("Payment error: %v", err)
+ c.JSON(http.StatusPaymentRequired, map[string]interface{}{
+ "error": err.Error(),
+ })
+ }),
+))
+```
+
+## Convenience Functions
+
+### X402Payment (Config Struct)
+
+With struct-based configuration:
+
+```go
+e.Use(echomw.X402Payment(echomw.Config{
+ Routes: routes,
+ Facilitator: facilitator,
+ Schemes: []echomw.SchemeConfig{{Network: "eip155:*", Server: evm.NewExactEvmScheme()}},
+ Timeout: 30 * time.Second,
+}))
+```
+
+### SimpleX402Payment
+
+Apply payment requirements to all routes:
+
+```go
+e.Use(echomw.SimpleX402Payment(
+ "0xYourAddress",
+ "$0.10",
+ "eip155:84532",
+ "https://facilitator.x402.org",
+))
+```
+
+## Complete Example
+
+```go
+package main
+
+import (
+ "log"
+ "net/http"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ echomw "github.com/coinbase/x402/go/http/echo"
+ evm "github.com/coinbase/x402/go/mechanisms/evm/exact/server"
+ "github.com/labstack/echo/v4"
+)
+
+func main() {
+ e := echo.New()
+
+ facilitator := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: "https://facilitator.x402.org",
+ })
+
+ routes := x402http.RoutesConfig{
+ "GET /api/data": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xYourAddress",
+ Price: "$0.10",
+ Network: "eip155:84532",
+ },
+ },
+ Description: "Basic data access",
+ },
+ "POST /api/compute": {
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xYourAddress",
+ Price: "$1.00",
+ Network: "eip155:8453",
+ },
+ },
+ Description: "Compute operation",
+ },
+ }
+
+ paywallConfig := &x402http.PaywallConfig{
+ AppName: "My API Service",
+ AppLogo: "/logo.svg",
+ Testnet: true,
+ }
+
+ e.Use(echomw.PaymentMiddlewareFromConfig(routes,
+ echomw.WithFacilitatorClient(facilitator),
+ echomw.WithScheme("eip155:*", evm.NewExactEvmScheme()),
+ echomw.WithPaywallConfig(paywallConfig),
+ echomw.WithTimeout(60*time.Second),
+ echomw.WithSettlementHandler(func(c echo.Context, settlement *x402.SettleResponse) {
+ log.Printf("Payment settled: %s", settlement.Transaction)
+ }),
+ ))
+
+ e.GET("/api/data", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "Protected content"})
+ })
+
+ e.POST("/api/compute", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"result": "Computation complete"})
+ })
+
+ e.GET("/", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"message": "Welcome"})
+ })
+
+ e.Logger.Fatal(e.Start(":8080"))
+}
+```
diff --git a/go/http/echo/builder.go b/go/http/echo/builder.go
new file mode 100644
index 0000000000..35189f55dd
--- /dev/null
+++ b/go/http/echo/builder.go
@@ -0,0 +1,171 @@
+package echo
+
+import (
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ "github.com/labstack/echo/v4"
+)
+
+// Config provides struct-based configuration for x402 payment middleware.
+// This is a cleaner alternative to the variadic options pattern.
+type Config struct {
+ // Routes maps HTTP patterns to payment requirements
+ Routes x402http.RoutesConfig
+
+ // Facilitator is a single facilitator client (most common case)
+ // Use this OR Facilitators (not both)
+ Facilitator x402.FacilitatorClient
+
+ // Facilitators is an array of facilitator clients (for fallback/redundancy)
+ // Use this OR Facilitator (not both)
+ Facilitators []x402.FacilitatorClient
+
+ // Schemes to register with the server
+ Schemes []SchemeConfig
+
+ // PaywallConfig for browser-based payment UI (optional)
+ PaywallConfig *x402http.PaywallConfig
+
+ // SyncFacilitatorOnStart fetches supported kinds from facilitators on startup
+ // Default: true
+ SyncFacilitatorOnStart bool
+
+ // Timeout for payment operations
+ // Default: 30 seconds
+ Timeout time.Duration
+
+ // ErrorHandler for custom error handling (optional)
+ ErrorHandler func(echo.Context, error)
+
+ // SettlementHandler called after successful settlement (optional)
+ SettlementHandler func(echo.Context, *x402.SettleResponse)
+}
+
+// SchemeConfig configures a payment scheme for a network.
+type SchemeConfig struct {
+ Network x402.Network
+ Server x402.SchemeNetworkServer
+}
+
+// X402Payment creates payment middleware using struct-based configuration.
+// This is a cleaner, more readable alternative to PaymentMiddlewareFromConfig with variadic options.
+//
+// Example:
+//
+// e.Use(echomw.X402Payment(echomw.Config{
+// Routes: routes,
+// Facilitator: facilitatorClient,
+// Schemes: []echomw.SchemeConfig{
+// {Network: "eip155:*", Server: evm.NewExactEvmServer()},
+// {Network: "solana:*", Server: svm.NewExactSvmServer()},
+// },
+// SyncFacilitatorOnStart: true,
+// Timeout: 30 * time.Second,
+// }))
+func X402Payment(config Config) echo.MiddlewareFunc {
+ // Set defaults
+ if config.Timeout == 0 {
+ config.Timeout = 30 * time.Second
+ }
+
+ // Default to sync unless explicitly disabled
+ syncOnStart := config.SyncFacilitatorOnStart
+ if !syncOnStart && config.Facilitator == nil && len(config.Facilitators) == 0 {
+ // If no explicit setting and no facilitators, default to false
+ syncOnStart = false
+ } else if config.Facilitator != nil || len(config.Facilitators) > 0 {
+ // If facilitators provided but SyncFacilitatorOnStart not explicitly set, default to true
+ if config.Timeout != 0 {
+ // User set something, so they're configuring - default to true
+ syncOnStart = true
+ }
+ }
+
+ // Normalize facilitators list
+ var facilitators []x402.FacilitatorClient
+ if config.Facilitator != nil {
+ facilitators = append(facilitators, config.Facilitator)
+ }
+ facilitators = append(facilitators, config.Facilitators...)
+
+ // Convert to middleware options
+ opts := []MiddlewareOption{
+ WithSyncFacilitatorOnStart(syncOnStart),
+ WithTimeout(config.Timeout),
+ }
+
+ // Add facilitators
+ for _, facilitator := range facilitators {
+ opts = append(opts, WithFacilitatorClient(facilitator))
+ }
+
+ // Add schemes
+ for _, scheme := range config.Schemes {
+ opts = append(opts, WithScheme(scheme.Network, scheme.Server))
+ }
+
+ // Add optional handlers
+ if config.PaywallConfig != nil {
+ opts = append(opts, WithPaywallConfig(config.PaywallConfig))
+ }
+ if config.ErrorHandler != nil {
+ opts = append(opts, WithErrorHandler(config.ErrorHandler))
+ }
+ if config.SettlementHandler != nil {
+ opts = append(opts, WithSettlementHandler(config.SettlementHandler))
+ }
+
+ // Delegate to PaymentMiddlewareFromConfig (reuse all logic)
+ return PaymentMiddlewareFromConfig(config.Routes, opts...)
+}
+
+// SimpleX402Payment creates middleware with minimal configuration.
+// Uses a single route pattern and facilitator for the simplest possible setup.
+//
+// Args:
+//
+// payTo: Payment recipient address
+// price: Payment amount (e.g., "$0.001")
+// network: Payment network
+// facilitatorURL: Facilitator server URL
+//
+// Returns:
+//
+// Echo middleware function
+//
+// Example:
+//
+// e.Use(echomw.SimpleX402Payment(
+// "0x123...",
+// "$0.001",
+// "eip155:8453",
+// "https://facilitator.example.com",
+// ))
+func SimpleX402Payment(payTo string, price string, network x402.Network, facilitatorURL string) echo.MiddlewareFunc {
+ // Create facilitator client
+ facilitator := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
+ URL: facilitatorURL,
+ })
+
+ // Create routes for all endpoints
+ routes := x402http.RoutesConfig{
+ "*": {
+ Accepts: []x402http.PaymentOption{
+ {
+ Scheme: "exact",
+ PayTo: payTo,
+ Price: x402.Price(price),
+ Network: network,
+ },
+ },
+ },
+ }
+
+ return X402Payment(Config{
+ Routes: routes,
+ Facilitator: facilitator,
+ SyncFacilitatorOnStart: true,
+ })
+}
diff --git a/go/http/echo/middleware.go b/go/http/echo/middleware.go
new file mode 100644
index 0000000000..81a5eb501e
--- /dev/null
+++ b/go/http/echo/middleware.go
@@ -0,0 +1,483 @@
+package echo
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "net/http"
+ "sync"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ "github.com/coinbase/x402/go/extensions/bazaar"
+ x402http "github.com/coinbase/x402/go/http"
+ "github.com/labstack/echo/v4"
+)
+
+// SetSettlementOverrides sets settlement overrides on the Echo response for partial settlement.
+// The middleware extracts these before settlement and strips the header from the client response.
+func SetSettlementOverrides(c echo.Context, overrides *x402.SettlementOverrides) {
+ c.Response().Header().Set(x402http.SettlementOverridesHeader, x402http.MarshalSettlementOverrides(overrides))
+}
+
+// ============================================================================
+// Echo Adapter Implementation
+// ============================================================================
+
+// EchoAdapter implements HTTPAdapter for Echo framework
+type EchoAdapter struct {
+ ctx echo.Context
+}
+
+// NewEchoAdapter creates a new Echo adapter
+func NewEchoAdapter(ctx echo.Context) *EchoAdapter {
+ return &EchoAdapter{ctx: ctx}
+}
+
+// GetHeader gets a request header
+func (a *EchoAdapter) GetHeader(name string) string {
+ return a.ctx.Request().Header.Get(name)
+}
+
+// GetMethod gets the HTTP method
+func (a *EchoAdapter) GetMethod() string {
+ return a.ctx.Request().Method
+}
+
+// GetPath gets the request path
+func (a *EchoAdapter) GetPath() string {
+ return a.ctx.Request().URL.Path
+}
+
+// GetURL gets the full request URL
+func (a *EchoAdapter) GetURL() string {
+ req := a.ctx.Request()
+ scheme := "http"
+ if req.TLS != nil {
+ scheme = "https"
+ }
+ host := req.Host
+ if host == "" {
+ host = req.Header.Get("Host")
+ }
+ return fmt.Sprintf("%s://%s%s", scheme, host, req.RequestURI)
+}
+
+// GetAcceptHeader gets the Accept header
+func (a *EchoAdapter) GetAcceptHeader() string {
+ return a.ctx.Request().Header.Get("Accept")
+}
+
+// GetUserAgent gets the User-Agent header
+func (a *EchoAdapter) GetUserAgent() string {
+ return a.ctx.Request().Header.Get("User-Agent")
+}
+
+// ============================================================================
+// Middleware Configuration
+// ============================================================================
+
+// MiddlewareConfig configures the payment middleware
+type MiddlewareConfig struct {
+ // Routes configuration
+ Routes x402http.RoutesConfig
+
+ // Facilitator client(s)
+ FacilitatorClients []x402.FacilitatorClient
+
+ // Scheme registrations
+ Schemes []SchemeRegistration
+
+ // Paywall configuration
+ PaywallConfig *x402http.PaywallConfig
+
+ // Sync with facilitator on start
+ SyncFacilitatorOnStart bool
+
+ // Custom error handler
+ ErrorHandler func(echo.Context, error)
+
+ // Custom settlement handler
+ SettlementHandler func(echo.Context, *x402.SettleResponse)
+
+ // Context timeout for payment operations
+ Timeout time.Duration
+}
+
+// SchemeRegistration registers a scheme with the server
+type SchemeRegistration struct {
+ Network x402.Network
+ Server x402.SchemeNetworkServer
+}
+
+// MiddlewareOption configures the middleware
+type MiddlewareOption func(*MiddlewareConfig)
+
+// WithFacilitatorClient adds a facilitator client
+func WithFacilitatorClient(client x402.FacilitatorClient) MiddlewareOption {
+ return func(c *MiddlewareConfig) {
+ c.FacilitatorClients = append(c.FacilitatorClients, client)
+ }
+}
+
+// WithScheme registers a scheme server
+func WithScheme(network x402.Network, schemeServer x402.SchemeNetworkServer) MiddlewareOption {
+ return func(c *MiddlewareConfig) {
+ c.Schemes = append(c.Schemes, SchemeRegistration{
+ Network: network,
+ Server: schemeServer,
+ })
+ }
+}
+
+// WithPaywallConfig sets the paywall configuration
+func WithPaywallConfig(config *x402http.PaywallConfig) MiddlewareOption {
+ return func(c *MiddlewareConfig) {
+ c.PaywallConfig = config
+ }
+}
+
+// WithSyncFacilitatorOnStart sets whether to sync with facilitator on startup
+func WithSyncFacilitatorOnStart(sync bool) MiddlewareOption {
+ return func(c *MiddlewareConfig) {
+ c.SyncFacilitatorOnStart = sync
+ }
+}
+
+// WithErrorHandler sets a custom error handler
+func WithErrorHandler(handler func(echo.Context, error)) MiddlewareOption {
+ return func(c *MiddlewareConfig) {
+ c.ErrorHandler = handler
+ }
+}
+
+// WithSettlementHandler sets a custom settlement handler
+func WithSettlementHandler(handler func(echo.Context, *x402.SettleResponse)) MiddlewareOption {
+ return func(c *MiddlewareConfig) {
+ c.SettlementHandler = handler
+ }
+}
+
+// WithTimeout sets the context timeout for payment operations
+func WithTimeout(timeout time.Duration) MiddlewareOption {
+ return func(c *MiddlewareConfig) {
+ c.Timeout = timeout
+ }
+}
+
+// ============================================================================
+// Payment Middleware
+// ============================================================================
+
+// PaymentMiddleware creates Echo middleware for x402 payment handling using a pre-configured server.
+func PaymentMiddleware(routes x402http.RoutesConfig, server *x402.X402ResourceServer, opts ...MiddlewareOption) echo.MiddlewareFunc {
+ config := &MiddlewareConfig{
+ Routes: routes,
+ SyncFacilitatorOnStart: true,
+ Timeout: 30 * time.Second,
+ }
+
+ // Apply options
+ for _, opt := range opts {
+ opt(config)
+ }
+
+ // Wrap the resource server with HTTP functionality
+ httpServer := x402http.Wrappedx402HTTPResourceServer(routes, server)
+
+ httpServer.RegisterExtension(bazaar.BazaarResourceServerExtension)
+
+ // Initialize if requested - queries facilitator /supported to populate facilitatorClients map
+ if config.SyncFacilitatorOnStart {
+ ctx, cancel := context.WithTimeout(context.Background(), config.Timeout)
+ defer cancel()
+ if err := httpServer.Initialize(ctx); err != nil {
+ fmt.Printf("Warning: failed to initialize x402 server: %v\n", err)
+ }
+ }
+
+ // Create middleware handler using shared logic
+ return createMiddlewareHandler(httpServer, config)
+}
+
+// PaymentMiddlewareFromHTTPServer creates Echo middleware using a pre-configured HTTPServer.
+// This allows registering hooks (e.g., OnProtectedRequest) on the server before attaching to the router.
+//
+// Example:
+//
+// resourceServer := x402.Newx402ResourceServer(
+// x402.WithFacilitatorClient(facilitator),
+// ).Register("eip155:*", evm.NewExactEvmScheme())
+//
+// httpServer := x402http.Wrappedx402HTTPResourceServer(routes, resourceServer).
+// OnProtectedRequest(requestHook)
+//
+// e.Use(echomw.PaymentMiddlewareFromHTTPServer(httpServer))
+func PaymentMiddlewareFromHTTPServer(httpServer *x402http.HTTPServer, opts ...MiddlewareOption) echo.MiddlewareFunc {
+ config := &MiddlewareConfig{
+ SyncFacilitatorOnStart: true,
+ Timeout: 30 * time.Second,
+ }
+
+ // Apply options
+ for _, opt := range opts {
+ opt(config)
+ }
+
+ httpServer.RegisterExtension(bazaar.BazaarResourceServerExtension)
+
+ // Initialize if requested - queries facilitator /supported to populate facilitatorClients map
+ if config.SyncFacilitatorOnStart {
+ ctx, cancel := context.WithTimeout(context.Background(), config.Timeout)
+ defer cancel()
+ if err := httpServer.Initialize(ctx); err != nil {
+ fmt.Printf("Warning: failed to initialize x402 server: %v\n", err)
+ }
+ }
+
+ // Create middleware handler using shared logic
+ return createMiddlewareHandler(httpServer, config)
+}
+
+// PaymentMiddlewareFromConfig creates Echo middleware for x402 payment handling.
+// This creates the server internally from the provided options.
+func PaymentMiddlewareFromConfig(routes x402http.RoutesConfig, opts ...MiddlewareOption) echo.MiddlewareFunc {
+ config := &MiddlewareConfig{
+ Routes: routes,
+ FacilitatorClients: []x402.FacilitatorClient{},
+ Schemes: []SchemeRegistration{},
+ SyncFacilitatorOnStart: true,
+ Timeout: 30 * time.Second,
+ }
+
+ // Apply options
+ for _, opt := range opts {
+ opt(config)
+ }
+
+ serverOpts := []x402.ResourceServerOption{}
+ for _, client := range config.FacilitatorClients {
+ serverOpts = append(serverOpts, x402.WithFacilitatorClient(client))
+ }
+
+ httpServer := x402http.Newx402HTTPResourceServer(config.Routes, serverOpts...)
+
+ httpServer.RegisterExtension(bazaar.BazaarResourceServerExtension)
+
+ // Register schemes
+ for _, scheme := range config.Schemes {
+ httpServer.Register(scheme.Network, scheme.Server)
+ }
+
+ // Initialize if requested - queries facilitator /supported to populate facilitatorClients map
+ if config.SyncFacilitatorOnStart {
+ ctx, cancel := context.WithTimeout(context.Background(), config.Timeout)
+ defer cancel()
+ if err := httpServer.Initialize(ctx); err != nil {
+ fmt.Printf("Warning: failed to initialize x402 server: %v\n", err)
+ }
+ }
+
+ // Create middleware handler
+ return createMiddlewareHandler(httpServer, config)
+}
+
+// createMiddlewareHandler creates the actual Echo middleware function.
+func createMiddlewareHandler(server *x402http.HTTPServer, config *MiddlewareConfig) echo.MiddlewareFunc {
+ return func(next echo.HandlerFunc) echo.HandlerFunc {
+ return func(c echo.Context) error {
+ // Create adapter and request context
+ adapter := NewEchoAdapter(c)
+ reqCtx := x402http.HTTPRequestContext{
+ Adapter: adapter,
+ Path: c.Request().URL.Path,
+ Method: c.Request().Method,
+ }
+
+ // Check if route requires payment before waiting for initialization
+ if !server.RequiresPayment(reqCtx) {
+ return next(c)
+ }
+
+ // Create context with timeout
+ ctx, cancel := context.WithTimeout(c.Request().Context(), config.Timeout)
+ defer cancel()
+
+ result := server.ProcessHTTPRequest(ctx, reqCtx, config.PaywallConfig)
+
+ // Handle result
+ switch result.Type {
+ case x402http.ResultNoPaymentRequired:
+ // No payment required, continue to next handler
+ return next(c)
+
+ case x402http.ResultPaymentError:
+ // Payment required but not provided or invalid
+ return handlePaymentError(c, result.Response)
+
+ case x402http.ResultPaymentVerified:
+ // Payment verified, continue with settlement handling
+ return handlePaymentVerified(c, next, server, ctx, reqCtx, result, config)
+
+ default:
+ return next(c)
+ }
+ }
+ }
+}
+
+// handlePaymentError handles payment error responses
+func handlePaymentError(c echo.Context, response *x402http.HTTPResponseInstructions) error {
+ // Set headers
+ for key, value := range response.Headers {
+ c.Response().Header().Set(key, value)
+ }
+
+ // Send response body
+ if response.IsHTML {
+ return c.HTMLBlob(response.Status, []byte(response.Body.(string)))
+ }
+ return c.JSON(response.Status, response.Body)
+}
+
+// handlePaymentVerified handles verified payments with settlement
+func handlePaymentVerified(c echo.Context, next echo.HandlerFunc, server *x402http.HTTPServer, ctx context.Context, reqCtx x402http.HTTPRequestContext, result x402http.HTTPProcessResult, config *MiddlewareConfig) error {
+ // Capture response for settlement
+ origWriter := c.Response().Writer
+ capture := &responseCapture{
+ ResponseWriter: origWriter,
+ body: &bytes.Buffer{},
+ statusCode: http.StatusOK,
+ }
+ c.Response().Writer = capture
+
+ // Set payment data in context for downstream handlers
+ if result.PaymentPayload != nil {
+ c.Set("x402_payload", *result.PaymentPayload)
+ }
+ if result.PaymentRequirements != nil {
+ c.Set("x402_requirements", *result.PaymentRequirements)
+ }
+
+ // Continue to protected handler
+ err := next(c)
+
+ // Restore original writer
+ c.Response().Writer = origWriter
+ c.Response().Committed = false
+
+ // If handler returned error, propagate it
+ if err != nil {
+ return err
+ }
+
+ // Don't settle if response failed
+ if capture.statusCode >= 400 {
+ // Write captured error response
+ origWriter.WriteHeader(capture.statusCode)
+ _, _ = origWriter.Write(capture.body.Bytes())
+ return nil
+ }
+
+ settleResult := server.ProcessSettlement(
+ ctx,
+ *result.PaymentPayload,
+ *result.PaymentRequirements,
+ nil,
+ &x402http.HTTPTransportContext{
+ Request: &reqCtx,
+ ResponseBody: capture.body.Bytes(),
+ ResponseHeaders: capture.Header(),
+ },
+ )
+
+ // Check settlement success
+ if !settleResult.Success {
+ // Always set PAYMENT-RESPONSE header on settlement failure
+ for key, value := range settleResult.Headers {
+ origWriter.Header().Set(key, value)
+ }
+ switch {
+ case config.ErrorHandler != nil:
+ errorReason := settleResult.ErrorReason
+ if errorReason == "" {
+ errorReason = "Settlement failed"
+ }
+ config.ErrorHandler(c, fmt.Errorf("settlement failed: %s", errorReason))
+ case settleResult.Response != nil:
+ return handlePaymentError(c, settleResult.Response)
+ default:
+ return c.JSON(http.StatusPaymentRequired, map[string]interface{}{})
+ }
+ return nil
+ }
+
+ // Add settlement headers
+ for key, value := range settleResult.Headers {
+ origWriter.Header().Set(key, value)
+ }
+
+ // Call settlement handler if configured
+ if config.SettlementHandler != nil {
+ settleResponse := &x402.SettleResponse{
+ Success: true,
+ Transaction: settleResult.Transaction,
+ Network: settleResult.Network,
+ Payer: settleResult.Payer,
+ }
+ config.SettlementHandler(c, settleResponse)
+ }
+
+ // Write captured response
+ origWriter.WriteHeader(capture.statusCode)
+ _, _ = origWriter.Write(capture.body.Bytes())
+ return nil
+}
+
+// ============================================================================
+// Response Capture
+// ============================================================================
+
+// responseCapture captures the response for settlement processing
+type responseCapture struct {
+ http.ResponseWriter
+ body *bytes.Buffer
+ statusCode int
+ written bool
+ mu sync.Mutex
+}
+
+// WriteHeader captures the status code
+func (w *responseCapture) WriteHeader(code int) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ w.writeHeaderLocked(code)
+}
+
+// writeHeaderLocked sets the status code (must be called with lock held)
+func (w *responseCapture) writeHeaderLocked(code int) {
+ if !w.written {
+ w.statusCode = code
+ w.written = true
+ }
+}
+
+// Write captures the response body
+func (w *responseCapture) Write(data []byte) (int, error) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ if !w.written {
+ w.writeHeaderLocked(http.StatusOK)
+ }
+ return w.body.Write(data)
+}
+
+// WriteString captures string responses
+func (w *responseCapture) WriteString(s string) (int, error) {
+ return w.Write([]byte(s))
+}
+
+// Flush is a no-op to prevent premature flushing to the wire before settlement.
+func (w *responseCapture) Flush() {}
diff --git a/go/http/echo/middleware_test.go b/go/http/echo/middleware_test.go
new file mode 100644
index 0000000000..6500357a33
--- /dev/null
+++ b/go/http/echo/middleware_test.go
@@ -0,0 +1,1339 @@
+package echo
+
+import (
+ "bytes"
+ "context"
+ "encoding/base64"
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ x402 "github.com/coinbase/x402/go"
+ x402http "github.com/coinbase/x402/go/http"
+ "github.com/coinbase/x402/go/types"
+ "github.com/labstack/echo/v4"
+)
+
+// ============================================================================
+// Mock Implementations
+// ============================================================================
+
+// mockSchemeServer implements x402.SchemeNetworkServer for testing
+type mockSchemeServer struct {
+ scheme string
+}
+
+func (m *mockSchemeServer) Scheme() string {
+ return m.scheme
+}
+
+func (m *mockSchemeServer) ParsePrice(price x402.Price, network x402.Network) (x402.AssetAmount, error) {
+ return x402.AssetAmount{
+ Asset: "USDC",
+ Amount: "1000000",
+ }, nil
+}
+
+func (m *mockSchemeServer) EnhancePaymentRequirements(ctx context.Context, base types.PaymentRequirements, supported types.SupportedKind, extensions []string) (types.PaymentRequirements, error) {
+ return base, nil
+}
+
+// mockFacilitatorClient implements x402.FacilitatorClient for testing
+type mockFacilitatorClient struct {
+ verifyFunc func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.VerifyResponse, error)
+ settleFunc func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.SettleResponse, error)
+ supportedFunc func(ctx context.Context) (x402.SupportedResponse, error)
+}
+
+func (m *mockFacilitatorClient) Verify(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.VerifyResponse, error) {
+ if m.verifyFunc != nil {
+ return m.verifyFunc(ctx, payloadBytes, requirementsBytes)
+ }
+ return &x402.VerifyResponse{IsValid: true, Payer: "0xmock"}, nil
+}
+
+func (m *mockFacilitatorClient) Settle(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.SettleResponse, error) {
+ if m.settleFunc != nil {
+ return m.settleFunc(ctx, payloadBytes, requirementsBytes)
+ }
+ return &x402.SettleResponse{Success: true, Transaction: "0xtx", Network: "eip155:1", Payer: "0xmock"}, nil
+}
+
+func (m *mockFacilitatorClient) GetSupported(ctx context.Context) (x402.SupportedResponse, error) {
+ if m.supportedFunc != nil {
+ return m.supportedFunc(ctx)
+ }
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+}
+
+func (m *mockFacilitatorClient) Identifier() string {
+ return "mock"
+}
+
+// ============================================================================
+// Test Helpers
+// ============================================================================
+
+// createTestEcho creates an Echo instance for testing
+func createTestEcho() *echo.Echo {
+ e := echo.New()
+ return e
+}
+
+// createPaymentHeader creates a base64-encoded payment header for testing
+//
+//nolint:unparam // payTo is always "0xtest" in current tests but keeping param for flexibility
+func createPaymentHeader(payTo string) string {
+ payload := x402.PaymentPayload{
+ X402Version: 2,
+ Payload: map[string]interface{}{"sig": "test"},
+ Accepted: x402.PaymentRequirements{
+ Scheme: "exact",
+ Network: "eip155:1",
+ Asset: "USDC",
+ Amount: "1000000",
+ PayTo: payTo,
+ MaxTimeoutSeconds: 300,
+ Extra: map[string]interface{}{
+ "resourceUrl": "http://example.com/api",
+ },
+ },
+ }
+
+ payloadJSON, _ := json.Marshal(payload)
+ return base64.StdEncoding.EncodeToString(payloadJSON)
+}
+
+// ============================================================================
+// EchoAdapter Tests
+// ============================================================================
+
+func TestEchoAdapter_GetHeader(t *testing.T) {
+ e := createTestEcho()
+ var adapter *EchoAdapter
+
+ e.GET("/test", func(c echo.Context) error {
+ adapter = NewEchoAdapter(c)
+ return c.NoContent(http.StatusOK)
+ })
+
+ req := httptest.NewRequest("GET", "/test", nil)
+ req.Header.Set("X-Custom-Header", "test-value")
+ req.Header.Set("payment-signature", "sig-data")
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if adapter.GetHeader("X-Custom-Header") != "test-value" {
+ t.Error("Expected X-Custom-Header to be 'test-value'")
+ }
+
+ if adapter.GetHeader("Payment-Signature") != "sig-data" {
+ t.Error("Expected payment-signature header")
+ }
+}
+
+func TestEchoAdapter_GetMethod(t *testing.T) {
+ tests := []struct {
+ method string
+ expected string
+ }{
+ {"GET", "GET"},
+ {"POST", "POST"},
+ {"PUT", "PUT"},
+ {"DELETE", "DELETE"},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.method, func(t *testing.T) {
+ e := createTestEcho()
+ var adapter *EchoAdapter
+
+ e.Add(tt.method, "/test", func(c echo.Context) error {
+ adapter = NewEchoAdapter(c)
+ return c.NoContent(http.StatusOK)
+ })
+
+ req := httptest.NewRequest(tt.method, "/test", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if adapter.GetMethod() != tt.expected {
+ t.Errorf("Expected method %s, got %s", tt.expected, adapter.GetMethod())
+ }
+ })
+ }
+}
+
+func TestEchoAdapter_GetPath(t *testing.T) {
+ e := createTestEcho()
+ var adapter *EchoAdapter
+
+ e.GET("/api/users/:id", func(c echo.Context) error {
+ adapter = NewEchoAdapter(c)
+ return c.NoContent(http.StatusOK)
+ })
+
+ req := httptest.NewRequest("GET", "/api/users/123", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if adapter.GetPath() != "/api/users/123" {
+ t.Errorf("Expected path '/api/users/123', got '%s'", adapter.GetPath())
+ }
+}
+
+func TestEchoAdapter_GetURL(t *testing.T) {
+ tests := []struct {
+ name string
+ target string
+ expected string
+ }{
+ {
+ name: "with query params",
+ target: "/api/test?id=1",
+ expected: "http://example.com/api/test?id=1",
+ },
+ {
+ name: "without query params",
+ target: "/api/test",
+ expected: "http://example.com/api/test",
+ },
+ {
+ name: "with multiple query params",
+ target: "/api/test?id=1&foo=bar",
+ expected: "http://example.com/api/test?id=1&foo=bar",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ e := createTestEcho()
+ var adapter *EchoAdapter
+
+ e.GET("/api/test", func(c echo.Context) error {
+ adapter = NewEchoAdapter(c)
+ return c.NoContent(http.StatusOK)
+ })
+
+ req := httptest.NewRequest("GET", tt.target, nil)
+ req.Host = "example.com"
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if adapter.GetURL() != tt.expected {
+ t.Errorf("Expected URL '%s', got '%s'", tt.expected, adapter.GetURL())
+ }
+ })
+ }
+}
+
+func TestEchoAdapter_GetAcceptHeader(t *testing.T) {
+ e := createTestEcho()
+ var adapter *EchoAdapter
+
+ e.GET("/test", func(c echo.Context) error {
+ adapter = NewEchoAdapter(c)
+ return c.NoContent(http.StatusOK)
+ })
+
+ req := httptest.NewRequest("GET", "/test", nil)
+ req.Header.Set("Accept", "text/html")
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if adapter.GetAcceptHeader() != "text/html" {
+ t.Errorf("Expected Accept header 'text/html', got '%s'", adapter.GetAcceptHeader())
+ }
+}
+
+func TestEchoAdapter_GetUserAgent(t *testing.T) {
+ e := createTestEcho()
+ var adapter *EchoAdapter
+
+ e.GET("/test", func(c echo.Context) error {
+ adapter = NewEchoAdapter(c)
+ return c.NoContent(http.StatusOK)
+ })
+
+ req := httptest.NewRequest("GET", "/test", nil)
+ req.Header.Set("User-Agent", "Mozilla/5.0")
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if adapter.GetUserAgent() != "Mozilla/5.0" {
+ t.Errorf("Expected User-Agent 'Mozilla/5.0', got '%s'", adapter.GetUserAgent())
+ }
+}
+
+// ============================================================================
+// PaymentMiddleware Tests
+// ============================================================================
+
+func TestPaymentMiddleware_CallsNextWhenNoPaymentRequired(t *testing.T) {
+ routes := x402http.RoutesConfig{
+ "GET /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes, WithSyncFacilitatorOnStart(false)))
+
+ nextCalled := false
+ e.GET("/public", func(c echo.Context) error {
+ nextCalled = true
+ return c.JSON(http.StatusOK, map[string]interface{}{"message": "success"})
+ })
+
+ req := httptest.NewRequest("GET", "/public", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if !nextCalled {
+ t.Error("Expected next() to be called for non-protected route")
+ }
+ if w.Code != http.StatusOK {
+ t.Errorf("Expected status 200, got %d", w.Code)
+ }
+}
+
+func TestPaymentMiddleware_Returns402JSONForPaymentError(t *testing.T) {
+ mockClient := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "GET /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ Description: "API access",
+ },
+ }
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes,
+ WithFacilitatorClient(mockClient),
+ WithScheme("eip155:1", mockServer),
+ WithSyncFacilitatorOnStart(true),
+ WithTimeout(5*time.Second),
+ ))
+
+ e.GET("/api", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "protected"})
+ })
+
+ req := httptest.NewRequest("GET", "/api", nil)
+ req.Header.Set("Accept", "application/json")
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusPaymentRequired {
+ t.Errorf("Expected status 402, got %d", w.Code)
+ }
+
+ if w.Header().Get("PAYMENT-REQUIRED") == "" {
+ t.Error("Expected PAYMENT-REQUIRED header")
+ }
+}
+
+func TestPaymentMiddleware_Returns402HTMLForBrowserRequest(t *testing.T) {
+ mockClient := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "*": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$5.00",
+ Network: "eip155:1",
+ },
+ },
+ Description: "Premium content",
+ },
+ }
+
+ paywallConfig := &x402http.PaywallConfig{
+ AppName: "Test App",
+ }
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes,
+ WithFacilitatorClient(mockClient),
+ WithScheme("eip155:1", mockServer),
+ WithPaywallConfig(paywallConfig),
+ WithSyncFacilitatorOnStart(true),
+ WithTimeout(5*time.Second),
+ ))
+
+ e.GET("/content", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "protected"})
+ })
+
+ req := httptest.NewRequest("GET", "/content", nil)
+ req.Header.Set("Accept", "text/html")
+ req.Header.Set("User-Agent", "Mozilla/5.0")
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusPaymentRequired {
+ t.Errorf("Expected status 402, got %d", w.Code)
+ }
+
+ contentType := w.Header().Get("Content-Type")
+ if !bytes.Contains([]byte(contentType), []byte("text/html")) {
+ t.Errorf("Expected Content-Type to contain 'text/html', got '%s'", contentType)
+ }
+
+ body := w.Body.String()
+ if !bytes.Contains([]byte(body), []byte("Payment Required")) {
+ t.Error("Expected 'Payment Required' in HTML body")
+ }
+ if !bytes.Contains([]byte(body), []byte("Test App")) {
+ t.Error("Expected app name in HTML body")
+ }
+}
+
+func TestPaymentMiddleware_SettlesAndReturnsResponseForVerifiedPayment(t *testing.T) {
+ settleCalled := false
+
+ mockClient := &mockFacilitatorClient{
+ verifyFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.VerifyResponse, error) {
+ return &x402.VerifyResponse{IsValid: true, Payer: "0xpayer"}, nil
+ },
+ settleFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.SettleResponse, error) {
+ settleCalled = true
+ return &x402.SettleResponse{
+ Success: true,
+ Transaction: "0xtx",
+ Network: "eip155:1",
+ Payer: "0xpayer",
+ }, nil
+ },
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "POST /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes,
+ WithFacilitatorClient(mockClient),
+ WithScheme("eip155:1", mockServer),
+ WithSyncFacilitatorOnStart(true),
+ WithTimeout(5*time.Second),
+ ))
+
+ e.POST("/api", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "protected-data"})
+ })
+
+ req := httptest.NewRequest("POST", "/api", nil)
+ req.Header.Set("PAYMENT-SIGNATURE", createPaymentHeader("0xtest"))
+ req.Host = "example.com"
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Errorf("Expected status 200, got %d. Body: %s", w.Code, w.Body.String())
+ }
+
+ if !settleCalled {
+ t.Error("Expected settlement to be called")
+ }
+
+ if w.Header().Get("PAYMENT-RESPONSE") == "" {
+ t.Error("Expected PAYMENT-RESPONSE header")
+ }
+}
+
+func TestPaymentMiddleware_SkipsSettlementWhenHandlerReturns400OrHigher(t *testing.T) {
+ settleCalled := false
+
+ mockClient := &mockFacilitatorClient{
+ verifyFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.VerifyResponse, error) {
+ return &x402.VerifyResponse{IsValid: true, Payer: "0xpayer"}, nil
+ },
+ settleFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.SettleResponse, error) {
+ settleCalled = true
+ return &x402.SettleResponse{Success: true, Transaction: "0xtx"}, nil
+ },
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "POST /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes,
+ WithFacilitatorClient(mockClient),
+ WithScheme("eip155:1", mockServer),
+ WithSyncFacilitatorOnStart(true),
+ WithTimeout(5*time.Second),
+ ))
+
+ e.POST("/api", func(c echo.Context) error {
+ // Handler returns error status
+ return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "internal error"})
+ })
+
+ req := httptest.NewRequest("POST", "/api", nil)
+ req.Header.Set("PAYMENT-SIGNATURE", createPaymentHeader("0xtest"))
+ req.Host = "example.com"
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusInternalServerError {
+ t.Errorf("Expected status 500, got %d", w.Code)
+ }
+
+ if settleCalled {
+ t.Error("Settlement should NOT be called when handler returns >= 400")
+ }
+}
+
+func TestPaymentMiddleware_Returns402WhenSettlementFails(t *testing.T) {
+ mockClient := &mockFacilitatorClient{
+ verifyFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.VerifyResponse, error) {
+ return &x402.VerifyResponse{IsValid: true, Payer: "0xpayer"}, nil
+ },
+ settleFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.SettleResponse, error) {
+ return &x402.SettleResponse{
+ Success: false,
+ ErrorReason: "Insufficient funds",
+ }, nil
+ },
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "POST /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes,
+ WithFacilitatorClient(mockClient),
+ WithScheme("eip155:1", mockServer),
+ WithSyncFacilitatorOnStart(true),
+ WithTimeout(5*time.Second),
+ ))
+
+ e.POST("/api", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "protected-data"})
+ })
+
+ req := httptest.NewRequest("POST", "/api", nil)
+ req.Header.Set("PAYMENT-SIGNATURE", createPaymentHeader("0xtest"))
+ req.Host = "example.com"
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusPaymentRequired {
+ t.Errorf("Expected status 402, got %d", w.Code)
+ }
+
+ // Empty body by default on settlement failure
+ var response map[string]interface{}
+ if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
+ t.Fatalf("Failed to parse response: %v", err)
+ }
+ if len(response) != 0 {
+ t.Errorf("Expected empty body {}, got %v", response)
+ }
+
+ // PAYMENT-RESPONSE header must be included on settlement failure
+ if w.Header().Get("PAYMENT-RESPONSE") == "" {
+ t.Error("Expected PAYMENT-RESPONSE header on settlement failure")
+ }
+}
+
+func TestPaymentMiddleware_CustomErrorHandler(t *testing.T) {
+ customHandlerCalled := false
+
+ mockClient := &mockFacilitatorClient{
+ verifyFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.VerifyResponse, error) {
+ return &x402.VerifyResponse{IsValid: true, Payer: "0xpayer"}, nil
+ },
+ settleFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.SettleResponse, error) {
+ return &x402.SettleResponse{
+ Success: false,
+ ErrorReason: "Settlement rejected",
+ }, nil
+ },
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "POST /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ customErrorHandler := func(c echo.Context, err error) {
+ customHandlerCalled = true
+ // Reset committed state so we can write
+ c.Response().Committed = false
+ _ = c.JSON(http.StatusPaymentRequired, map[string]interface{}{
+ "custom_error": err.Error(),
+ })
+ }
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes,
+ WithFacilitatorClient(mockClient),
+ WithScheme("eip155:1", mockServer),
+ WithErrorHandler(customErrorHandler),
+ WithSyncFacilitatorOnStart(true),
+ WithTimeout(5*time.Second),
+ ))
+
+ e.POST("/api", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "protected-data"})
+ })
+
+ req := httptest.NewRequest("POST", "/api", nil)
+ req.Header.Set("PAYMENT-SIGNATURE", createPaymentHeader("0xtest"))
+ req.Host = "example.com"
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if !customHandlerCalled {
+ t.Error("Expected custom error handler to be called")
+ }
+
+ var response map[string]interface{}
+ if err := json.Unmarshal(w.Body.Bytes(), &response); err != nil {
+ t.Fatalf("Failed to parse response: %v", err)
+ }
+
+ if response["custom_error"] == nil {
+ t.Error("Expected custom_error in response")
+ }
+}
+
+func TestPaymentMiddleware_CustomSettlementHandler(t *testing.T) {
+ settlementHandlerCalled := false
+ var capturedSettleResponse *x402.SettleResponse
+
+ mockClient := &mockFacilitatorClient{
+ verifyFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.VerifyResponse, error) {
+ return &x402.VerifyResponse{IsValid: true, Payer: "0xpayer"}, nil
+ },
+ settleFunc: func(ctx context.Context, payloadBytes []byte, requirementsBytes []byte) (*x402.SettleResponse, error) {
+ return &x402.SettleResponse{
+ Success: true,
+ Transaction: "0xtx123",
+ Network: "eip155:1",
+ Payer: "0xpayer",
+ }, nil
+ },
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "POST /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ customSettlementHandler := func(c echo.Context, settleResponse *x402.SettleResponse) {
+ settlementHandlerCalled = true
+ capturedSettleResponse = settleResponse
+ // Add custom header
+ c.Response().Header().Set("X-Transaction-ID", settleResponse.Transaction)
+ }
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes,
+ WithFacilitatorClient(mockClient),
+ WithScheme("eip155:1", mockServer),
+ WithSettlementHandler(customSettlementHandler),
+ WithSyncFacilitatorOnStart(true),
+ WithTimeout(5*time.Second),
+ ))
+
+ e.POST("/api", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "protected-data"})
+ })
+
+ req := httptest.NewRequest("POST", "/api", nil)
+ req.Header.Set("PAYMENT-SIGNATURE", createPaymentHeader("0xtest"))
+ req.Host = "example.com"
+
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Errorf("Expected status 200, got %d", w.Code)
+ }
+
+ if !settlementHandlerCalled {
+ t.Error("Expected custom settlement handler to be called")
+ }
+
+ if capturedSettleResponse == nil {
+ t.Fatal("Expected settle response to be captured")
+ }
+
+ if capturedSettleResponse.Transaction != "0xtx123" {
+ t.Errorf("Expected transaction '0xtx123', got '%s'", capturedSettleResponse.Transaction)
+ }
+
+ if w.Header().Get("X-Transaction-ID") != "0xtx123" {
+ t.Error("Expected custom X-Transaction-ID header")
+ }
+}
+
+func TestPaymentMiddleware_WithTimeout(t *testing.T) {
+ mockClient := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "*": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ timeout := 10 * time.Second
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromConfig(routes,
+ WithFacilitatorClient(mockClient),
+ WithScheme("eip155:1", mockServer),
+ WithTimeout(timeout),
+ WithSyncFacilitatorOnStart(true),
+ ))
+
+ e.GET("/test", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"message": "success"})
+ })
+
+ // Verify the middleware is created and requires payment
+ req := httptest.NewRequest("GET", "/test", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ // Route should require payment
+ if w.Code != http.StatusPaymentRequired {
+ t.Errorf("Expected status 402, got %d", w.Code)
+ }
+}
+
+// ============================================================================
+// PaymentMiddlewareFromHTTPServer Tests
+// ============================================================================
+
+func TestPaymentMiddlewareFromHTTPServer_Returns402ForProtectedRoute(t *testing.T) {
+ mockClient := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ routes := x402http.RoutesConfig{
+ "GET /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ // Build the resource server externally
+ resourceServer := x402.Newx402ResourceServer(
+ x402.WithFacilitatorClient(mockClient),
+ )
+ resourceServer.Register("eip155:1", &mockSchemeServer{scheme: "exact"})
+
+ // Wrap with HTTP server
+ httpServer := x402http.Wrappedx402HTTPResourceServer(routes, resourceServer)
+
+ // Use PaymentMiddlewareFromHTTPServer
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromHTTPServer(httpServer, WithTimeout(5*time.Second)))
+
+ e.GET("/api", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "protected"})
+ })
+
+ req := httptest.NewRequest("GET", "/api", nil)
+ req.Header.Set("Accept", "application/json")
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusPaymentRequired {
+ t.Errorf("Expected status 402, got %d", w.Code)
+ }
+}
+
+func TestPaymentMiddlewareFromHTTPServer_PassesThroughNonProtectedRoute(t *testing.T) {
+ routes := x402http.RoutesConfig{
+ "GET /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ resourceServer := x402.Newx402ResourceServer()
+ httpServer := x402http.Wrappedx402HTTPResourceServer(routes, resourceServer)
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromHTTPServer(httpServer, WithSyncFacilitatorOnStart(false)))
+
+ nextCalled := false
+ e.GET("/public", func(c echo.Context) error {
+ nextCalled = true
+ return c.JSON(http.StatusOK, map[string]interface{}{"message": "public"})
+ })
+
+ req := httptest.NewRequest("GET", "/public", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if !nextCalled {
+ t.Error("Expected next() to be called for non-protected route")
+ }
+ if w.Code != http.StatusOK {
+ t.Errorf("Expected status 200, got %d", w.Code)
+ }
+}
+
+func TestPaymentMiddlewareFromHTTPServer_HookGrantsAccess(t *testing.T) {
+ mockClient := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ routes := x402http.RoutesConfig{
+ "GET /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ resourceServer := x402.Newx402ResourceServer(
+ x402.WithFacilitatorClient(mockClient),
+ )
+ resourceServer.Register("eip155:1", &mockSchemeServer{scheme: "exact"})
+
+ // Register a hook that grants free access
+ httpServer := x402http.Wrappedx402HTTPResourceServer(routes, resourceServer).
+ OnProtectedRequest(func(ctx context.Context, reqCtx x402http.HTTPRequestContext, routeConfig x402http.RouteConfig) (*x402http.ProtectedRequestHookResult, error) {
+ return &x402http.ProtectedRequestHookResult{GrantAccess: true}, nil
+ })
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromHTTPServer(httpServer, WithTimeout(5*time.Second)))
+
+ nextCalled := false
+ e.GET("/api", func(c echo.Context) error {
+ nextCalled = true
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "free-access"})
+ })
+
+ // Request without payment header - hook should grant access
+ req := httptest.NewRequest("GET", "/api", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Errorf("Expected status 200 (hook granted access), got %d. Body: %s", w.Code, w.Body.String())
+ }
+ if !nextCalled {
+ t.Error("Expected next handler to be called when hook grants access")
+ }
+}
+
+func TestPaymentMiddlewareFromHTTPServer_HookAbortsRequest(t *testing.T) {
+ mockClient := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ routes := x402http.RoutesConfig{
+ "GET /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ resourceServer := x402.Newx402ResourceServer(
+ x402.WithFacilitatorClient(mockClient),
+ )
+ resourceServer.Register("eip155:1", &mockSchemeServer{scheme: "exact"})
+
+ // Register a hook that aborts the request
+ httpServer := x402http.Wrappedx402HTTPResourceServer(routes, resourceServer).
+ OnProtectedRequest(func(ctx context.Context, reqCtx x402http.HTTPRequestContext, routeConfig x402http.RouteConfig) (*x402http.ProtectedRequestHookResult, error) {
+ return &x402http.ProtectedRequestHookResult{Abort: true, Reason: "IP blocked"}, nil
+ })
+
+ e := createTestEcho()
+ e.Use(PaymentMiddlewareFromHTTPServer(httpServer, WithTimeout(5*time.Second)))
+
+ e.GET("/api", func(c echo.Context) error {
+ t.Error("Handler should not be called when hook aborts")
+ return nil
+ })
+
+ req := httptest.NewRequest("GET", "/api", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusForbidden {
+ t.Errorf("Expected status 403 (hook aborted), got %d", w.Code)
+ }
+}
+
+// ============================================================================
+// X402Payment (Builder Pattern) Tests
+// ============================================================================
+
+func TestX402Payment_CreatesWorkingMiddleware(t *testing.T) {
+ mockClient := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "GET /api": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ e := createTestEcho()
+ e.Use(X402Payment(Config{
+ Routes: routes,
+ Facilitator: mockClient,
+ Schemes: []SchemeConfig{
+ {Network: "eip155:1", Server: mockServer},
+ },
+ SyncFacilitatorOnStart: true,
+ Timeout: 5 * time.Second,
+ }))
+
+ e.GET("/api", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"data": "protected"})
+ })
+
+ // Test non-protected route passes through
+ e.GET("/public", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"message": "public"})
+ })
+
+ req := httptest.NewRequest("GET", "/public", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Errorf("Expected status 200 for public route, got %d", w.Code)
+ }
+
+ // Test protected route requires payment
+ req = httptest.NewRequest("GET", "/api", nil)
+ w = httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusPaymentRequired {
+ t.Errorf("Expected status 402 for protected route, got %d", w.Code)
+ }
+}
+
+func TestX402Payment_RegistersMultipleFacilitators(t *testing.T) {
+ mockClient1 := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+ mockClient2 := &mockFacilitatorClient{
+ supportedFunc: func(ctx context.Context) (x402.SupportedResponse, error) {
+ return x402.SupportedResponse{
+ Kinds: []x402.SupportedKind{
+ {X402Version: 2, Scheme: "exact", Network: "eip155:1"},
+ },
+ Extensions: []string{},
+ Signers: make(map[string][]string),
+ }, nil
+ },
+ }
+
+ mockServer := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "*": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ // This should not panic and properly register multiple facilitators
+ e := createTestEcho()
+ e.Use(X402Payment(Config{
+ Routes: routes,
+ Facilitators: []x402.FacilitatorClient{mockClient1, mockClient2},
+ Schemes: []SchemeConfig{
+ {Network: "eip155:1", Server: mockServer},
+ },
+ SyncFacilitatorOnStart: true,
+ }))
+
+ e.GET("/test", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"message": "success"})
+ })
+
+ req := httptest.NewRequest("GET", "/test", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusPaymentRequired {
+ t.Errorf("Expected status 402, got %d", w.Code)
+ }
+}
+
+func TestX402Payment_RegistersMultipleSchemes(t *testing.T) {
+ mockServer1 := &mockSchemeServer{scheme: "exact"}
+ mockServer2 := &mockSchemeServer{scheme: "exact"}
+
+ routes := x402http.RoutesConfig{
+ "*": x402http.RouteConfig{
+ Accepts: x402http.PaymentOptions{
+ {
+ Scheme: "exact",
+ PayTo: "0xtest",
+ Price: "$1.00",
+ Network: "eip155:1",
+ },
+ },
+ },
+ }
+
+ // This should not panic
+ e := createTestEcho()
+ e.Use(X402Payment(Config{
+ Routes: routes,
+ Schemes: []SchemeConfig{
+ {Network: "eip155:1", Server: mockServer1},
+ {Network: "eip155:8453", Server: mockServer2},
+ },
+ SyncFacilitatorOnStart: false,
+ }))
+
+ e.GET("/test", func(c echo.Context) error {
+ return c.JSON(http.StatusOK, map[string]interface{}{"message": "success"})
+ })
+
+ req := httptest.NewRequest("GET", "/test", nil)
+ w := httptest.NewRecorder()
+ e.ServeHTTP(w, req)
+
+ if w.Code != http.StatusPaymentRequired {
+ t.Errorf("Expected status 402, got %d", w.Code)
+ }
+}
+
+// ============================================================================
+// responseCapture Tests
+// ============================================================================
+
+func TestResponseCapture_CapturesStatusCode(t *testing.T) {
+ capture := &responseCapture{
+ ResponseWriter: httptest.NewRecorder(),
+ body: &bytes.Buffer{},
+ statusCode: http.StatusOK,
+ }
+
+ capture.WriteHeader(http.StatusCreated)
+
+ if capture.statusCode != http.StatusCreated {
+ t.Errorf("Expected status 201, got %d", capture.statusCode)
+ }
+}
+
+func TestResponseCapture_CapturesBody(t *testing.T) {
+ capture := &responseCapture{
+ ResponseWriter: httptest.NewRecorder(),
+ body: &bytes.Buffer{},
+ statusCode: http.StatusOK,
+ }
+
+ data := []byte(`{"message":"test"}`)
+ n, err := capture.Write(data)
+
+ if err != nil {
+ t.Fatalf("Write failed: %v", err)
+ }
+ if n != len(data) {
+ t.Errorf("Expected to write %d bytes, wrote %d", len(data), n)
+ }
+ if capture.body.String() != `{"message":"test"}` {
+ t.Errorf("Expected body '%s', got '%s'", `{"message":"test"}`, capture.body.String())
+ }
+}
+
+func TestResponseCapture_WriteString(t *testing.T) {
+ capture := &responseCapture{
+ ResponseWriter: httptest.NewRecorder(),
+ body: &bytes.Buffer{},
+ statusCode: http.StatusOK,
+ }
+
+ n, err := capture.WriteString("hello world")
+
+ if err != nil {
+ t.Fatalf("WriteString failed: %v", err)
+ }
+ if n != 11 {
+ t.Errorf("Expected to write 11 bytes, wrote %d", n)
+ }
+ if capture.body.String() != "hello world" {
+ t.Errorf("Expected body 'hello world', got '%s'", capture.body.String())
+ }
+}
+
+func TestResponseCapture_WriteHeaderOnlyOnce(t *testing.T) {
+ capture := &responseCapture{
+ ResponseWriter: httptest.NewRecorder(),
+ body: &bytes.Buffer{},
+ statusCode: http.StatusOK,
+ }
+
+ capture.WriteHeader(http.StatusCreated)
+ capture.WriteHeader(http.StatusAccepted) // Should be ignored
+
+ if capture.statusCode != http.StatusCreated {
+ t.Errorf("Expected status 201 (first call), got %d", capture.statusCode)
+ }
+}
diff --git a/go/http/evm_paywall_template.go b/go/http/evm_paywall_template.go
index 24e183877f..1f03ef1070 100644
--- a/go/http/evm_paywall_template.go
+++ b/go/http/evm_paywall_template.go
@@ -2,4 +2,4 @@
package http
// EVMPaywallTemplate is the pre-built EVM paywall template with inlined CSS and JS
-const EVMPaywallTemplate = "