Potential settlement-ordering issue: paid resource can be released without completed settlement
Hi, I noticed a possible payment-flow issue while reviewing the current repository state. This is a conservative report based on the visible code path, and I may be missing deployment-specific guards outside this repository.
Reviewed HEAD: bf1f63de9018
What I observed
In the visible payment path, I observed:
x402 gateway advertises paid MCP tools and per-tool prices; middleware verifies X-Payment then calls next() without invoking settle(), while PaymentVerifier has an unused settle() method. The verifier also returns cached positive results before rechecking expectedAmount/payTo/network, and its cache key excludes tool/resource/amount, allowing a previously verified lower-priced payment to be reused within TTL against a different paid tool.
Relevant code locations:
README.md
src/gateway/x402-server.ts
src/gateway/payment-verifier.ts
src/gateway/pricing.ts
Relevant code excerpts
README.md:200-224
200
201 - 🔄 **Swap/DEX** - Token swaps via 1inch, 0x, ParaSwap
202 - 🌉 **Bridge** - Cross-chain transfers via LayerZero, Stargate, Wormhole
203 - ⛽ **Gas** - Gas prices across chains, EIP-1559 suggestions
204 - 📦 **Multicall** - Batch read/write operations
205 - 📊 **Events/Logs** - Query historical events, decode logs
206 - 🔒 **Security** - Rug pull detection, honeypot check, GoPlus token/address security, dApp phishing detection
207 - 💰 **Staking** - Liquid staking (Lido), LP farming
208 - ✍️ **Signatures** - Sign messages, verify signatures, EIP-712
209 - 🏦 **Lending** - Aave/Compound positions, borrow rates
210 - 📈 **Price Feeds** - Historical prices, TWAP, oracle aggregation
211 - 📁 **Portfolio** - Track holdings across chains
212 - 🏛️ **Governance** - Snapshot votes, on-chain proposals
213 - 🚀 **Deployment** - Deploy contracts, CREATE2, upgradeable proxies, verification
214 - 🛡️ **MEV Protection** - Flashbots Protect, private transactions, bundle simulation
215 - 🆔 **ENS/Domains** - Register, transfer, renew, set records, subdomains
216 - 📊 **Market Data** - CoinGecko & CoinStats prices, OHLCV, trending, categories, exchanges
217 - 🌐 **DeFi Analytics** - DefiLlama TVL, yields, fees, bridges, stablecoins, protocol data
218 - 💬 **Social Sentiment** - LunarCrush social metrics, influencers, trending topics
219 - 📈 **DEX Analytics** - DexPaprika & GeckoTerminal pools, trades, OHLCV, trending tokens
220 - 🔮 **Predictions** - Polymarket prediction markets, crypto forecasts
221 - 📉 **Technical Indicators** - 50+ indicators (RSI, MACD, Bollinger Bands, etc.)
222 - 🔔 **Alerts** - Price alerts, whale movement alerts, gas alerts (NEW)
223 - 📡 **WebSockets** - Real-time price streams, trade feeds, mempool monitoring (NEW)
224 - 🐋 **Wallet Analytics** - Whale tracking, wallet scoring, behavior analysis (NEW)
src/gateway/x402-server.ts:98-122
98 network: NETWORK,
99 payTo: PAY_TO_ADDRESS,
100 })
101 }
102
103 // Verify payment
104 try {
105 const paymentData = JSON.parse(Buffer.from(paymentSignature, "base64").toString())
106 const verification = await paymentVerifier.verify(paymentData, {
107 expectedAmount: pricing.priceUSDC,
108 expectedPayTo: PAY_TO_ADDRESS,
109 expectedNetwork: NETWORK,
110 })
111
112 if (!verification.valid) {
113 return res.status(402).json({
114 error: "Payment Invalid",
115 message: verification.reason || "Payment verification failed",
116 })
117 }
118
119 // Rate limiting check (even with valid payment)
120 const clientId = paymentData.from || req.ip || "anonymous"
121 const rateLimitResult = await rateLimiter.check(clientId, toolName)
122
src/gateway/payment-verifier.ts:241-265
241 headers: {
242 "Content-Type": "application/json",
243 },
244 body: JSON.stringify({ payment }),
245 })
246
247 if (!response.ok) {
248 const error = await response.text()
249 return { valid: false, reason: `Settlement failed: ${error}` }
250 }
251
252 const result = await response.json()
253 return {
254 valid: result.success === true,
255 transactionHash: result.transactionHash,
256 settledAt: result.settledAt,
257 }
258 } catch (error) {
259 console.error("Settlement request failed:", error)
260 return { valid: false, reason: "Settlement request failed" }
261 }
262 }
263
264 /**
265 * Generate cache key for a payment
src/gateway/pricing.ts:59-83
59 category: "free",
60 },
61
62 // ═══════════════════════════════════════════════════════════════
63 // MICRO TIER ($0.0001) - High-volume reads
64 // ═══════════════════════════════════════════════════════════════
65 "get_balance": {
66 priceUSDC: "100",
67 description: "Get wallet balance",
68 rateLimit: 60,
69 category: "read",
70 },
71 "get_token_balance": {
72 priceUSDC: "100",
73 description: "Get ERC-20 token balance",
74 rateLimit: 60,
75 category: "read",
76 },
77 "get_transaction": {
78 priceUSDC: "100",
79 description: "Get transaction details",
80 rateLimit: 60,
81 category: "read",
82 },
83 "get_transaction_receipt": {
Why this may matter
If the paid resource is returned after verification but before settlement is completed or enforced, a client may receive the protected result without a confirmed transfer.
Suggested check
Consider making the paid-resource path depend on server-trusted payment requirements and a completed payment state. In particular, re-check recipient/payTo, amount, asset, network, nonce/idempotency, resource binding, and settlement result at the exact point where the protected API/tool/content is released.
Conservative caveat
I only reviewed the code visible in this repository at the HEAD above. If deployment-specific middleware or an upstream service enforces the missing binding/settlement invariant, this may already be mitigated there.
Potential settlement-ordering issue: paid resource can be released without completed settlement
Hi, I noticed a possible payment-flow issue while reviewing the current repository state. This is a conservative report based on the visible code path, and I may be missing deployment-specific guards outside this repository.
Reviewed HEAD:
bf1f63de9018What I observed
In the visible payment path, I observed:
Relevant code locations:
README.mdsrc/gateway/x402-server.tssrc/gateway/payment-verifier.tssrc/gateway/pricing.tsRelevant code excerpts
README.md:200-224src/gateway/x402-server.ts:98-122src/gateway/payment-verifier.ts:241-265src/gateway/pricing.ts:59-83Why this may matter
If the paid resource is returned after verification but before settlement is completed or enforced, a client may receive the protected result without a confirmed transfer.
Suggested check
Consider making the paid-resource path depend on server-trusted payment requirements and a completed payment state. In particular, re-check recipient/payTo, amount, asset, network, nonce/idempotency, resource binding, and settlement result at the exact point where the protected API/tool/content is released.
Conservative caveat
I only reviewed the code visible in this repository at the HEAD above. If deployment-specific middleware or an upstream service enforces the missing binding/settlement invariant, this may already be mitigated there.