uvd-x402-sdk 0.14.0
Python SDK for x402 payments - gasless crypto payments across 21 blockchains (including Scroll, SKALE). Features: ERC-8004 Trustless Agents, Escrow/Refunds, multi-stablecoin (USDC, EURC, AUSD, PYUSD, USDT)
Requires Python: >=3.9
Keywords: x402, payments, crypto, usdc, eurc, stablecoin, web3, evm, solana, near, stellar, algorand, sui, scroll, skale, facilitator, gasless, eip-712, eip-3009, erc-8004, trustless-agents, escrow, refund

# uvd-x402-sdk

Python SDK for integrating **x402 cryptocurrency payments** via the Ultravioleta DAO facilitator.

Accept **gasless stablecoin payments** across **21 blockchain networks** with a single integration. The SDK handles signature verification, on-chain settlement, and all the complexity of multi-chain payments.

**New in v0.6.0**: ERC-8004 Trustless Agents (on-chain reputation), Escrow & Refund support, Scroll and SKALE networks!

## Features

- **21 Networks**: EVM chains (13 including Scroll, SKALE), SVM chains (Solana, Fogo), NEAR, Stellar, Algorand, and Sui
- **5 Stablecoins**: USDC, EURC, AUSD, PYUSD, USDT (EVM chains)
- **x402 v1 & v2**: Full support for both protocol versions with auto-detection
- **Framework Integrations**: Flask, FastAPI, Django, AWS Lambda
- **Gasless Payments**: Users sign EIP-712/EIP-3009 authorizations, facilitator pays all network fees
- **Simple API**: Decorators and middleware for quick integration
- **Type Safety**: Full Pydantic models and type hints
- **Extensible**: Register custom networks and tokens easily
- **ERC-8004 Trustless Agents**: On-chain reputation and identity for AI agents
- **Escrow & Refunds**: Hold payments in escrow with dispute resolution

## Quick Start (5 Lines)

```python
from decimal import Decimal
from uvd_x402_sdk import X402Client

client = X402Client(recipient_address="0xYourWallet...")
result = client.process_payment(request.headers["X-PAYMENT"], Decimal("10.00"))
print(f"Paid by {result.payer_address}, tx: {result.transaction_hash}")
```

## Complete Usage Example

Here's a complete example showing the full x402 payment flow - returning a 402 response when no payment is provided, and processing the payment when it arrives:

```python
from decimal import Decimal
from uvd_x402_sdk import (
    X402Client,
    X402Config,
    create_402_response,
    PaymentRequiredError,
)

# 1. Configure the client with your recipient addresses
config = X402Config(
    recipient_evm="0xYourEVMWallet...",           # For Base, Ethereum, etc.
    recipient_solana="YourSolanaAddress...",      # For Solana, Fogo
    recipient_near="your-account.near",           # For NEAR
    recipient_stellar="G...YourStellarAddress",   # For Stellar
    recipient_algorand="YOUR_ALGO_ADDRESS...",    # For Algorand
    recipient_sui="0xYourSuiAddress...",          # For Sui
)

client = X402Client(config=config)

def handle_api_request(request):
    """
    Handle an API request that requires payment.
    """
    price = Decimal("1.00")  # $1.00 USD

    # Check if payment header exists
    x_payment = request.headers.get("X-PAYMENT")

    if not x_payment:
        # No payment provided - return 402 Payment Required
        return {
            "status": 402,
            "headers": {
                "Content-Type": "application/json",
            },
            "body": create_402_response(
                amount_usd=price,
                config=config,
                resource="/api/premium",
                description="Premium API access",
            )
        }

    # Payment provided - verify and settle
    try:
        result = client.process_payment(x_payment, price)

        # Payment successful!
        return {
            "status": 200,
            "body": {
                "success": True,
                "message": "Payment verified and settled!",
                "payer": result.payer_address,
                "network": result.network,
                "transaction_hash": result.transaction_hash,
                "amount_paid": str(result.amount),
            }
        }

    except PaymentRequiredError as e:
        # Payment verification failed
        return {
            "status": 402,
            "body": {"error": str(e)}
        }

# The 402 response includes payment options for all configured networks:
# {
#     "x402Version": 1,
#     "accepts": [
#         {"network": "base", "asset": "0x833589fCD...", "amount": "1000000", "payTo": "0xYour..."},
#         {"network": "solana", "asset": "EPjFWdd5...", "amount": "1000000", "payTo": "Your..."},
#         {"network": "near", "asset": "17208628...", "amount": "1000000", "payTo": "your.near"},
#         {"network": "stellar", "asset": "CCW67Q...", "amount": "10000000", "payTo": "G..."},
#         {"network": "algorand", "asset": "31566704", "amount": "1000000", "payTo": "YOUR..."},
#         {"network": "sui", "asset": "0xdba346...", "amount": "1000000", "payTo": "0xYour..."},
#     ],
#     "payTo": "0xYour...",
#     "maxAmountRequired": "1000000",
#     "resource": "/api/premium",
#     "description": "Premium API access"
# }
```

### Using the Decorator (Simpler)

For even simpler integration, use the `@require_payment` decorator:

```python
from decimal import Decimal
from uvd_x402_sdk import require_payment, configure_x402

# Configure once at startup
configure_x402(
    recipient_address="0xYourEVMWallet...",
    recipient_solana="YourSolanaAddress...",
)

@require_payment(amount_usd=Decimal("1.00"))
def premium_endpoint(payment_result):
    """
    This function only runs if payment is verified.
    Returns 402 automatically if no valid payment.
    """
    return {
        "data": "Premium content here!",
        "paid_by": payment_result.payer_address,
        "tx": payment_result.transaction_hash,
    }
```

