Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5bc5af3
Fix x402 MCP balance argument validation
Mar 14, 2026
3da924f
Bump x402 MCP to beta.10
Mar 14, 2026
b1926a7
Resolve EVM Permit2 addresses by network
Mar 14, 2026
f88e8be
Align EVM permit2 witness with deployed proxies
Mar 14, 2026
0b2ebf9
feat(python-sdk): add payload context and gas-sponsoring extensions
Hades-Ye Mar 17, 2026
d9f2dc6
feat(python-sdk): implement evm permit2 with sponsoring extensions
Hades-Ye Mar 17, 2026
28362c2
feat(python-sdk): align tron exact flow with evm and add permit2
Hades-Ye Mar 17, 2026
e8d63bf
test(python-sdk): cover evm/tron permit2 and sponsoring extensions
Hades-Ye Mar 17, 2026
ee30365
test(e2e): add evm/tron permit2 coverage across python and ts stacks
Hades-Ye Mar 17, 2026
917f802
fix(python-sdk): restore payload API compatibility and resolve CI fai…
Hades-Ye Mar 17, 2026
ffbcc59
style(python-sdk): apply ruff formatting for ci
Hades-Ye Mar 17, 2026
9db1c42
refactor(tron): rename transfer method to transferWithAuthorization
Hades-Ye Mar 18, 2026
4b72d63
chore(e2e): drop tronpy_smoke script
Hades-Ye Mar 18, 2026
212b7ea
fix(evm): require permit2 facilitator across ts/python
Hades-Ye Mar 18, 2026
3f993d2
fix(tron): extend receipt wait to 120s
Hades-Ye Mar 19, 2026
b83cd1c
fix(evm): estimate fees for permit2 approval
Hades-Ye Mar 19, 2026
4247c96
fix(e2e): ts facilitator tron support
Hades-Ye Mar 19, 2026
3ab6fbd
chore(e2e): extend client/server timeouts
Hades-Ye Mar 19, 2026
64dfb31
feat(evm): resolve rpc by network with chain overrides
Hades-Ye Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions e2e/.env-local
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
# 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=
# E2E Environment Template
# Copy to .env and fill in real values.

# Client wallets
CLIENT_EVM_PRIVATE_KEY=0x...
CLIENT_SVM_PRIVATE_KEY=...
CLIENT_APTOS_PRIVATE_KEY=...
CLIENT_STELLAR_PRIVATE_KEY=...
CLIENT_TRON_PRIVATE_KEY=...

# Server payment addresses
SERVER_EVM_ADDRESS=0x...
SERVER_SVM_ADDRESS=...
SERVER_APTOS_ADDRESS=...
SERVER_STELLAR_ADDRESS=...
SERVER_TRON_ADDRESS=...
EVM_FACILITATOR_ADDRESS=0x...
TRON_FACILITATOR_ADDRESS=...

# Facilitator wallets
FACILITATOR_EVM_PRIVATE_KEY=0x...
FACILITATOR_SVM_PRIVATE_KEY=...
FACILITATOR_APTOS_PRIVATE_KEY=...
FACILITATOR_STELLAR_PRIVATE_KEY=...
FACILITATOR_TRON_PRIVATE_KEY=...

# Optional network overrides
# EVM_NETWORK=eip155:97
# TRON_NETWORK=tron:nile

# Optional RPC overrides
# BSC_TESTNET_RPC_URL=https://bsc-testnet-rpc.publicnode.com
# TRON_NILE_RPC_URL=https://nile.trongrid.io
# SOLANA_DEVNET_RPC_URL=https://api.devnet.solana.com
# APTOS_TESTNET_RPC_URL=https://fullnode.testnet.aptoslabs.com/v1
# STELLAR_TESTNET_RPC_URL=https://soroban-testnet.stellar.org
3 changes: 3 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,15 @@ 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
SERVER_TRON_ADDRESS=... # Where servers receive TRON payments
TRON_FACILITATOR_ADDRESS=... # TRON facilitator address for Permit2 (base58)

