You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a global deposit/ingress limit to pallet-circuit-breaker.
Hydration currently has two related protections:
a per-asset issuance-increase fuse (IssuanceIncreaseFuse) backed by each asset's xcm_rate_limit; and
a global withdrawal/egress limit, which values participating assets in HDX and maintains a decaying accumulator.
There is no chain-wide cap on aggregate inbound issuance. An invalid or compromised bridge/minter can therefore spread deposits across several assets, or use an asset whose per-asset rate limit is missing/misconfigured, without hitting a single aggregate exposure budget.
The new limit should cap aggregate cross-chain ingress, expressed in HDX reference units, across every participating asset and ingress path. It should be an additional layer: it must not replace the per-asset issuance fuse.
Required semantics
Maintain an independent gross-ingress accumulator with its own governance-configurable limit and window.
A participating deposit succeeds only when the decayed accumulator plus the new ingress remains below the configured limit, matching the boundary convention used by the current global withdrawal limit.
The accumulator decays using the same time source and decay semantics as the existing withdrawal accumulator.
None (and, for compatibility with the withdrawal limiter, a zero window) disables the limit.
Opposite-direction egress must not reduce the deposit accumulator. Otherwise an attacker could repeatedly deposit one asset, exchange it, withdraw another asset, and recreate deposit capacity.
The current behavior in which genuine ingress can credit the withdrawal accumulator may remain for backward compatibility, but the deposit accumulator itself must measure gross ingress.
Internal balance movements, fee withdrawals/refunds, and other explicitly ignored system operations must not consume or create limit capacity.
A participating asset that cannot be converted to HDX must not silently bypass the deposit limit. The operation must fail closed before the deposited balance becomes spendable, or the funds must be placed in a non-spendable quarantine/trap flow.
What counts as ingress
Use the existing GlobalAssetCategory and overrides as the source of participation:
External: issuance/deposit representing assets entering Hydration, including XCM and authorized NTT minting.
Local: assets returning from an egress account/boundary.
None: ordinary internal assets and balance movements are not counted.
Only a real boundary crossing should be counted. A transfer between normal Hydration accounts is not ingress.
Atomicity and failure behavior
The current OnDepositHook and OnTransferHook are invoked after the underlying currency mutation in several pallet-currencies paths. The new limiter must not simply return GlobalDepositLimitExceeded from a post-mutation hook and assume every caller is transactional.
Before implementation, define and enforce one of these safe contracts for every covered path:
preflight the projected ingress before mutating balances/issuance; or
execute the mutation and accounting in a storage transaction that is guaranteed to roll back on an accounting error.
On rejection:
no newly deposited/minted balance may remain spendable;
the deposit accumulator must remain unchanged;
unrelated withdrawal state must remain unchanged; and
XCM processing must have an explicit trap/quarantine outcome rather than leaving partial accounting.
This must be coordinated with #1150, which tracks the existing operational difficulty of recovering XCM assets trapped when a deposit limit rejects an intermediate deposit.
XCM accounting
Extend/replace XcmEgressBuffer; adding a second total to the current HDX-wide tuple is not sufficient.
Within one XCM execution, temporary withdrawals and refunds should not be treated as new cross-chain flow. Net per asset first, then aggregate the positive sides in HDX:
Do not net all assets into one signed HDX total. Cross-asset netting would allow an ingress of asset A and an egress of asset B to cancel even though both risk budgets should be consumed.
The XCM path must:
include already buffered ingress when prechecking subsequent deposits in the same message;
use checked arithmetic rather than saturating away overflow;
account successful balance changes even when a later XCM instruction fails (partial execution);
avoid double-counting deposit_asset plus the currency mutation hook;
cover queued XCMP/DMP processing and locally executed XCM; and
prove that an over-limit deposit does not leave a spendable minted balance.
Context
The per-asset issuance fuse and a global ingress limit protect against different failure modes:
the per-asset fuse bounds one asset and can reserve/lock excess issuance;
the global limit bounds aggregate HDX-equivalent exposure across all participating assets;
the global limit provides defense in depth when several assets are attacked together or one asset lacks a correct xcm_rate_limit.
Relevant current code:
pallets/circuit-breaker/src/lib.rs
GlobalWithdrawLimitConfig
WithdrawLimitAccumulator
note_egress
note_deposit
ensure_xcm_withdraw_can_proceed
XcmEgressBuffer
pallets/circuit-breaker/src/fuses/issuance.rs
runtime/hydradx/src/circuit_breaker.rs
asset categorization and HDX conversion
withdraw/deposit/transfer hooks
runtime/hydradx/src/xcm.rs
ProcessXcmWithBreaker
LocalAssetTransactor
pallets/currencies/src/lib.rs and pallets/currencies/src/fungibles.rs
runtime/hydradx/src/evm/precompiles/multicurrency.rs (NTT mint path)
update the flow buffer to retain asset identity with an explicit bound;
append new call indices for configuration/reset/lockdown calls without renumbering existing extrinsics;
add deposit-specific events and errors (DepositLimitConfigUpdated, GlobalDepositLimitExceeded, reset/lockdown events as applicable); and
rename AssetWithdrawHandler, WithdrawFuseControl, and IgnoreWithdrawLimit to direction-neutral equivalents if they now govern both ingress and egress.
The ignore mechanism must cover both directions and restore its prior value on every success/error path. A scoped/transaction-safe guard is preferable to manually writing a global boolean before and after fallible fee operations.
Governance operations should support:
setting/updating the deposit limit and window;
resetting only the deposit accumulator;
disabling the limiter without corrupting accumulated state; and
optionally applying/lifting a manual deposit lockdown.
Use the existing AuthorityOrigin unless there is a reason to separate deposit-risk governance.
Acceptance criteria
Aggregate ingress across two or more participating assets is limited in HDX reference units.
An asset with no configured per-asset issuance limit is still covered when its global category participates.
Egress does not replenish the deposit accumulator.
Same-asset XCM staging/refunds are netted once, while cross-asset ingress and egress consume their respective limits.
Over-limit direct deposits/mints/transfers are atomic: balances, issuance, events, and both accumulators are unchanged.
Over-limit XCM ingress leaves no spendable balance and has a tested trap/quarantine/recovery outcome.
NTT mint reverts cleanly when the global deposit limit is reached and can be retried after capacity becomes available.
Detailed Description
Add a global deposit/ingress limit to
pallet-circuit-breaker.Hydration currently has two related protections:
IssuanceIncreaseFuse) backed by each asset'sxcm_rate_limit; andThere is no chain-wide cap on aggregate inbound issuance. An invalid or compromised bridge/minter can therefore spread deposits across several assets, or use an asset whose per-asset rate limit is missing/misconfigured, without hitting a single aggregate exposure budget.
The new limit should cap aggregate cross-chain ingress, expressed in HDX reference units, across every participating asset and ingress path. It should be an additional layer: it must not replace the per-asset issuance fuse.
Required semantics
limitandwindow.None(and, for compatibility with the withdrawal limiter, a zero window) disables the limit.What counts as ingress
Use the existing
GlobalAssetCategoryand overrides as the source of participation:External: issuance/deposit representing assets entering Hydration, including XCM and authorized NTT minting.Local: assets returning from an egress account/boundary.None: ordinary internal assets and balance movements are not counted.Only a real boundary crossing should be counted. A transfer between normal Hydration accounts is not ingress.
Atomicity and failure behavior
The current
OnDepositHookandOnTransferHookare invoked after the underlying currency mutation in severalpallet-currenciespaths. The new limiter must not simply returnGlobalDepositLimitExceededfrom a post-mutation hook and assume every caller is transactional.Before implementation, define and enforce one of these safe contracts for every covered path:
On rejection:
This must be coordinated with #1150, which tracks the existing operational difficulty of recovering XCM assets trapped when a deposit limit rejects an intermediate deposit.
XCM accounting
Extend/replace
XcmEgressBuffer; adding a second total to the current HDX-wide tuple is not sufficient.Within one XCM execution, temporary withdrawals and refunds should not be treated as new cross-chain flow. Net per asset first, then aggregate the positive sides in HDX:
Do not net all assets into one signed HDX total. Cross-asset netting would allow an ingress of asset A and an egress of asset B to cancel even though both risk budgets should be consumed.
The XCM path must:
deposit_assetplus the currency mutation hook;Context
The per-asset issuance fuse and a global ingress limit protect against different failure modes:
xcm_rate_limit.Relevant current code:
pallets/circuit-breaker/src/lib.rsGlobalWithdrawLimitConfigWithdrawLimitAccumulatornote_egressnote_depositensure_xcm_withdraw_can_proceedXcmEgressBufferpallets/circuit-breaker/src/fuses/issuance.rsruntime/hydradx/src/circuit_breaker.rsruntime/hydradx/src/xcm.rsProcessXcmWithBreakerLocalAssetTransactorpallets/currencies/src/lib.rsandpallets/currencies/src/fungibles.rsruntime/hydradx/src/evm/precompiles/multicurrency.rs(NTT mint path)Related issues/changes:
Possible Implementation
Prefer an additive implementation that preserves the existing withdrawal storage:
GlobalWithdrawLimitParametersto a reusableGlobalLimitParameters, or introduce an equivalent deposit parameter type;GlobalDepositLimitConfig;DepositLimitAccumulator: (Balance, Moment);DepositLockdownUntilonly if manual ingress lockdown is required for operational parity;note_ingress/ensure_ingress_can_proceedhelpers;DepositLimitConfigUpdated,GlobalDepositLimitExceeded, reset/lockdown events as applicable); andAssetWithdrawHandler,WithdrawFuseControl, andIgnoreWithdrawLimitto direction-neutral equivalents if they now govern both ingress and egress.The ignore mechanism must cover both directions and restore its prior value on every success/error path. A scoped/transaction-safe guard is preferable to manually writing a global boolean before and after fallible fee operations.
Governance operations should support:
Use the existing
AuthorityOriginunless there is a reason to separate deposit-risk governance.Acceptance criteria
mintreverts cleanly when the global deposit limit is reached and can be retried after capacity becomes available.polkadot_xcm::execute,pallet-currenciesMultiCurrency, fungiblesMutate, and NTT precompile paths have integration coverage.