## Supported Networks

| Network | Type | Chain ID | CAIP-2 | Status |
|---------|------|----------|--------|--------|
| Base | EVM | 8453 | `eip155:8453` | Active |
| Ethereum | EVM | 1 | `eip155:1` | Active |
| Polygon | EVM | 137 | `eip155:137` | Active |
| Arbitrum | EVM | 42161 | `eip155:42161` | Active |
| Optimism | EVM | 10 | `eip155:10` | Active |
| Avalanche | EVM | 43114 | `eip155:43114` | Active |
| Celo | EVM | 42220 | `eip155:42220` | Active |
| HyperEVM | EVM | 999 | `eip155:999` | Active |
| Unichain | EVM | 130 | `eip155:130` | Active |
| Monad | EVM | 143 | `eip155:143` | Active |
| Scroll | EVM | 534352 | `eip155:534352` | Active |
| SKALE | EVM | 1187947933 | `eip155:1187947933` | Active |
| SKALE Testnet | EVM | 324705682 | `eip155:324705682` | Active |
| Solana | SVM | - | `solana:5eykt...` | Active |
| Fogo | SVM | - | `solana:fogo` | Active |
| NEAR | NEAR | - | `near:mainnet` | Active |
| Stellar | Stellar | - | `stellar:pubnet` | Active |
| Algorand | Algorand | - | `algorand:mainnet` | Active |
| Algorand Testnet | Algorand | - | `algorand:testnet` | Active |
| Sui | Sui | - | `sui:mainnet` | Active |
| Sui Testnet | Sui | - | `sui:testnet` | Active |

### Supported Tokens

| Token | Networks | Decimals |
|-------|----------|----------|
| USDC | All networks | 6 |
| EURC | Ethereum, Base, Avalanche | 6 |
| AUSD | Ethereum, Arbitrum, Avalanche, Polygon, Monad, Sui | 6 |
| PYUSD | Ethereum | 6 |
| USDT | Ethereum, Arbitrum, Optimism, Avalanche, Polygon | 6 |

## Installation

```bash
# Core SDK (minimal dependencies)
pip install uvd-x402-sdk

# With framework support
pip install uvd-x402-sdk[flask]      # Flask integration
pip install uvd-x402-sdk[fastapi]    # FastAPI/Starlette integration
pip install uvd-x402-sdk[django]     # Django integration
pip install uvd-x402-sdk[aws]        # AWS Lambda helpers
pip install uvd-x402-sdk[algorand]   # Algorand atomic group helpers

# All integrations
pip install uvd-x402-sdk[all]
```

---

## Framework Examples

### Flask

```python
from decimal import Decimal
from flask import Flask, g, jsonify
from uvd_x402_sdk.integrations import FlaskX402

app = Flask(__name__)
x402 = FlaskX402(
    app,
    recipient_address="0xYourEVMWallet...",
    recipient_solana="YourSolanaAddress...",
    recipient_near="your-account.near",
    recipient_stellar="G...YourStellarAddress",
)

@app.route("/api/premium")
@x402.require_payment(amount_usd=Decimal("5.00"))
def premium():
    return jsonify({
        "message": "Premium content!",
        "payer": g.payment_result.payer_address,
        "tx": g.payment_result.transaction_hash,
        "network": g.payment_result.network,
    })

@app.route("/api/basic")
@x402.require_payment(amount_usd=Decimal("0.10"))
def basic():
    return jsonify({"data": "Basic tier data"})

if __name__ == "__main__":
    app.run(debug=True)
```

### FastAPI

```python
from decimal import Decimal
from fastapi import FastAPI, Depends
from uvd_x402_sdk.config import X402Config
from uvd_x402_sdk.models import PaymentResult
from uvd_x402_sdk.integrations import FastAPIX402

app = FastAPI()
x402 = FastAPIX402(
    app,
    recipient_address="0xYourEVMWallet...",
    recipient_solana="YourSolanaAddress...",
    recipient_near="your-account.near",
    recipient_stellar="G...YourStellarAddress",
)

@app.get("/api/premium")
async def premium(
    payment: PaymentResult = Depends(x402.require_payment(amount_usd="5.00"))
):
    return {
        "message": "Premium content!",
        "payer": payment.payer_address,
        "network": payment.network,
    }

@app.post("/api/generate")
async def generate(
    body: dict,
    payment: PaymentResult = Depends(x402.require_payment(amount_usd="1.00"))
):
    # Dynamic processing based on request
    return {"result": "generated", "payer": payment.payer_address}
```

### Django

```python
# settings.py
X402_FACILITATOR_URL = "https://facilitator.ultravioletadao.xyz"
X402_RECIPIENT_EVM = "0xYourEVMWallet..."
X402_RECIPIENT_SOLANA = "YourSolanaAddress..."
X402_RECIPIENT_NEAR = "your-account.near"
X402_RECIPIENT_STELLAR = "G...YourStellarAddress"
X402_PROTECTED_PATHS = {
    "/api/premium/": "5.00",
    "/api/basic/": "1.00",
}

MIDDLEWARE = [
    # ...other middleware...
    "uvd_x402_sdk.integrations.django_integration.DjangoX402Middleware",
]

# views.py
from django.http import JsonResponse
from uvd_x402_sdk.integrations import django_x402_required

@django_x402_required(amount_usd="5.00")
def premium_view(request):
    payment = request.payment_result
    return JsonResponse({
        "message": "Premium content!",
        "payer": payment.payer_address,
    })
```