# Facilitator wallets (for payment verification/settlement)
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
FACILITATOR_TRON_PRIVATE_KEY=... # TRON private key for facilitator
```

### Account Setup Instructions
Expand Down
23 changes: 22 additions & 1 deletion e2e/clients/axios/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { config } from "dotenv";
import axios from "axios";
import { wrapAxiosWithPayment, decodePaymentResponseHeader } from "@bankofai/x402-axios";
import { decodePaymentRequiredHeader } from "@bankofai/x402-core/http";
import { createPublicClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { bscTestnet } from "viem/chains";
Expand Down Expand Up @@ -107,11 +108,31 @@ axiosWithPayment
process.exit(0);
})
.catch(error => {
const status = error.response?.status || 500;
const headers = error.response?.headers || {};
const paymentRequiredHeader = headers["payment-required"] || headers["x-payment"];
const paymentResponseHeader = headers["payment-response"] || headers["x-payment-response"];

let paymentRequired = undefined;
let paymentResponse = undefined;
if (paymentRequiredHeader) {
try {
paymentRequired = decodePaymentRequiredHeader(paymentRequiredHeader);
} catch {}
}
if (paymentResponseHeader) {
try {
paymentResponse = decodePaymentResponseHeader(paymentResponseHeader);
} catch {}
}

console.error(
JSON.stringify({
success: false,
error: error.message || "Request failed",
status_code: error.response?.status || 500,
status_code: status,
payment_required: paymentRequired,
payment_response: paymentResponse,
}),
);
process.exit(1);
Expand Down
26 changes: 23 additions & 3 deletions e2e/clients/httpx/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@

# Import from new x402 package
from bankofai.x402 import x402Client
from bankofai.x402.http import decode_payment_response_header
from bankofai.x402.http import decode_payment_response_header, decode_payment_required_header
from bankofai.x402.http.clients import x402_httpx_transport
from bankofai.x402.mechanisms.evm import EthAccountSigner
from bankofai.x402.mechanisms.evm.exact import register_exact_evm_client
from bankofai.x402.mechanisms.svm import KeypairSigner
from bankofai.x402.mechanisms.svm.exact import register_exact_svm_client
from bankofai.x402.mechanisms.tron.signers import ClientTronSigner
from bankofai.x402.mechanisms.tron.exact import register_exact_tron_client
import httpx

# Load environment variables
Expand All @@ -22,6 +24,8 @@
# Get environment variables
evm_private_key = os.getenv("EVM_PRIVATE_KEY")
svm_private_key = os.getenv("SVM_PRIVATE_KEY")
tron_private_key = os.getenv("TRON_PRIVATE_KEY")
tron_rpc_url = os.getenv("TRON_RPC_URL")
base_url = os.getenv("RESOURCE_SERVER_URL")
endpoint_path = os.getenv("ENDPOINT_PATH")

Expand Down Expand Up @@ -54,9 +58,17 @@ async def main():
svm_signer = KeypairSigner.from_base58(svm_private_key)
register_exact_svm_client(client, svm_signer)

# Register TRON exact scheme if private key is available
if tron_private_key:
tron_signer = ClientTronSigner(
private_key=tron_private_key,
full_node=tron_rpc_url or "https://nile.trongrid.io",
)
register_exact_tron_client(client, tron_signer)

# Create httpx client with x402 payment transport and increased timeout
# Set timeout to 30 seconds to handle busy servers during test runs
timeout = httpx.Timeout(30.0, connect=10.0)
# Set timeout to 90 seconds to handle slow on-chain settlement during test runs
timeout = httpx.Timeout(90.0, connect=10.0)
async with httpx.AsyncClient(
base_url=base_url,
timeout=timeout,
Expand All @@ -76,6 +88,7 @@ async def main():
"data": response_data,
"status_code": response.status_code,
"payment_response": None,
"payment_required": None,
}

# Check for payment response header (V2: PAYMENT-RESPONSE, V1: X-PAYMENT-RESPONSE)
Expand All @@ -86,6 +99,13 @@ async def main():
payment_response = decode_payment_response_header(payment_header)
result["payment_response"] = payment_response.model_dump()

payment_required_header = response.headers.get("PAYMENT-REQUIRED") or response.headers.get(
"X-PAYMENT"
)
if payment_required_header:
payment_required = decode_payment_required_header(payment_required_header)
result["payment_required"] = payment_required.model_dump()

# Output structured result as JSON for proxy to parse
print(json.dumps(result))
exit(0)
Expand Down
2 changes: 1 addition & 1 deletion e2e/clients/httpx/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description = "Python httpx client for x402 e2e tests"
requires-python = ">=3.10"
dependencies = [
"python-dotenv>=1.0.0",
"bankofai.x402[httpx,evm,svm,extensions]"
"bankofai.x402[httpx,evm,svm,tron,extensions]"
]

[build-system]
Expand Down
14 changes: 10 additions & 4 deletions e2e/clients/httpx/test.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
"language": "python",
"protocolFamilies": [
"evm",
"svm"
"svm",
"tron"
],
"x402Versions": [
1,
2
],
"evm": {
"transferMethods": ["eip3009"]
"transferMethods": ["eip3009", "permit2"]
},
"tron": {
"transferMethods": ["transferWithAuthorization", "permit2"]
},
"description": "Python httpx client with x402 v2 payment hooks",
"environment": {
Expand All @@ -21,7 +25,9 @@
],
"optional": [
"EVM_PRIVATE_KEY",
"SVM_PRIVATE_KEY"
"SVM_PRIVATE_KEY",
"TRON_PRIVATE_KEY",
"TRON_RPC_URL"
]
}
}
}
Loading
Loading