-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Summary
We're experiencing intermittent unable to estimate gas errors when calling the CDP x402 Facilitator /settle endpoint on Base mainnet. The failures are non-deterministic - identical requests sometimes succeed and sometimes fail, even with fresh signatures and nonces each time.
Environment
- Network: Base Mainnet (chainId: 8453)
- Asset: USDC (
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) - Scheme:
exact(ERC-3009 TransferWithAuthorization) - Payment Amount: 1000 atomic units (0.001 USDC)
- CDP SDK Version: Latest (@coinbase/cdp-sdk)
Error Details
{
"error": "facilitator_settle_failed",
"debug": {
"error": "Facilitator settle failed (400): {\"errorMessage\": \"failed to send transaction: error (status 400): invalid_request: unable to estimate gas\", \"errorReason\": \"invalid_payload\", \"network\": \"base\", \"payer\": \"0x9A95677Df6ED534bbb2521936eEca92B268B94Db\", \"success\": false}"
}
}Reproduction Pattern
Running 5 identical API requests with 3-second delays between each:
| Test # | Result |
|---|---|
| 1 | FAILED |
| 2 | SUCCESS |
| 3 | FAILED |
| 4 | FAILED |
| 5 | SUCCESS |
Success rate: ~40% with identical code and configuration.
What We've Verified
Fresh Nonce Each Request
Each request generates a cryptographically random 32-byte nonce:
TypeScript:
const randomBytes = new Uint8Array(32);
crypto.getRandomValues(randomBytes);
const nonce = '0x' + Array.from(randomBytes)
.map(b => b.toString(16).padStart(2, '0'))
.join('');Python:
import secrets
nonce = '0x' + secrets.token_hex(32)Confirmed via logs - each request has a unique nonce:
Request 1: 0xd56eed5c3460dd37ded5a77fd228142997c2613f...
Request 2: 0x691c9ed5ab4cc628de537fcce4bddb81f3cf8c9e...
Request 3: 0xef7d9741968707e660ed9f0c89d00d0a98935c5b...
Correct EIP-712 Domain
Using the official USDC contract domain parameters:
const domain = {
name: "USD Coin",
version: "2",
chainId: 8453,
verifyingContract: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
};Sufficient Balance
- USDC Balance: ~2.99 USDC (verified via balanceOf)
- ETH Balance: Sufficient for any fees
- Each payment: 0.001 USDC
/verify Passes
The /verify endpoint returns isValid: true before /settle fails:
{
"isValid": true,
"invalidReason": null
}On-Chain Transactions Succeed When They Work
Successful payments are confirmed on-chain. Balance decreases correctly. The USDC transfers complete to the payTo address.
Code Examples (From our own SDK implementation for https://waterfall.finance)
TypeScript SDK Usage
import { WaterfallClient } from 'waterfall-ts';
const client = new WaterfallClient({ apiKey: 'wf_xxx' });
await client.configureWallet({ walletName: 'My Wallet' });
// This works ~40% of the time
const response = await client.chat.send({
model: 'openai/gpt-3.5-turbo',
messages: [{ role: 'user', content: 'Hello' }]
});
// Multi-turn conversations fail on subsequent calls
const response2 = await client.chat.send({
model: 'openai/gpt-3.5-turbo',
messages: [
{ role: 'user', content: 'Hello' },
{ role: 'assistant', content: response.choices[0].message.content },
{ role: 'user', content: 'Follow up question' }
]
});Python SDK Usage
from waterfall import WaterfallClient
client = WaterfallClient(api_key='wf_xxx')
client.configure_wallet(wallet_name='My Wallet')
# First call - works ~40% of the time
response1 = client.chat.send(
model='openai/gpt-3.5-turbo',
messages=[{'role': 'user', 'content': 'Hello'}]
)
# Second call - often fails with "unable to estimate gas"
response2 = client.chat.send(
model='openai/gpt-3.5-turbo',
messages=[
{'role': 'user', 'content': 'Hello'},
{'role': 'assistant', 'content': response1.choices[0].message.content},
{'role': 'user', 'content': 'Follow up'}
]
)x402 Gateway Payment Flow
// 1. Receive 402 Payment Required
const paymentRequired = {
scheme: 'exact',
network: 'base',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
payTo: '0x6e560Fd994dA2f434E95Cde3CAA868FB0bbCA8Ba',
maxAmountRequired: '1000',
extra: { name: 'USD Coin', version: '2' }
};
// 2. Generate ERC-3009 Authorization
const authorization = {
from: walletAddress,
to: paymentRequired.payTo,
value: paymentRequired.maxAmountRequired,
validAfter: Math.floor(Date.now() / 1000).toString(),
validBefore: (Math.floor(Date.now() / 1000) + 600).toString(),
nonce: generateRandomNonce()
};
// 3. Sign with EIP-712
const signature = await wallet.signTypedData({
domain: { name: 'USD Coin', version: '2', chainId: 8453, verifyingContract: asset },
types: { TransferWithAuthorization: [...] },
primaryType: 'TransferWithAuthorization',
message: authorization
});
// 4. Call Facilitator
// /verify returns isValid: true
// /settle returns "unable to estimate gas" (intermittent)Expected Behavior
All valid payment signatures with fresh nonces should settle successfully, especially when:
/verifyreturnsisValid: true- Wallet has sufficient USDC balance
- EIP-712 domain matches the token contract
- Nonce has never been used before
Actual Behavior
~60% of valid, verified payments fail at /settle with unable to estimate gas, despite:
- Passing verification
- Using fresh nonces
- Having sufficient balance
- Using correct domain parameters
Questions
- Is there internal rate limiting on the facilitator that could cause this pattern?
- Are there multiple facilitator instances, some of which may be unhealthy?
- Is there a recommended retry strategy for handling these intermittent failures?
- Could there be RPC node issues on the facilitator's side affecting gas estimation?
Workaround Attempted
Adding delays (2s, 10s, 30s) between requests does not resolve the issue. The failures appear random regardless of timing.
Impact
This makes multi-turn conversations unreliable for production use, as subsequent API calls in a session frequently fail even though the payment signatures are valid.
Additional Context
- First request in a session has the same ~40% failure rate as subsequent requests
- Failures are not correlated with time of day
- Same behavior observed across TypeScript and Python SDKs
- On-chain transaction history shows successful payments when /settle succeeds