### AWS Lambda

```python
import json
from decimal import Decimal
from uvd_x402_sdk.config import X402Config
from uvd_x402_sdk.integrations import LambdaX402

config = X402Config(
    recipient_evm="0xYourEVMWallet...",
    recipient_solana="YourSolanaAddress...",
    recipient_near="your-account.near",
    recipient_stellar="G...YourStellarAddress",
)
x402 = LambdaX402(config=config)

def handler(event, context):
    # Calculate price based on request
    body = json.loads(event.get("body", "{}"))
    quantity = body.get("quantity", 1)
    price = Decimal(str(quantity * 0.01))

    # Process payment or return 402
    result = x402.process_or_require(event, price)

    # If 402 response, return it
    if isinstance(result, dict) and "statusCode" in result:
        return result

    # Payment verified!
    return {
        "statusCode": 200,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({
            "success": True,
            "payer": result.payer_address,
            "tx": result.transaction_hash,
            "network": result.network,
            "quantity": quantity,
        })
    }
```

---

## Network-Specific Examples

### EVM Chains (Base, Ethereum, Polygon, etc.)

EVM chains use ERC-3009 `TransferWithAuthorization` with EIP-712 signatures.

```python
from uvd_x402_sdk import X402Client, X402Config

# Accept payments on Base and Ethereum only
config = X402Config(
    recipient_evm="0xYourEVMWallet...",
    supported_networks=["base", "ethereum"],
)

client = X402Client(config=config)
result = client.process_payment(x_payment_header, Decimal("10.00"))

# The payload contains EIP-712 signature + authorization
payload = client.extract_payload(x_payment_header)
evm_data = payload.get_evm_payload()
print(f"From: {evm_data.authorization.from_address}")
print(f"To: {evm_data.authorization.to}")
print(f"Value: {evm_data.authorization.value}")
```

### Solana & Fogo (SVM Chains)

SVM chains use partially-signed VersionedTransactions with SPL token transfers.

```python
from uvd_x402_sdk import X402Client, X402Config

# Accept payments on Solana and Fogo
config = X402Config(
    recipient_solana="YourSolanaAddress...",
    supported_networks=["solana", "fogo"],
)

client = X402Client(config=config)
result = client.process_payment(x_payment_header, Decimal("5.00"))

# The payload contains a base64-encoded VersionedTransaction
payload = client.extract_payload(x_payment_header)
svm_data = payload.get_svm_payload()
print(f"Transaction: {svm_data.transaction[:50]}...")

# Fogo has ultra-fast finality (~400ms)
if result.network == "fogo":
    print("Payment confirmed in ~400ms!")
```

### Stellar

Stellar uses Soroban Authorization Entries with fee-bump transactions.

```python
from uvd_x402_sdk import X402Client, X402Config

config = X402Config(
    recipient_stellar="G...YourStellarAddress",
    supported_networks=["stellar"],
)

client = X402Client(config=config)
result = client.process_payment(x_payment_header, Decimal("1.00"))

# Stellar uses 7 decimals (stroops)
payload = client.extract_payload(x_payment_header)
stellar_data = payload.get_stellar_payload()
print(f"From: {stellar_data.from_address}")
print(f"Amount (stroops): {stellar_data.amount}")
print(f"Token Contract: {stellar_data.tokenContract}")
```

### NEAR Protocol

NEAR uses NEP-366 meta-transactions with Borsh serialization.

```python
from uvd_x402_sdk import X402Client, X402Config

config = X402Config(
    recipient_near="your-recipient.near",
    supported_networks=["near"],
)

client = X402Client(config=config)
result = client.process_payment(x_payment_header, Decimal("2.00"))

# NEAR payload contains a SignedDelegateAction
payload = client.extract_payload(x_payment_header)
near_data = payload.get_near_payload()
print(f"SignedDelegateAction: {near_data.signedDelegateAction[:50]}...")

# Validate NEAR payload structure
from uvd_x402_sdk.networks.near import validate_near_payload
validate_near_payload(payload.payload)  # Raises ValueError if invalid
```

### Algorand

Algorand uses atomic groups with ASA (Algorand Standard Assets) transfers.

```python
from uvd_x402_sdk import X402Client, X402Config

config = X402Config(
    recipient_algorand="NCDSNUQ2QLXDMJXRALAW4CRUSSKG4IS37MVOFDQQPC45SE4EBZO42U6ZX4",
    supported_networks=["algorand"],
)

client = X402Client(config=config)
result = client.process_payment(x_payment_header, Decimal("1.00"))

# Algorand uses atomic groups: [fee_tx, payment_tx]
from uvd_x402_sdk.networks.algorand import (
    validate_algorand_payload,
    get_algorand_fee_payer,
    build_atomic_group,
)

# Get the facilitator fee payer address
fee_payer = get_algorand_fee_payer("algorand")
print(f"Fee payer: {fee_payer}")  # KIMS5H6Q...

# Validate payload structure
payload = client.extract_payload(x_payment_header)
validate_algorand_payload(payload.payload)  # Raises ValueError if invalid
```

#### Building Algorand Payments (requires `pip install uvd-x402-sdk[algorand]`)

