From 2314ece9d12f3c29c74a130657024fa5d2ea7af6 Mon Sep 17 00:00:00 2001 From: tinkle-community Date: Thu, 12 Mar 2026 14:29:42 +0800 Subject: [PATCH] fix: disable outer retry for x402 payment providers to prevent duplicate charges The outer retry loop in client.go re-initiates the entire x402 payment flow on each attempt, causing duplicate USDC charges. The inner x402 retry loop (5 attempts with re-signing) already handles all retryable scenarios. Set MaxRetries=1 for Claw402, BlockRunBase, and BlockRunSol to ensure only one payment per AI decision. --- mcp/payment/blockrun_base.go | 1 + mcp/payment/blockrun_sol.go | 1 + mcp/payment/claw402.go | 1 + 3 files changed, 3 insertions(+) diff --git a/mcp/payment/blockrun_base.go b/mcp/payment/blockrun_base.go index edc27ad5..e7394f54 100644 --- a/mcp/payment/blockrun_base.go +++ b/mcp/payment/blockrun_base.go @@ -73,6 +73,7 @@ func NewBlockRunBaseClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient { mcp.WithModel(DefaultBlockRunModel), mcp.WithBaseURL(DefaultBlockRunBaseURL), mcp.WithTimeout(X402Timeout), + mcp.WithMaxRetries(1), // disable outer retry — inner x402 loop handles retries; outer retry causes duplicate payments } allOpts := append(baseOpts, opts...) baseClient := mcp.NewClient(allOpts...).(*mcp.Client) diff --git a/mcp/payment/blockrun_sol.go b/mcp/payment/blockrun_sol.go index 3cad783e..95a3e75f 100644 --- a/mcp/payment/blockrun_sol.go +++ b/mcp/payment/blockrun_sol.go @@ -53,6 +53,7 @@ func NewBlockRunSolClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient { mcp.WithModel(DefaultBlockRunModel), mcp.WithBaseURL(DefaultBlockRunSolURL), mcp.WithTimeout(X402Timeout), + mcp.WithMaxRetries(1), // disable outer retry — inner x402 loop handles retries; outer retry causes duplicate payments } allOpts := append(baseOpts, opts...) baseClient := mcp.NewClient(allOpts...).(*mcp.Client) diff --git a/mcp/payment/claw402.go b/mcp/payment/claw402.go index 90713f59..623d70fb 100644 --- a/mcp/payment/claw402.go +++ b/mcp/payment/claw402.go @@ -70,6 +70,7 @@ func NewClaw402ClientWithOptions(opts ...mcp.ClientOption) mcp.AIClient { mcp.WithModel(DefaultClaw402Model), mcp.WithBaseURL(DefaultClaw402URL), mcp.WithTimeout(X402Timeout), + mcp.WithMaxRetries(1), // disable outer retry — inner x402 loop handles retries; outer retry causes duplicate payments } allOpts := append(baseOpts, opts...) baseClient := mcp.NewClient(allOpts...).(*mcp.Client)