Skip to content

fix: estimate ERC-3009 settlement gas#23

Open
peterxing wants to merge 2 commits into
rawgroundbeef:mainfrom
peterxing:fix/issue-21-erc3009-gas-estimate
Open

fix: estimate ERC-3009 settlement gas#23
peterxing wants to merge 2 commits into
rawgroundbeef:mainfrom
peterxing:fix/issue-21-erc3009-gas-estimate

Conversation

@peterxing

Copy link
Copy Markdown

Summary

  • replace the fixed 100000n ERC-3009 settlement gas cap with publicClient.estimateGas(...)
  • apply a 30% buffer to the estimated limit before broadcasting
  • check the facilitator ETH balance against the dynamic gas limit so Base Mainnet USDC v2.2 first-use storage paths are not rejected by the old 100k assumption

Why

Base Mainnet native USDC can exceed 100k gas for transferWithAuthorization when blacklist/pause checks and first-time storage allocation are exercised. The old hard cap could revert even when the signature/payload was otherwise valid.

Fixes #21.

Verification

  • corepack pnpm install --frozen-lockfile --ignore-scripts
  • corepack pnpm --filter @openfacilitator/core lint
  • corepack pnpm --filter @openfacilitator/core build
  • git diff --check HEAD~1 HEAD

Note: the normal install without --ignore-scripts is blocked on Windows by @stellar/stellar-sdk running yarn setup || true from postinstall where yarn/POSIX true are unavailable; the core TypeScript gates pass after an ignore-scripts install.

@vercel

vercel Bot commented May 21, 2026

Copy link
Copy Markdown

@peterxing is attempting to deploy a commit to the Ben's Team Team on Vercel.

A member of the Team first needs to authorize it.

@TateLyman

Copy link
Copy Markdown

Source-level pass on the settlement fix: this moves the production-critical path off the fixed 100000n assumption in both places that mattered to #21: the facilitator balance check and sendTransaction({ gas }). That addresses the Base mainnet USDC v2.2 first-storage / blacklist / pause overhead case described in the issue.

A few acceptance checks I would run before merge:

  • Re-run the failed Base-mainnet calldata shape from Hardcoded gas: 100000n in ERC-3009 settlement reverts Base Mainnet USDC v2.2 #21 with logging for estimatedGas, buffered gasLimit, receipt.gasUsed, token address, and chain id.
  • Confirm estimateGas failures return a non-settlement error before an EVM nonce is acquired. In this patch it happens before NonceManager.acquireNonce, which is the right ordering.
  • Confirm the existing ERC-3009 processing nonce is released on pre-broadcast estimate/send failures, but not released after a broadcast timeout where the tx may still land. The current outer catch appears to preserve that distinction.
  • Keep one Base Sepolia fixture too, but do not treat it as sufficient coverage for Base mainnet native USDC.

The visible failing check is Vercel authorization for a fork deploy, not a code gate. I did not sign requests, send payment headers, call /settle, or broadcast transactions; this is a public source/no-payment review.

@peterxing

Copy link
Copy Markdown
Author

Added a follow-up commit after the source-level acceptance pass: e9be708 (test: cover ERC-3009 gas estimate ordering).

What it covers locally without signing/broadcasting:

  • estimateGas failures happen before NonceManager.acquireNonce / getTransactionCount, so a failed simulation does not consume an EVM nonce.
  • The ERC-3009 processing lock is released after an estimateGas failure; the same authorization can be retried instead of being stuck as a duplicate.
  • The facilitator ETH balance check uses the dynamic buffered gas estimate before acquiring an EVM nonce or calling sendTransaction.

Verification re-run:

  • corepack pnpm --filter @openfacilitator/core test -- src/erc3009.test.ts — 2 tests passed.
  • corepack pnpm --filter @openfacilitator/core lint — passed.
  • corepack pnpm --filter @openfacilitator/core build — passed.
  • git diff --check — passed after staging the test file.

I did not run the Base-mainnet calldata replay because that would require facilitator wallet/private-key access and potentially a live /settle/broadcast path. No payment headers were sent, no wallet transaction was signed, and no funds were moved.

@TateLyman

Copy link
Copy Markdown

Re-read the follow-up test commit. The added coverage addresses the two source-level acceptance points I was most concerned about:

  • estimateGas failure now happens before EVM nonce acquisition and can be retried without leaving the ERC-3009 authorization stuck as processing.
  • facilitator balance is checked against the buffered dynamic estimate before getTransactionCount / sendTransaction.

That is the right no-broadcast regression shape for the bug in #21. The only remaining proof gap is the one you already called out: a Base mainnet calldata replay or staging settlement trace with estimatedGas, bufferedLimit, receipt.gasUsed, token address, and chain id. That requires facilitator wallet/test-scope access, so I would not expect it from this public PR thread.

No payment headers, wallet signatures, settlement calls, or transactions from me here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hardcoded gas: 100000n in ERC-3009 settlement reverts Base Mainnet USDC v2.2

2 participants