```python
from uvd_x402_sdk.networks.algorand import (
    build_atomic_group,
    build_x402_payment_request,
    get_algorand_fee_payer,
)
from algosdk.v2client import algod

# Connect to Algorand node
client = algod.AlgodClient("", "https://mainnet-api.algonode.cloud")

# Build atomic group
payload = build_atomic_group(
    sender_address="YOUR_ADDRESS...",
    recipient_address="MERCHANT_ADDRESS...",
    amount=1000000,  # 1 USDC (6 decimals)
    asset_id=31566704,  # USDC ASA ID on mainnet
    facilitator_address=get_algorand_fee_payer("algorand"),
    sign_transaction=lambda txn: txn.sign(private_key),
    algod_client=client,
)

# Build x402 payment request
request = build_x402_payment_request(payload, network="algorand")
```

### Sui

Sui uses sponsored transactions with Move-based programmable transaction blocks.

```python
from uvd_x402_sdk import X402Client, X402Config

config = X402Config(
    recipient_sui="0xYourSuiAddress...",
    supported_networks=["sui"],
)

client = X402Client(config=config)
result = client.process_payment(x_payment_header, Decimal("1.00"))

# Sui payload contains a user-signed PTB that the facilitator sponsors
payload = client.extract_payload(x_payment_header)
sui_data = payload.get_sui_payload()
print(f"From: {sui_data.from_address}")
print(f"To: {sui_data.to}")
print(f"Amount: {sui_data.amount}")
print(f"Coin Object ID: {sui_data.coinObjectId}")
print(f"Transaction Bytes: {sui_data.transactionBytes[:50]}...")

# Sui uses sponsored transactions (user pays ZERO SUI for gas)
# Facilitator adds sponsor signature and pays gas fees
```

#### Sui-Specific Utilities

```python
from uvd_x402_sdk.networks.sui import (
    validate_sui_payload,
    is_valid_sui_address,
    is_valid_sui_coin_type,
    get_sui_fee_payer,
    get_sui_usdc_coin_type,
    get_sui_ausd_coin_type,
    SUI_FEE_PAYER_MAINNET,
    SUI_USDC_COIN_TYPE_MAINNET,
    SUI_AUSD_COIN_TYPE_MAINNET,
)

# Validate Sui addresses (0x + 64 hex chars)
assert is_valid_sui_address("0xe7bbf2b13f7d72714760aa16e024fa1b35a978793f9893d0568a4fbf356a764a")

# Validate coin types (package::module::type format)
assert is_valid_sui_coin_type(SUI_USDC_COIN_TYPE_MAINNET)

# Get fee payer (sponsor) address
fee_payer = get_sui_fee_payer("sui")  # Returns mainnet sponsor
print(f"Sui sponsor: {fee_payer}")

# Get USDC coin type
usdc_type = get_sui_usdc_coin_type("sui")
# '0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC'

# Get AUSD coin type (mainnet only)
ausd_type = get_sui_ausd_coin_type("sui")
# '0x2053d08c1e2bd02791056171aab0fd12bd7cd7efad2ab8f6b9c8902f14df2ff2::ausd::AUSD'

# Validate Sui payment payload
payload = client.extract_payload(x_payment_header)
validate_sui_payload(payload.payload)  # Raises ValueError if invalid
```

---

## x402 v1 vs v2

The SDK supports both x402 protocol versions with automatic detection.

### Version Differences

| Aspect | v1 | v2 |
|--------|----|----|
| Network ID | String (`"base"`) | CAIP-2 (`"eip155:8453"`) |
| Payment delivery | JSON body | `PAYMENT-REQUIRED` header |
| Multiple options | Limited | `accepts` array |
| Discovery | Implicit | Optional extension |

### Auto-Detection

```python
from uvd_x402_sdk import PaymentPayload

# The SDK auto-detects based on network format
payload = PaymentPayload(
    x402Version=1,
    scheme="exact",
    network="base",  # v1 format
    payload={"signature": "...", "authorization": {...}}
)

print(payload.is_v2())  # False

payload_v2 = PaymentPayload(
    x402Version=2,
    scheme="exact",
    network="eip155:8453",  # v2 CAIP-2 format
    payload={"signature": "...", "authorization": {...}}
)

print(payload_v2.is_v2())  # True

# Both work the same way
print(payload.get_normalized_network())  # "base"
print(payload_v2.get_normalized_network())  # "base"
```

### Creating v2 Responses

```python
from uvd_x402_sdk import X402Config, create_402_response_v2, Payment402BuilderV2

config = X402Config(
    recipient_evm="0xYourEVM...",
    recipient_solana="YourSolana...",
    recipient_near="your.near",
    recipient_stellar="G...Stellar",
)

# Simple v2 response
response = create_402_response_v2(
    amount_usd=Decimal("5.00"),
    config=config,
    resource="/api/premium",
    description="Premium API access",
)
# Returns:
# {
#     "x402Version": 2,
#     "scheme": "exact",
#     "resource": "/api/premium",
#     "accepts": [
#         {"network": "eip155:8453", "asset": "0x833...", "amount": "5000000", "payTo": "0xYour..."},
#         {"network": "solana:5eykt...", "asset": "EPjF...", "amount": "5000000", "payTo": "Your..."},
#         {"network": "near:mainnet", "asset": "1720...", "amount": "5000000", "payTo": "your.near"},
#         ...
#     ]
# }

# Builder pattern for more control
response = (
    Payment402BuilderV2(config)
    .amount(Decimal("10.00"))
    .resource("/api/generate")
    .description("AI generation credits")
    .networks(["base", "solana", "near"])  # Limit to specific networks
    .build()
)
```

---

## Payload Validation

Each network type has specific payload validation:

### EVM Validation

