Skip to content

fix: signed dispatch_permit weight refund + nonce consumption on revert#1468

Open
mrq1911 wants to merge 1 commit into
masterfrom
fix/signed-permit-weight-nonce
Open

fix: signed dispatch_permit weight refund + nonce consumption on revert#1468
mrq1911 wants to merge 1 commit into
masterfrom
fix/signed-permit-weight-nonce

Conversation

@mrq1911
Copy link
Copy Markdown
Member

@mrq1911 mrq1911 commented May 30, 2026

Follow-up to #1462. Two fixes to do_dispatch_permit_signed, both in the result match:

1. Charge the paymaster for gas used, not the full gas_limit

On success the branch returned Ok(().into()) (actual_weight: None, Pays::Yes), discarding the PostDispatchInfo the runner already computed. The signer was charged a Substrate weight fee against the full declared gas_limit on top of EVM gas, while the unsigned path forwards the runner's actual weight. Now forwards Ok(post).

2. Consume the permit nonce when the inner call reverts

A revert made dispatch_permit return Err after incrementing NoncesStorage; FRAME's per-extrinsic storage layer then rolled that increment back, leaving the permit replayable — diverging from the unsigned path, which commits (unwrap_or_elseOk) and burns the nonce on revert.

The error arm is now split:

  • RunnerError (nothing charged) → still Err with default post-info, so the paymaster pays the extrinsic fee per attempt. This per-attempt cost is the signed path's anti-grief floor in place of the unsigned-path autopause; the branch still never calls on_dispatch_permit_error().
  • Call-execution revert (nonce already consumed, gas charged in the runner) → Ok(e.post_info), mirroring the unsigned path. Pays::No waives the duplicate Substrate fee — the EVM gas is the payment.

No double-spend was possible before or after: a successful execution consumes the nonce, so a permit succeeds at most once.

Tests

Updated four signed-permit integration tests to the new semantics (revert → Ok + nonce consumed; restore-on-revert; default post-info repointed to the RunnerError path via a u64::MAX-signed gas limit). Unsigned-path, success, replay, and two-paymaster-race tests untouched.

  • runtime-integration-testsevm_permit: 43 passed
  • pallet-transaction-multi-payment: 45 passed

Copilot AI review requested due to automatic review settings May 30, 2026 21:56
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, reopen this pull request to trigger a review.

@github-actions
Copy link
Copy Markdown

Crate versions that have not been updated:

  • runtime-integration-tests: v1.87.0
  • pallet-transaction-multi-payment: v10.6.0

Runtime version has not been increased.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is a follow-up fix to the signed dispatch_permit path in pallet-transaction-multi-payment, aligning its fee/weight accounting and nonce-consumption behavior with the unsigned path while preserving the signed path’s anti-grief behavior for runner-level rejections.

Changes:

  • Forward runner-computed PostDispatchInfo on signed success to refund/charge based on actual weight (gas used) instead of the declared gas_limit.
  • On signed revert (call-execution failure), return Ok(post_info) to commit nonce consumption and avoid permit replay.
  • Update signed-permit integration tests to reflect “revert commits + nonce consumed” semantics and to isolate the hard-failure (Err) case to runner errors.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pallets/transaction-multi-payment/src/lib.rs Adjusts signed dispatch_permit result handling to forward post-info on success and commit nonce on revert.
integration-tests/src/evm_permit.rs Updates signed-permit integration tests to validate new revert/nonce and runner-error behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +504 to +510
// RunnerError: nothing charged. Keep Err (`Pays::Yes`) so the paymaster
// still pays the extrinsic fee — the per-attempt cost that replaces the
// unsigned-path autopause. Never call `on_dispatch_permit_error()` here.
Err(e) if e.error == Error::<T>::EvmPermitRunnerError.into() => Err(e.error.into()),
// Revert already consumed the nonce and charged gas; commit it like the
// unsigned path. Returning Err would roll the nonce back → replayable.
Err(e) => Ok(e.post_info),
@github-actions
Copy link
Copy Markdown

Quick benchmark at commit abd1dbc has been executed successfully.
View results

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.

2 participants