fix: don't revert claim_rewards on un-convertible dust#1474
Merged
Conversation
|
Crate versions that have been updated:
Runtime version has been increased. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a referrals runtime regression where claim_rewards could revert if PendingConversions contains a dust (sub-MinTradingLimit) balance that the new omnipool-backed converter rejects, enabling same-block griefing and incidental claim failures.
Changes:
- Make
claim_rewardsconversion loop best-effort (ignore conversion errors) and always dropPendingConversionsentries, matchingon_idle. - Add a regression test for sub-min pending dust not blocking unrelated claims, and extend the mock
AssetConvertto optionally reject sub-min amounts with a non-referrals error. - Bump
pallet-referralsand runtime spec/runtime crate versions.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| runtime/hydradx/src/lib.rs | Bumps spec_version to reflect runtime behavior change. |
| runtime/hydradx/Cargo.toml | Bumps hydradx-runtime crate version. |
| pallets/referrals/src/lib.rs | Changes claim_rewards pending conversion handling to best-effort (non-fatal). |
| pallets/referrals/src/tests/claim.rs | Adds regression test for dust pending conversion not blocking claims. |
| pallets/referrals/src/tests.rs | Extends test converter with optional min-amount rejection gate + builder hook. |
| pallets/referrals/Cargo.toml | Bumps pallet-referrals crate version. |
| Cargo.lock | Updates locked versions for the bumped crates. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+77
to
+81
| // Regression: a sub-`MinTradingLimit` dust balance of a non-reward asset in `PendingConversions` | ||
| // must not block reward claims. The converter rejects the dust with a non-referrals error | ||
| // (`InsufficientTradingAmount`), which the claim loop only tolerates for its own two legacy | ||
| // variants — so today it reverts the whole claim instead of skipping the dust like `on_idle` | ||
| // does. This test asserts the desired self-healing behaviour and currently FAILS, proving the bug. |
Comment on lines
+92
to
+94
| // Price present so the converter reaches the min-amount check (not the no-price branch, | ||
| // whose error the claim loop happens to tolerate). | ||
| .with_conversion_price((HDX, DAI), EmaPrice::new(1_000_000_000_000, 1_000_000_000_000_000_000)) |
|
Quick benchmark at commit 9bed6c4 has been executed successfully. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
problem
claim_rewardsiterates the globalPendingConversionsmap and converts each pending asset to the reward asset before paying out shares. it only tolerates two conversion errors —ConversionMinTradingAmountNotReachedandConversionZeroAmountReceived— and reverts the whole call on anything else.the v49 fee-flow refactor moved conversion to
ConvertViaOmnipool, which dropped the old explicit min-amount pre-check (that returnedConversionMinTradingAmountNotReached). it now callsOmnipool::selldirectly → for a pot balance belowMinTradingLimit(1_000 raw units) it propagates the rawpallet_omnipool::Error::InsufficientTradingAmount, which is not in the tolerated set.result → any sub-
MinTradingLimitdust balance of a non-reward asset inPendingConversionsmakesclaim_rewardsrevert:on_idle, a dust entry left by a trade earlier in the same block blocks everyclaim_rewardsin that block[1, 999]of an obscure asset)regression vs v48: the old
convertreturnedConversionMinTradingAmountNotReachedfor sub-min amounts (which the claim loop tolerated); the new converter returnsInsufficientTradingAmount, and the claim loop's tolerated-error list was never updated.fix
make the claim loop best-effort, identical to the sibling
on_idlehook (which already doeslet _ = T::Convert::convert(...)): skip an un-convertible slice instead of reverting, and drop the entry either way. a skipped conversion now emits aConversionFailed { asset_id, reason }event (in bothclaim_rewardsandon_idle) so it is surfaced rather than silently swallowed — mirroring the fee-processor.claim_rewards↔on_idletolerance divergence by constructionwhy not extend the exact-match list instead?
the alternative is to keep the
if let Errpropagation and add the new tolerated variants (InsufficientTradingAmount,ConversionFailed,PriceNotAvailable). rejected because:pallet-referralsconstrains its converter toConvert<…, Error = DispatchError>and does not depend onpallet-omnipool/pallet-fee-processor. matching their error variants would mean either adding those deps (bad layering for a generic rewards pallet) or matching rawDispatchError::Modulediscriminants (fragile).on_idle, which already abandoned exact-match — so both cleanup paths now behave identically.the visibility that exact-match gave (surfacing an unexpected converter error rather than dropping it) is preserved by the
ConversionFailedevent, without re-introducing the coupling.test
claim_rewards_should_succeed_when_pending_asset_balance_is_below_min_trading_limit→ a 500-unit (sub-min) DAI dust entry no longer blocks an unrelated user's claim, and aConversionFailedevent is emitted for it. the mockAssetConvertnow models the production min-trading-limit rejection (returns a non-referrals error for sub-min amounts), gated behind a newwith_convert_min_amountbuilder defaulting to disabled so existing tests are unaffected.versions
pallet-referrals1.6.0 → 1.6.1hydradx-runtime426.0.0 → 427.0.0