```python
from uvd_x402_sdk.models import EVMPayloadContent, EVMAuthorization

# Parse and validate EVM payload
payload = client.extract_payload(x_payment_header)
evm_data = payload.get_evm_payload()

# Validate authorization fields
auth = evm_data.authorization
assert auth.from_address.startswith("0x")
assert auth.to.startswith("0x")
assert int(auth.value) > 0
assert int(auth.validBefore) > int(auth.validAfter)
```

### SVM Validation

```python
from uvd_x402_sdk.networks.solana import validate_svm_payload, is_valid_solana_address

# Validate SVM payload
payload = client.extract_payload(x_payment_header)
validate_svm_payload(payload.payload)  # Raises ValueError if invalid

# Validate Solana addresses
assert is_valid_solana_address("YourSolanaAddress...")
```

### NEAR Validation

```python
from uvd_x402_sdk.networks.near import (
    validate_near_payload,
    is_valid_near_account_id,
    BorshSerializer,
)

# Validate NEAR payload
payload = client.extract_payload(x_payment_header)
validate_near_payload(payload.payload)  # Raises ValueError if invalid

# Validate NEAR account IDs
assert is_valid_near_account_id("your-account.near")
assert is_valid_near_account_id("0xultravioleta.near")
```

### Stellar Validation

```python
from uvd_x402_sdk.networks.stellar import (
    is_valid_stellar_address,
    is_valid_contract_address,
    stroops_to_usd,
)

# Validate Stellar addresses
assert is_valid_stellar_address("G...YourStellarAddress")  # G...
assert is_valid_contract_address("C...USDCContract")  # C...

# Convert stroops to USD (7 decimals)
usd = stroops_to_usd(50000000)  # Returns 5.0
```

---

## Configuration

### Environment Variables

```bash
# Core configuration
X402_FACILITATOR_URL=https://facilitator.ultravioletadao.xyz
X402_VERIFY_TIMEOUT=30
X402_SETTLE_TIMEOUT=55

# Recipient addresses (at least one required)
X402_RECIPIENT_EVM=0xYourEVMWallet
X402_RECIPIENT_SOLANA=YourSolanaAddress
X402_RECIPIENT_NEAR=your-account.near
X402_RECIPIENT_STELLAR=G...YourStellarAddress

# Optional
X402_FACILITATOR_SOLANA=F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq
X402_RESOURCE_URL=https://api.example.com
X402_DESCRIPTION=API access payment
```

### Programmatic Configuration

```python
from uvd_x402_sdk import X402Config, MultiPaymentConfig

# Full configuration
config = X402Config(
    facilitator_url="https://facilitator.ultravioletadao.xyz",

    # Recipients
    recipient_evm="0xYourEVMWallet",
    recipient_solana="YourSolanaAddress",
    recipient_near="your-account.near",
    recipient_stellar="G...YourStellarAddress",

    # Timeouts
    verify_timeout=30.0,
    settle_timeout=55.0,

    # Limit to specific networks
    supported_networks=["base", "solana", "near", "stellar"],

    # Metadata
    resource_url="https://api.example.com/premium",
    description="Premium API access",

    # Protocol version (1, 2, or "auto")
    x402_version="auto",
)

# From environment
config = X402Config.from_env()
```

---

## Facilitator Addresses

The SDK includes all facilitator addresses as embedded constants. You don't need to configure them manually.

### Fee Payer Addresses (Non-EVM)

Non-EVM chains require a fee payer address for gasless transactions:

```python
from uvd_x402_sdk import (
    # Algorand
    ALGORAND_FEE_PAYER_MAINNET,  # KIMS5H6QLCUDL65L5UBTOXDPWLMTS7N3AAC3I6B2NCONEI5QIVK7LH2C2I
    ALGORAND_FEE_PAYER_TESTNET,  # 5DPPDQNYUPCTXRZWRYSF3WPYU6RKAUR25F3YG4EKXQRHV5AUAI62H5GXL4

    # Solana
    SOLANA_FEE_PAYER_MAINNET,    # F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq
    SOLANA_FEE_PAYER_DEVNET,     # 6xNPewUdKRbEZDReQdpyfNUdgNg8QRc8Mt263T5GZSRv

    # Fogo
    FOGO_FEE_PAYER_MAINNET,      # F742C4VfFLQ9zRQyithoj5229ZgtX2WqKCSFKgH2EThq
    FOGO_FEE_PAYER_TESTNET,      # 6xNPewUdKRbEZDReQdpyfNUdgNg8QRc8Mt263T5GZSRv

    # NEAR
    NEAR_FEE_PAYER_MAINNET,      # uvd-facilitator.near
    NEAR_FEE_PAYER_TESTNET,      # uvd-facilitator.testnet

    # Stellar
    STELLAR_FEE_PAYER_MAINNET,   # GCHPGXJT2WFFRFCA5TV4G4E3PMMXLNIDUH27PKDYA4QJ2XGYZWGFZNHB
    STELLAR_FEE_PAYER_TESTNET,   # GBBFZMLUJEZVI32EN4XA2KPP445XIBTMTRBLYWFIL556RDTHS2OWFQ2Z

    # Sui
    SUI_FEE_PAYER_MAINNET,       # 0xe7bbf2b13f7d72714760aa16e024fa1b35a978793f9893d0568a4fbf356a764a
    SUI_FEE_PAYER_TESTNET,       # 0xabbd16a2fab2a502c9cfe835195a6fc7d70bfc27cffb40b8b286b52a97006e67

    # Helper function
    get_fee_payer,               # Get fee payer for any network
)

# Get fee payer for any network
fee_payer = get_fee_payer("algorand")  # Returns KIMS5H6Q...
fee_payer = get_fee_payer("solana")    # Returns F742C4VfF...
fee_payer = get_fee_payer("sui")       # Returns 0xe7bbf2b...
fee_payer = get_fee_payer("base")      # Returns None (EVM doesn't need fee payer)
```

