Skip to content

🐛 Apply max fee overhead to privacy pool fee - #72

Merged
florian-bellotti merged 1 commit into
mainfrom
apply-overhead-to-pool-fee
Apr 23, 2026
Merged

florian-bellotti merged 1 commit into
mainfrom
apply-overhead-to-pool-fee

Conversation

@florian-bellotti

Copy link
Copy Markdown
Contributor

Summary

  • Fixes POOL_FEE_TOO_LOW (167) on paymaster_executeTransaction for apply_action in sponsored_private mode when pool_fee_token is not STRK (reproduced with USDC on mainnet).
  • Root cause: the max_fee_multiplier was applied only to the gas portion; the privacy pool fee was added raw. With a non-STRK fee token, STRK↔token price drift between build and execute left the user-approved pool-fee transfer short of the executor's re-computed amount.
  • Fix: fold pool_fee_amount into the total before applying compute_max_fee_in_strk, and apply the same overhead to the sponsored-mode fee_action_amount.

Test plan

  • cargo build -p paymaster-execution
  • cargo clippy -p paymaster-execution (no new warnings)
  • cargo test -p paymaster-execution (all tests pass)
  • Mainnet repro: re-submit the failing apply_action tx in sponsored_private with pool_fee_token = USDC and confirm no POOL_FEE_TOO_LOW
  • Regression check with pool_fee_token = STRK
  • Regression check on non-sponsored private flow

Pool fee was added raw on top of the multiplied gas fee, leaving no
headroom for STRK/token price drift between build and execute. With a
non-STRK pool_fee_token (e.g. USDC), this caused POOL_FEE_TOO_LOW (167)
on paymaster_executeTransaction. Fold pool fee into the total before
applying the max_fee_multiplier, and apply the same overhead to the
sponsored-mode fee_action_amount.
@vercel

vercel Bot commented Apr 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
paymaster Ready Ready Preview, Comment Apr 23, 2026 7:45am

Request Review

@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/paymaster-execution/src/execution/build.rs 0.00% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

@florian-bellotti
florian-bellotti merged commit f282753 into main Apr 23, 2026
5 of 6 checks passed
florian-bellotti added a commit that referenced this pull request Apr 24, 2026
PR #72 applied `max_fee_multiplier` (~3x) to the pool fee, which caused
users to approve triple the amount they actually owed and hit
"Insufficient ERC20 balance" on execute.

The pool fee is a fixed amount in STRK read from the pool, so it does not
need the large safety multiplier that guards against gas-fee volatility —
only a small overhead to cover STRK / gas-token price drift between build
and execute. Use `compute_paid_fee_in_strk` (~1.1x via `provider_fee_overhead`)
for the pool portion; keep `compute_max_fee_in_strk` on the gas portion.
florian-bellotti added a commit that referenced this pull request Apr 24, 2026
PR #72 applied `max_fee_multiplier` (~3x) to the pool fee, which caused
users to approve triple the amount they actually owed and hit
"Insufficient ERC20 balance" on execute.

The pool fee is a fixed amount in STRK read from the pool, so it does not
need the large safety multiplier that guards against gas-fee volatility —
only a small overhead to cover STRK / gas-token price drift between build
and execute. Use `compute_paid_fee_in_strk` (~1.1x via `provider_fee_overhead`)
for the pool portion; keep `compute_max_fee_in_strk` on the gas portion.
florian-bellotti added a commit that referenced this pull request Apr 24, 2026
PR #72 applied `max_fee_multiplier` (~3x) to the pool fee, which caused
users to approve triple the amount they actually owed and hit
"Insufficient ERC20 balance" on execute.

The pool fee is a fixed amount in STRK read from the pool, so it does not
need the large safety multiplier that guards against gas-fee volatility —
only a small overhead to cover STRK / gas-token price drift between build
and execute. Use `compute_paid_fee_in_strk` (~1.1x via `provider_fee_overhead`)
for the pool portion; keep `compute_max_fee_in_strk` on the gas portion.
florian-bellotti added a commit that referenced this pull request Apr 24, 2026
* Approve privacy pool to pull STRK fee in forwarder

The last call of a private batch targets a privacy pool whose `apply_actions`
collects its fee via `transferFrom(forwarder, fee_collector, fee)` on STRK,
independent of the user's gas token. Without an allowance, that transfer
reverts on the ERC20 check.

- Add `IPrivacyPool` dispatcher (`get_fee_amount`) and a 10_000 STRK safety cap.
- Hardcode the canonical STRK address on Starknet; `approve_pool_fee` always
  approves the pool on STRK, never on the user's gas token.
- Wire the helper into `execute_private` and `execute_private_sponsored`.
- Add a minimal `MockPrivacyPool` and tests covering the fee-limit guard.

* Apply only provider overhead to the privacy pool fee

PR #72 applied `max_fee_multiplier` (~3x) to the pool fee, which caused
users to approve triple the amount they actually owed and hit
"Insufficient ERC20 balance" on execute.

The pool fee is a fixed amount in STRK read from the pool, so it does not
need the large safety multiplier that guards against gas-fee volatility —
only a small overhead to cover STRK / gas-token price drift between build
and execute. Use `compute_paid_fee_in_strk` (~1.1x via `provider_fee_overhead`)
for the pool portion; keep `compute_max_fee_in_strk` on the gas portion.

* Pre-fund forwarder with STRK from relayer for privacy pool fee

The privacy pool collects its fee via `transferFrom(forwarder, fee_collector, fee)`
in STRK. When the user pays in a non-STRK gas token (USDC, etc.), the forwarder
has no STRK balance for the pool to pull, so `apply_actions` reverts.

The relayer already holds STRK (refilled by the gas-tank → relayers rebalancing),
so we prepend a `strk.transfer(forwarder, pool_fee_amount)` call before the
forwarder call in both private and private-sponsored flows. The relayer is
reimbursed when the forwarder forwards the user's gas-token payment to the gas
tank, closing the loop via the existing rebalancing.

Only emitted when `privacy_pool_fee_amount > 0`, so non-pool-fee transactions
are unchanged.

This branch was successfully deployed

1 active deployment
Preview — 9e20228f Deployed Apr 23, 2026 by vercel[bot]
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.

1 participant