Skip to content

fix: dispatch with extra gas transaction charges previous currency#1372

Merged
mrq1911 merged 13 commits intomasterfrom
fix/set-currency-charges
Apr 10, 2026
Merged

fix: dispatch with extra gas transaction charges previous currency#1372
mrq1911 merged 13 commits intomasterfrom
fix/set-currency-charges

Conversation

@khuzama98
Copy link
Copy Markdown
Contributor

@khuzama98 khuzama98 commented Feb 26, 2026

Description

Fixes fee currency resolution for dispatch_with_extra_gas wrapping set_currency calls.
When users submitted dispatch_with_extra_gas { set_currency { currency: X } }, the transaction fee was incorrectly charged in the previous account currency instead of the newly specified currency X. Direct set_currency calls worked correctly.

This affected both substrate accounts (issue #1296) and EVM accounts (issue #1293).

Root Cause

The pallet's resolve_currency_from_call function had duplicate currency resolution logic that was missing the dispatch_with_extra_gas handler, while the runtime's TryCallCurrency implementation already handled it correctly. The two resolvers diverged over time.

Fix

Replaced the duplicate pattern-matching logic in resolve_currency_from_call with a simple delegation to T::TryCallCurrency::try_convert(). This ensures both code paths use the same authoritative resolver and eliminates future divergence.

Related Issue

Fixes: #1296 #1293

Motivation and Context

How Has This Been Tested?

Integration tests have been added to test the scenario in which new currencies are being charged successfully.

Checklist:

  • I have updated the documentation if necessary.
  • I have added tests to cover my changes, regression test if fixing an issue.
  • This is a breaking change.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 26, 2026

Crate versions that have been updated:

  • runtime-integration-tests: v1.76.2 -> v1.77.0
  • pallet-transaction-multi-payment: v10.4.0 -> v10.5.0

Runtime version has not been increased.

@dmoka
Copy link
Copy Markdown
Contributor

dmoka commented Mar 3, 2026

I cannot run cargo test command from root hydration-node/pallets/transaction-multi-payment, could you please fix that?

you prolly need to do add std feature in cargo.toml, cor currencies

[dev-dependencies]
pallet-currencies = { workspace = true, features = ["std"] }
...

Comment thread integration-tests/src/non_native_fee.rs Outdated
bob_btc_after < bob_btc_before,
"BTC balance should decrease — fee must be charged in the new currency"
);
// HDX must not be touched.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
// HDX must not be touched.

Comment thread integration-tests/src/non_native_fee.rs Outdated
1_000_000_000_000_000_000i128,
));

// EVM accounts have WETH automatically set as their fee currency on account creation
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
// EVM accounts have WETH automatically set as their fee currency on account creation

Comment thread integration-tests/src/non_native_fee.rs Outdated
let weth_after = hydradx_runtime::Tokens::free_balance(WETH, &evm_acc);
let dai_after = hydradx_runtime::Tokens::free_balance(DAI, &evm_acc);

// Fee must be charged in DAI (the new currency), not WETH (EVM default).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Fee must be charged in DAI (the new currency), not WETH (EVM default).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i am removing these comments because in the assert you place very similar error message, so the comments feel redundant. small details...

Comment thread integration-tests/src/non_native_fee.rs Outdated
dai_after < dai_before,
"DAI balance should decrease — fee must be charged in the new currency"
);
// WETH must not be touched.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
// WETH must not be touched.

Comment thread integration-tests/src/non_native_fee.rs Outdated

let bob_btc_after = hydradx_runtime::Tokens::free_balance(BTC, &AccountId::from(BOB));

// BTC must not be charged — the resolver did not reach the nested set_currency.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
// BTC must not be charged — the resolver did not reach the nested set_currency.

impl<T, MC, DF, FR, WF> TransferFees<T, MC, DF, FR, WF>
where
T: Config + pallet_utility::Config,
T: Config,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i think you can do the same bound cleanups in the OnChargeTransaction impl for TransferFees

@khuzama98 khuzama98 requested a review from dmoka March 5, 2026 08:58
@mrq1911 mrq1911 enabled auto-merge April 9, 2026 11:39
Copilot AI review requested due to automatic review settings April 10, 2026 06:45
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

Fixes fee-currency resolution for set_currency when it is wrapped by dispatch_with_extra_gas, ensuring transaction fees are charged in the newly selected currency (matching the runtime’s authoritative call-to-currency resolver) and adds integration coverage for both Substrate and EVM accounts.

Changes:

  • Remove duplicated call-pattern matching in TransferFees::resolve_currency_from_call and delegate to T::TryCallCurrency::try_convert.
  • Update test mock configuration to use a call-currency converter that recognizes set_currency/batch patterns.
  • Add integration tests covering dispatch_with_extra_gas { set_currency } for both Substrate and EVM accounts, plus a negative test for deeper nesting; bump runtime/pallet versions accordingly.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
runtime/hydradx/src/lib.rs Bumps runtime spec_version to reflect the behavior change.
runtime/hydradx/Cargo.toml Bumps runtime crate version.
pallets/transaction-multi-payment/src/lib.rs Delegates fee-currency resolution to T::TryCallCurrency to avoid divergence and fix the dispatcher-wrapped set_currency case.
pallets/transaction-multi-payment/src/mock.rs Switches test runtime to a call-currency resolver that supports set_currency + utility batching patterns.
pallets/transaction-multi-payment/Cargo.toml Bumps pallet version and adjusts std/dev-dependency features for test/build compatibility.
integration-tests/src/non_native_fee.rs Adds regression tests for dispatcher-wrapped set_currency fee charging (Substrate + EVM) and a nested-negative case.
integration-tests/Cargo.toml Bumps integration tests crate version.
Cargo.lock Updates lockfile for the version bumps.

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

Comment on lines +914 to +915
/// (which requires `pallet_dispatcher` dependency).
pub struct TestCallCurrency<T>(PhantomData<T>);
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

TestCallCurrency is documented as “Test-only” but is defined in the main pallet module without any cfg(test)/feature gating. This makes it part of the pallet’s public API (and can be accidentally used in production runtimes), and can also increase compiled artifact size/complexity unnecessarily. Consider moving this helper into mock.rs (or a #[cfg(test)] module) or gating the type/impl behind #[cfg(test)] (or a dedicated test feature) so it’s only available for unit tests.

Suggested change
/// (which requires `pallet_dispatcher` dependency).
pub struct TestCallCurrency<T>(PhantomData<T>);
/// (which requires `pallet_dispatcher` dependency).
#[cfg(test)]
pub struct TestCallCurrency<T>(PhantomData<T>);
#[cfg(test)]

Copilot uses AI. Check for mistakes.
Comment on lines 3 to 8
use crate::polkadot_test_net::*;
use frame_support::sp_runtime::codec::Encode;
use frame_support::{
assert_ok,
dispatch::DispatchInfo,
dispatch::{DispatchInfo, GetDispatchInfo},
sp_runtime::{traits::DispatchTransaction, FixedU128, Permill},
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

In integration tests, other modules import Encode directly from sp_runtime::codec::Encode rather than via the frame_support::sp_runtime re-export. For consistency (and to avoid relying on a re-export that could change), consider switching this import to sp_runtime::codec::Encode like the other integration test files.

Copilot uses AI. Check for mistakes.
@mrq1911 mrq1911 merged commit ff231b0 into master Apr 10, 2026
10 checks passed
@github-actions
Copy link
Copy Markdown

Quick benchmark at commit 889ad50 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.

Bug: set currency charges with previous fee currency

5 participants