### EVM Facilitator Addresses

EVM chains use EIP-3009 transferWithAuthorization (gasless by design), but the facilitator wallet addresses are available for reference:

```python
from uvd_x402_sdk import (
    EVM_FACILITATOR_MAINNET,  # 0x103040545AC5031A11E8C03dd11324C7333a13C7
    EVM_FACILITATOR_TESTNET,  # 0x34033041a5944B8F10f8E4D8496Bfb84f1A293A8
)
```

### Helper Functions

```python
from uvd_x402_sdk import (
    get_fee_payer,           # Get fee payer address for a network
    requires_fee_payer,      # Check if network needs fee payer
    get_all_fee_payers,      # Get all registered fee payers
    build_payment_info,      # Build payment info with auto feePayer
    DEFAULT_FACILITATOR_URL, # https://facilitator.ultravioletadao.xyz
)

# Check if network needs fee payer
requires_fee_payer("algorand")  # True
requires_fee_payer("base")      # False

# Build payment info with automatic fee payer
info = build_payment_info(
    network="algorand",
    pay_to="MERCHANT_ADDRESS...",
    max_amount_required="1000000",
    description="API access"
)
# info = {
#     'network': 'algorand',
#     'payTo': 'MERCHANT_ADDRESS...',
#     'maxAmountRequired': '1000000',
#     'description': 'API access',
#     'asset': '31566704',
#     'extra': {
#         'token': 'usdc',
#         'feePayer': 'KIMS5H6QLCUDL65L5UBTOXDPWLMTS7N3AAC3I6B2NCONEI5QIVK7LH2C2I'
#     }
# }
```

---

## Registering Custom Networks

```python
from uvd_x402_sdk.networks import NetworkConfig, NetworkType, register_network

# Register a custom EVM network
custom_chain = NetworkConfig(
    name="mychain",
    display_name="My Custom Chain",
    network_type=NetworkType.EVM,
    chain_id=12345,
    usdc_address="0xUSDCContractAddress",
    usdc_decimals=6,
    usdc_domain_name="USD Coin",  # Check actual EIP-712 domain!
    usdc_domain_version="2",
    rpc_url="https://rpc.mychain.com",
    enabled=True,
)

register_network(custom_chain)

# Now you can use it
config = X402Config(
    recipient_evm="0xYourWallet...",
    supported_networks=["base", "mychain"],
)
```

---

## Multi-Token Support

The SDK supports 6 stablecoins on EVM chains. Use the token helper functions to query and work with different tokens.

### Querying Token Support

```python
from uvd_x402_sdk import (
    TokenType,
    get_token_config,
    get_supported_tokens,
    is_token_supported,
    get_networks_by_token,
)

# Check which tokens a network supports
tokens = get_supported_tokens("ethereum")
print(tokens)  # ['usdc', 'eurc', 'ausd', 'pyusd']

tokens = get_supported_tokens("base")
print(tokens)  # ['usdc', 'eurc']

# Check if a specific token is supported
if is_token_supported("ethereum", "eurc"):
    print("EURC is available on Ethereum!")

# Get token configuration
config = get_token_config("ethereum", "eurc")
if config:
    print(f"EURC address: {config.address}")
    print(f"Decimals: {config.decimals}")
    print(f"EIP-712 name: {config.name}")
    print(f"EIP-712 version: {config.version}")

# Find all networks that support a token
networks = get_networks_by_token("eurc")
for network in networks:
    print(f"EURC available on: {network.display_name}")
# Output: EURC available on: Ethereum, Base, Avalanche C-Chain
```

### Token Configuration

Each token has specific EIP-712 domain parameters required for signing:

```python
from uvd_x402_sdk import TokenConfig, get_token_config

# TokenConfig structure
# - address: Contract address
# - decimals: Token decimals (6 for all supported stablecoins)
# - name: EIP-712 domain name (e.g., "USD Coin", "EURC", "Gho Token")
# - version: EIP-712 domain version

# Example: Get EURC config on Base
eurc = get_token_config("base", "eurc")
# TokenConfig(
#     address="0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
#     decimals=6,
#     name="EURC",
#     version="2"
# )

# Example: Get PYUSD config on Ethereum
pyusd = get_token_config("ethereum", "pyusd")
# TokenConfig(
#     address="0x6c3ea9036406852006290770BEdFcAbA0e23A0e8",
#     decimals=6,
#     name="PayPal USD",
#     version="1"
# )
```

### Available Tokens

| Token | Description | Decimals | Issuer |
|-------|-------------|----------|--------|
| `usdc` | USD Coin | 6 | Circle |
| `eurc` | Euro Coin | 6 | Circle |
| `ausd` | Agora USD | 6 | Agora Finance |
| `pyusd` | PayPal USD | 6 | PayPal/Paxos |

### Critical Implementation Notes

#### EIP-712 Domain Names Vary by Chain

The same token may use **different EIP-712 domain names on different chains**. This affects signature verification.

