This release contains the changes from polkadot-stable2606-3 to polkadot-stable2606-4.
ℹ️ Please note: ⚠️ this is a patch release for the stable version stable2606 and contains only patches and fixes to the crates (list below). No binary or docker images will be provided for this release.
Once published, polkadot-stable2606-4 will also be tagged as polkadot-v1.24.4.
The following crates were updated to the corresponding versions:
Changed crates:
📦 The complete changelog with full descriptions is attached to this release as CHANGELOG.md, and in machine-readable form as changelog.json.
💥 Breaking Changes
3 changes are breaking according to Rust SemVer (each bumps the major version of at least one crate). Details in the 💥-marked entries below.
- Smart Contracts (Revive): #13617 [pallet-revive] Bound per-transaction receipt size (
pallet-revive) - Assets: #13256 pallet-psm: reject the
Noneorigin as a PSM admin (pallet-psm) - Assets: #13626 pallet-psm: simplify PSM account handling (
pallet-psm)
Changelog
Bridges
🛠️ Runtime Dev
#11910: XCM hardening - fix some regressions
This PR fixes some regressions, improves code and adds more defense-in-depth in a couple places.
xcm-executor: drain holdings by ownership in deposit_assets_with_retry
Add AssetsInHolding::into_per_asset_holdings() and use it in deposit_assets_with_retry so each pass consumes the input via owning BTreeMap/BTreeSet iterators instead of assets_iter().collect() + try_take. Per asset this drops one AssetId clone, one or two BTreeMap lookups, one dynamic dispatch on saturating_take, and one internal subsume_assets call; per pass it drops the intermediate Vec<Asset> allocation. Behaviour is unchanged: same iteration order, same partial-failure / retry semantics. …
Crates: staging-xcm-executor (minor), cumulus-primitives-utility (patch), snowbridge-runtime-common (patch), bridge-hub-westend-runtime (patch), snowbridge-beacon-primitives (patch)
#12569: Preserve Snowbridge v1 inbound liveness when the Asset Hub sovereign is empty
Also for Runtime User
Snowbridge v1 inbound messages now burn the teleported Asset Hub execution fee from the relayer's Bridge Hub account instead of requiring funds in the Asset Hub sovereign account on Bridge Hub. The Asset Hub sovereign reimburses the relayer best-effort for the burned fee plus delivery reward when it has enough reducible balance.
This preserves the existing funded-sovereign economics while preventing an empty Asset Hub sovereign from stalling the Ethereum-to-Polkadot inbound channel. In the empty-sovereign case, the message is still processed and the nonce advances, but the relayer is not reimbursed.
Crates: snowbridge-pallet-inbound-queue (patch), snowbridge-beacon-primitives (none), bridge-hub-westend-runtime (patch)
#13640: snowbridge: restore unchecked sync committee key parsing
Restore unchecked deserialization for authenticated Ethereum sync committee public keys. Repeating the G1 subgroup check for all 512 keys makes committee-update calls exceed the synchronous Bridge Hub block budget and stalls Snowbridge at committee rotation.
Crates: snowbridge-beacon-primitives (patch), bridge-hub-westend-runtime (patch)
XCM
🛠️ Runtime Dev
#13601: Fix user losing remainder of swapped funds when pool liquidity goes below ED
pallet-asset-conversion's SwapCredit::swap_tokens_for_exact_tokens split credit_in into the amount to swap and the change, and returned only the amount to swap if the swap then failed. The change was dropped inside the storage transaction that is rolled back on failure, so the caller lost it. The full credit_in is now returned on failure, as documented.
Crates: staging-xcm-executor (patch), staging-xcm-builder (none), pallet-asset-conversion (patch)
Smart Contracts (Revive)
🛠️ Runtime Dev
💥 #13617: [pallet-revive] Bound per-transaction receipt size
Also for Runtime User
No storage migration and no configuration change is needed. The new ReceiptLogsCommitted storage value is only written while an Ethereum transaction or its dry run runs and is empty between transactions.
Error has the new variant ReceiptTooLarge (index 0x45). Index 0x44 is left unused so that the indexes match later releases. Code that matches on the error exhaustively needs an arm for it. …
Breaking: pallet-revive
Other crates: pallet-revive-fixtures (minor), pallet-revive-uapi (none), pallet-assets-precompiles (patch)
#12267: [pallet-revive] fix double deposit charge to parent contractinfo
A contract that writes storage, calls back into itself, directly or through another contract, and then writes storage again now has each write counted once in its ContractInfo. Before, the writes made before the reentrant call were applied to the stored ContractInfo a second time when the outer call finished. This inflated storage_items, storage_bytes, storage_item_deposit and storage_byte_deposit, and the inflated values were kept across transactions: ContractInfo reported more storage items, bytes and deposit than the contract actually stored and held.
Calls that do not reenter the same contract are not affected. …
Crates: pallet-revive (minor), pallet-revive-fixtures (minor)
#13039: [pallet-revive] Enforce the storage deposit limit before recording the charge
charge_deposit now checks the storage deposit limit before recording a charge instead of recording it and resetting the whole meter on overflow. This keeps the transaction's other storage charges in place when a contract termination's deposit charge would exceed the limit, so those writes are charged and held rather than committed without a deposit.
Crates: pallet-revive (patch), pallet-revive-fixtures (minor)
#13085: [pallet-revive] System.terminate deletes the contract despite locks, freezes and holds
Also for Runtime User
No migration or configuration change is needed.
seal_terminate_logic still measures one storage deposit refund and the teardown. The refund now runs when System.terminate is called instead of at the end of the call stack, and each teardown step runs in its own storage layer. The pallet_revive weights are not regenerated for this release and no benchmark was run on it. The existing seal_terminate_logic weight is 222.41M ps ref_time, 6713 bytes proof size and 7 storage reads. The measurement after this change on master is 235.88M ps, 6348 bytes and 6 reads. The ref_time here is about 6% below that measurement, while the proof size and reads are above it. …
Crates: pallet-revive (minor), pallet-revive-uapi (patch), pallet-revive-fixtures (minor)
#13678: [pallet-revive] PVM performance: early return on mem read
Small optimization: We can return early when the read would fail anyway.
Crates: pallet-revive (patch)
Staking & Elections
🛠️ Runtime Dev
#13690: pallet-staking-async: Only move spendable funds out of expired era pots
When an era expires, its reward pot now only sends its spendable balance to UnclaimedRewardHandler. Locked funds stay in the pot.
Crates: pallet-staking-async (patch)
Assets
🛠️ Runtime Dev
💥 #13256: pallet-psm: reject the None origin as a PSM admin
Also for Runtime User
The None origin is now rejected both when storing an admin, via the new Error::InvalidAdminOrigin, and when checking one in ensure_psm_admin. The latter also covers admin records already in storage.
Previously a PSM admin could be set to the system None origin. The pool rejects unsigned PSM calls, but a block author bypasses the pool, so such a PSM would be managed by whoever authors the block. This is now fixed.
Breaking: pallet-psm
💥 #13626: pallet-psm: simplify PSM account handling
PSMs no longer manage provider refs on their reserve account and fee destination, so both follow the standard account rules.
create_psmandremove_psmno longer touch provider refs.mintandredeemwaive the fee when the fee destination can't receive it.add_external_assetrequires the reserve account to be able to receive the external
asset, and fails withReserveCannotReceiveotherwise. For a non-sufficient asset, create the reserve's asset account first (e.g. withtouch_other).- New
reserve_accountview function returns the reserve account of a PSM instance.
Breaking: pallet-psm
Other crates: pallet-assets (none), asset-hub-westend-runtime (patch)
#12318: pallet-balances: Clean up empty reserves & do not send the same event twice
When reserves are cleared, the corresponding storage should be cleaned up as well. So, we do not run into MaxReserves with only empty reserves. This pull request also fixes the double sending of the same event when slashing a reserved amount.
Crates: pallet-balances (patch)
#12674: Small balance fixes
Fix account preservation below the existential deposit, hold release and transfer handling, empty hold cleanup, and dust handling when removing freezes or locks. Prevent aggregate balance overflow and preserve inactive issuance when deactivating funds.
Crates: pallet-balances (patch), frame-support (patch), pallet-assets (patch)
#13627: pallet-psm: reduce debt by the amount actually burned on redeem
redeem burns the internal asset with Preservation::Expendable. When the redeemer is left below the internal asset's min_balance, burn_from also burns that remainder and lowers the issuance by more than effective_internal_net, while PsmDebt only dropped by effective_internal_net. A plain redeem could therefore leave the issuance below the debt, failing the try_state check and keeping the debt from ever reaching zero. …
Crates: pallet-psm (patch)
Parachains (protocol)
🛠️ Runtime Dev
#13009: Move on-demand coretime orders to the Coretime chain
Also for Runtime User
Adds pallet-on-demand-para, which is the first step of moving the on-demand coretime purchase functionality to the Coretime chain. …
Crates: pallet-on-demand-para (patch), polkadot-runtime-parachains (minor), rococo-runtime (patch), westend-runtime (patch), polkadot-sdk (minor)
Rust compiler versions
The crates and node binaries in this release were built and tested with the following rustc toolchain; other versions may work.
- Rust Stable:
1.93.0