| Token | Ethereum | Base | Avalanche |
|-------|----------|------|-----------|
| EURC | `"Euro Coin"` | `"EURC"` | `"Euro Coin"` |
| USDC | `"USD Coin"` | `"USD Coin"` | `"USD Coin"` |
| AUSD | `"Agora Dollar"` | N/A | `"Agora Dollar"` |
| PYUSD | `"PayPal USD"` | N/A | N/A |

**Important:** Always use `get_token_config()` to get the correct domain name. Never hardcode domain names.

```python
# CORRECT: Use get_token_config for each chain
eurc_base = get_token_config("base", "eurc")
# TokenConfig(name="EURC", version="2", ...)

eurc_ethereum = get_token_config("ethereum", "eurc")
# TokenConfig(name="Euro Coin", version="2", ...)
```

#### PYUSD Signature Format (PayPal USD)

PYUSD uses the Paxos implementation which only supports the **v,r,s signature variant** of `transferWithAuthorization`. This is different from Circle's USDC/EURC which support both compact bytes and v,r,s variants.

**Backend implications:**
- The x402 facilitator (v1.9.0+) automatically handles this by detecting PYUSD and using `transferWithAuthorization_1(v,r,s)` instead of `transferWithAuthorization_0(bytes signature)`
- If using a custom facilitator, ensure it supports the v,r,s variant for PYUSD

#### Token Info Must Be Passed to Facilitator

When using non-USDC tokens, your backend **must** pass the token info (including EIP-712 domain) to the facilitator. This is done via the `extra` field in `paymentRequirements`:

```python
# When building payment requirements for the facilitator
payment_requirements = {
    "asset": token_address,  # Use actual token address, NOT hardcoded USDC
    "extra": {
        "name": token_config.name,     # EIP-712 domain name
        "version": token_config.version,  # EIP-712 domain version
    }
}
```

Without this, the facilitator will use wrong EIP-712 domain and signature verification will fail with "invalid signature" error.

---

## Error Handling

```python
from uvd_x402_sdk.exceptions import (
    X402Error,
    PaymentRequiredError,
    PaymentVerificationError,
    PaymentSettlementError,
    UnsupportedNetworkError,
    InvalidPayloadError,
    FacilitatorError,
    X402TimeoutError,
)

try:
    result = client.process_payment(header, amount)
except PaymentVerificationError as e:
    # Signature invalid, amount mismatch, expired, etc.
    print(f"Verification failed: {e.reason}")
    print(f"Errors: {e.errors}")
except PaymentSettlementError as e:
    # On-chain settlement failed (insufficient balance, nonce used, etc.)
    print(f"Settlement failed on {e.network}: {e.message}")
except UnsupportedNetworkError as e:
    # Network not recognized or disabled
    print(f"Network {e.network} not supported")
    print(f"Supported: {e.supported_networks}")
except InvalidPayloadError as e:
    # Malformed X-PAYMENT header
    print(f"Invalid payload: {e.message}")
except FacilitatorError as e:
    # Facilitator returned error
    print(f"Facilitator error: {e.status_code} - {e.response_body}")
except X402TimeoutError as e:
    # Request timed out
    print(f"{e.operation} timed out after {e.timeout_seconds}s")
except X402Error as e:
    # Catch-all for x402 errors
    print(f"Payment error: {e.message}")
```

---

## ERC-8004 Trustless Agents

Build verifiable on-chain reputation for AI agents and services.

```python
from uvd_x402_sdk import Erc8004Client

async with Erc8004Client() as client:
    # Get agent identity
    identity = await client.get_identity("ethereum", 42)
    print(f"Agent URI: {identity.agent_uri}")

    # Get agent reputation
    reputation = await client.get_reputation("ethereum", 42)
    print(f"Score: {reputation.summary.summary_value}")

    # Submit feedback after payment
    result = await client.submit_feedback(
        network="ethereum",
        agent_id=42,
        value=95,
        tag1="quality",
        proof=settle_response.proof_of_payment,
    )

    # Respond to feedback (agents only)
    await client.append_response(
        network="ethereum",
        agent_id=42,
        feedback_index=1,
        response_text="Thank you for your feedback!",
    )
```

---

## Escrow & Refunds

Hold payments in escrow with dispute resolution.

```python
from uvd_x402_sdk import EscrowClient

async with EscrowClient() as client:
    # Create escrow payment
    escrow = await client.create_escrow(
        payment_header=request.headers["X-PAYMENT"],
        requirements=payment_requirements,
        escrow_duration=86400,  # 24 hours
    )

    # Release after service delivery
    await client.release(escrow.id)

    # Or request refund if service failed
    await client.request_refund(
        escrow_id=escrow.id,
        reason="Service not delivered",
    )
```

---

## How x402 Works

The x402 protocol enables gasless stablecoin payments (USDC, EURC, AUSD, PYUSD):

```
1. User Request     -->  Client sends request without payment
2. 402 Response     <--  Server returns payment requirements
3. User Signs       -->  Wallet signs authorization (NO GAS!)
4. Frontend Sends   -->  X-PAYMENT header with signed payload
5. SDK Verifies     -->  Validates signature with facilitator
6. SDK Settles      -->  Facilitator executes on-chain transfer
7. Success          <--  Payment confirmed, request processed
```

The facilitator (https://facilitator.ultravioletadao.xyz) handles all on-chain interactions and pays gas fees on behalf of users.

### Payment Flow by Network Type

| Network Type | User Signs | Facilitator Does |
|--------------|-----------|------------------|
| EVM | EIP-712 message | Calls `transferWithAuthorization()` |
| SVM | Partial transaction | Co-signs + submits transaction |
| NEAR | DelegateAction (Borsh) | Wraps in `Action::Delegate` |
| Stellar | Auth entry (XDR) | Wraps in fee-bump transaction |
| Algorand | ASA transfer tx | Signs fee tx + submits atomic group |
| Sui | Programmable tx block | Sponsors gas + submits transaction |

---

## Error Codes

| Exception | Description |
|-----------|-------------|
| `PaymentRequiredError` | No payment header provided |
| `PaymentVerificationError` | Signature invalid, amount mismatch, expired |
| `PaymentSettlementError` | On-chain settlement failed |
| `UnsupportedNetworkError` | Network not recognized or disabled |
| `InvalidPayloadError` | Malformed X-PAYMENT header |
| `FacilitatorError` | Facilitator service error |
| `ConfigurationError` | Invalid SDK configuration |
| `X402TimeoutError` | Request timed out |

---

## Security

- Users **NEVER** pay gas or submit transactions directly
- **EVM**: Users sign EIP-712 structured messages for any supported stablecoin (USDC, EURC, AUSD, PYUSD)
- **Solana/Fogo**: Users sign partial transactions (facilitator co-signs and submits)
- **Stellar**: Users sign Soroban authorization entries only
- **NEAR**: Users sign NEP-366 meta-transactions (DelegateAction)
- **Sui**: Users sign programmable transaction blocks (facilitator sponsors gas)
- The facilitator submits and pays for all on-chain transactions
- All signatures include expiration timestamps (`validBefore`) for replay protection
- Nonces prevent double-spending of authorizations
- Each token has verified contract addresses and EIP-712 domain parameters

---

## Troubleshooting

### Common Issues

**"Unsupported network"**
- Check that the network is in `supported_networks`
- Verify the network is enabled
- For v2, ensure CAIP-2 format is correct

**"Payment verification failed"**
- Amount mismatch between expected and signed
- Recipient address mismatch
- Authorization expired (`validBefore` in the past)
- Nonce already used (replay attack protection)

**"Settlement timed out"**
- Network congestion - increase `settle_timeout`
- Facilitator under load - retry after delay

**"Invalid payload"**
- Check base64 encoding of X-PAYMENT header
- Verify JSON structure matches expected format
- Ensure `x402Version` is 1 or 2

### Debug Logging

```python
import logging
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("uvd_x402_sdk").setLevel(logging.DEBUG)
```

---

## Development

```bash
# Clone and install
git clone https://github.com/UltravioletaDAO/uvd-x402-sdk-python
cd uvd-x402-sdk-python
pip install -e ".[dev]"

# Run tests
pytest

# Format code
black src tests
ruff check src tests

# Type checking
mypy src
```

---

## Links

- [x402 Protocol](https://x402.org)
- [Ultravioleta DAO](https://ultravioletadao.xyz)
- [402milly](https://402milly.xyz)
- [GitHub](https://github.com/UltravioletaDAO/uvd-x402-sdk-python)
- [PyPI](https://pypi.org/project/uvd-x402-sdk/)
- [TypeScript SDK](https://github.com/UltravioletaDAO/uvd-x402-sdk-typescript)

---

## License

MIT License - see LICENSE file.

---

## Changelog

### v0.6.0 (2026-01-30)

- **ERC-8004 Trustless Agents**: Full client for on-chain reputation system
  - `Erc8004Client` class with identity, reputation, and feedback methods
  - `append_response()` method for agents to respond to feedback
  - `ProofOfPayment` model for reputation submission authorization
  - `build_erc8004_payment_requirements()` helper
- **Escrow & Refund Support**: Complete escrow payment flow
  - `EscrowClient` class with create, release, refund, dispute methods
  - `EscrowPayment`, `RefundRequest`, `Dispute` models
  - Helper functions: `can_release_escrow()`, `can_refund_escrow()`, etc.
- **New Networks**: Scroll (534352) and SKALE (1187947933, testnet: 324705682)
  - SKALE is gasless L3 with sFUEL
  - Scroll is zkEVM Layer 2
- SDK now supports 21 blockchain networks

### v0.5.6 (2025-12-31)

- Added `SuiPayloadContent` Pydantic model for Sui sponsored transactions
- Added `coinObjectId` as required field (CRITICAL for facilitator deserialization)
- Added `get_sui_payload()` method to `PaymentPayload`
- Updated `validate_sui_payload()` to require `coinObjectId`

### v0.5.5 (2025-12-30)

- Added AUSD (Agora USD) support for Sui mainnet
- Added `SUI_AUSD_COIN_TYPE_MAINNET` constant
- Added `get_sui_ausd_coin_type()` helper function

### v0.5.4 (2025-12-30)

- **Sui Blockchain Support**: Added Sui mainnet and testnet networks
- Added `NetworkType.SUI` for Sui Move VM chains
- Added `SUI_FEE_PAYER_MAINNET` and `SUI_FEE_PAYER_TESTNET` sponsor addresses
- Added CAIP-2 support for `sui:mainnet` and `sui:testnet`
- Added Sui-specific utilities: `validate_sui_payload()`, `is_valid_sui_address()`, `is_valid_sui_coin_type()`
- SDK now supports 18 blockchain networks

### v0.5.3 (2025-12-27)

- Documentation updates for Algorand support
- Updated README with facilitator addresses and changelog

### v0.5.2 (2025-12-26)

- Added EVM facilitator addresses for reference
  - `EVM_FACILITATOR_MAINNET`: 0x103040545AC5031A11E8C03dd11324C7333a13C7
  - `EVM_FACILITATOR_TESTN