This release contains the changes from polkadot-unstable2604-rc2 to polkadot-unstable2604-rc3.
⚠️ Warning: Pre-release Version
The currently published version is a pre-release and is not yet a final stable version. This release is provided for testing purposes only, and there is no guarantee that everything will work as expected. Errors and unusual behaviours of some features are to be expected.
Please use this version at your own risk and report any issues you encounter. We recommend waiting for the official release if you need a stable version.
Changelog
Changelog for Node Dev
ℹ️ These changes are relevant to: Those who build around the client side code. Alternative client builders, SMOLDOT, those who consume RPCs. These are people who are oblivious to the runtime changes. They only care about the meta-protocol, not the protocol itself.
[#11557]: Double max memory on block import
CallContext::Onchain now carries an import: bool field.
When import: true (block import), the WASM executor allocates double
the configured heap pages. When import: false (block building), behavior
is unchanged.
This is a breaking change for any code that constructs or matches on
CallContext::Onchain.
The WasmModule::new_instance trait method now requires a
HeapAllocStrategy parameter. The WasmInstance trait gains a new
set_heap_alloc_strategy method with a default no-op implementation.
WASM memory limits are no longer baked into the compiled artifact;
they are enforced per-instance at instantiation time via wasmtime
StoreLimits.
[#11900]: Polkadot: Increase block size limit on the node side
This pull request increases the block size limit allowed while building a block. This block size limit is checked on the node side. The ultimate authority about the actual block size limit is the runtime.
[#11745]: statement-store: fix unbounded growth of the evicted-hashes map
Fix unbounded growth of the evicted-statement map used for re-gossip suppression.
Naturally-expired statements are no longer inserted into the map, and the map is
now bounded to max_total_statements entries with deadline-ordered purging.
[#11474]: Support multiple IndexOperation::Renew calls within a single extrinsic index in sc-client-db
Previously apply_index_ops used HashMap<u32, DbHash> so multiple Renew
operations at the same extrinsic index silently overwrote each other, blocking
batch-renewal inherents (e.g. the Bulletin chain's process_auto_renewals).
A new DbExtrinsic::MultiRenew variant carries all renewed hashes for an
extrinsic; single-renewal extrinsics still produce DbExtrinsic::Indexed
(backwards-compatible). Downstream consumers reading BODY_INDEX directly
must handle the new variant; users of the standard BlockchainDb APIs do not.
[#11650]: Update PolkaVM to latest version (0.31 → 0.33)
Updates all PolkaVM dependencies from 0.31.0 to 0.33.0.
[#11641]: Fix runtime upgrade zombienet tests exceeding max_code_size
Zombienet runtime upgrade tests now use compact (WASM_BINARY) runtimes
instead of WASM_BINARY_BLOATY, which had grown beyond the relay chain's
max_code_size limit. A new submit_sudo_runtime_upgrade helper verifies
that the inner sudo dispatch succeeded and renders any dispatch errors using
runtime metadata (e.g. ParachainSystem::TooBig instead of raw byte indices).
[#11576]: collator-protocol revamp: use ratio for peer eviction from DB
The current implementation of prune_for_para gives edge to new peers because it uses only the
timestamp of the last bump when evicting peers from the DB. As a result, a high score collator
which is inactive for a while can easily be evicted by new peers with minimal score.
To fix this we now calculate score / time_since_last_bump ratio for each peer and evict the one
with the min value.
[#11793]: Updated wasmtime to 36.0.7
Wasmtime 35.0.0 → 36.0.7
[#11487]: statement-store: fix statement loss during major sync
Fixes two cases where statements are permanently lost during major sync.
New peers (fresh start): peers connecting while major sync is active are buffered in
deferred_peers instead of being added to the reserved set immediately. When sync ends
all buffered peers are added in one batch so each performs a fresh initial sync.
Already-connected peers (fall-behind): when a running node temporarily falls behind
and drops statements via the is_major_syncing() guard, one peer is force-disconnected
and re-added after a 1-minute window so it re-sends its statements via initial sync.
The recovery only fires when statements were actually dropped during sync.
[#11107]: Add runtime metadata detection for Aura authority ID
Adds optional runtime metadata-based detection for Aura authority ID types in polkadot-omni-node-lib.
When metadata is available, the node detects whether Aura uses sr25519 or ed25519
directly from the runtime. If detection fails or metadata is unavailable, it falls
back to chain spec ID heuristics.
A warning is emitted when the Aura authority ID type is not found in metadata.
[#11615]: statement-store: add eviction priority ordering tests
Description
Implement unit tests for "Eviction: verify the lowest-priority statements are evicted first, corner cases of priority ordering" #11534
Summary
- Extend the existing
constraints()test with eviction priority ordering corner cases:- Verify that equal priority statements are rejected with
AccountFullwhen the account is full - Verify that specific evicted statement hashes appear in the expired map
- Verify that equal priority statements are rejected with
[#11892]: statement-store: reduce sync burst interval
Reduces INITIAL_SYNC_BURST_INTERVAL from 100 ms to 10 ms. With many light clients connected, this interval determines how quickly a light client syncs and observes statements of interest. Other flows are unaffected because the polling branch is already at the end of a select! biased toward higher-priority work.
[#11600]: collator-proto/metrics: Fix blindspot in collation fetch latency metrics
The current implementation of the collation_fetch_latency metric contains a critical observability blindspot due to insufficient histogram bucket resolution.
Currently, the the collation fetching is capped at an upper bound of 5 seconds. This effectively creates a black box for investigating latency events. Any fetch operation exceeding the 5s threshold is aggregated into the final bucket, regardless if the fetching took 30s or 1h. This obscures the true distribution of network delays and prevents accurate performance profiling for high-latency scenarios.
The discrepancy was identified with https://github.com/lexnv/block-confidence-monitor and confirmed via manual analysis of the logs. Without granular visibility into these outliers, we cannot effectively measure the success of our block confidence / debug bottlenecks in validator-side protocols.
While at it, have increased the granularity of other buckets which might be relevant.
Part of the block confidence work:
[#11629]: collator-protocol: Enhance visibility into failed to fetch collations
This PR enhances the observability of our logs when validators refuse to fetch advertised collations.
The validator keeps track of advertised collations from collators.
Then, the validator checks if it has sufficient slots (is_slot_available) and if the collation can be seconded (can_second).
If either of the mentioned functions fails, the validator silently ignores the collation.
This PR aims to surface this behavior and help investigate further issues.
This PR is already deployed on validators and has surfaced:
Part of:
[#11918]: Invalidate storage transaction cache on state version change
Fix a cache-bleed bug. The cache stored the pre-computed storage root and transaction without
recording which StateVersion was used to build them.
The cache now stores the state_version it was built with and is invalidated and recomputed
whenever a different version is requested.
[#10757]: rpc-server: Use own thread pool for RPC functionality
Right now the RPC is using the same thread pool as the rest of the node. When there is high usage and the node is running out of threads for blocking futures, RPC calls start to take very long time. This may also results in problems with other node functionality that would also be blocked by waiting for new threads. This pull request assigns the rpc server its own thread pool that gets the same number as threads as max_connections. These threads are only started on demand, but should allow any RPC connection to have at least one thread to run blocking tasks.
In a next step we should finally look into the performance metering of RPC calls and ensure that we have some proper rate limit in place to give every connection a fair share.
Hopefully helps with: #10719
[#11838]: statement-store: fix metrics accuracy
Description
Fixes accuracy gaps and clarify help text in statement store and network metrics.
Integration
No integration needed.
[#11626]: Fix paths_via_relay_parent returning paths longer than scheduling_lookahead
Fixes a bug where paths_via_relay_parent() in the implicit view could return
paths longer than scheduling_lookahead during relay chain forks. Fork leaves
kept old blocks in block_info_storage, and the path traversal walked through
all of them regardless of which leaf they belonged to.
This caused is_slot_available to compute valid_len = 0 for valid scheduling
parents, unconditionally rejecting collation advertisements with
SecondedLimitReached.
The fix derives paths directly from each leaf's allowed_relay_parents_contiguous
instead of walking block_info_storage, bounding path length to
scheduling_lookahead.
Fixes #11625.
[#11762]: Bump collation in_peers to 300 for the experimental collator protocol
Bump MAX_AUTHORITY_INCOMING_STREAMS in peer_set to be in line with CONNECTED_PEERS_PARA_LIMIT
in experimental collator protocol.
[#11673]: statement-store: Add malformed input unit tests
Add unit tests for malformed input handling in statement-store crates.
[#12017]: Improve the sync
We should not panic and handle it more gracefully.
[#11611]: statement-store: add concurrent and multi-peer propagation tests
Implements unit tests for "Propagation under normal load"
[#11521]: Implement bitswap_v1_get RPC method
Implement bitswap_v1_get RPC method according to the spec.
[#11906]: cli: Update list of AH Polkadot relay chain side peers
This PR updates the list of relay chain side peer IDs corresponding to the AH Polkadot collators.
Previously, the list contained the parachain side peer IDs. This behavior resulted in all collations being held off for 300 ms.
The peer IDs have been confirmed with their respective node operators.
[#11685]: Pass NodeExtraArgs through Dev Chain
Enables the statement store when running polkadot-omni-node in dev mode with
--enable-statement-store.
The dev Aura node path now wires the statement store the same way as the regular node path:
it registers the statement network protocol, builds the store, exposes the RPCs, and passes
the statement store extension into offchain workers.
Also wires up the storage monitor in dev mode and emits warnings when
collation-specific flags (--authoring-policy, --export-pov,
--max-pov-percentage) are passed in dev mode where they have no effect.
NodeExtraArgs is now destructured in start_dev_node so the compiler
will enforce that any newly added fields are explicitly handled.
[#11628]: fix: slot-based collator node exits immediately on startup
Regression from #11381: the wait_for_aura init wrapper was spawned as an essential task, but it
completes immediately after spawning the actual long-running collator tasks, causing the node to
shut down. Use spawn_handle() instead. Only affects --authoring=slot-based collators.
[#11772]: Prospective parachains support for older relay parents
Decouples a candidate's relay parent from its scheduling parent in the prospective-parachains
subsystem, enabling V3 descriptors whose relay parent may be much older than the scheduling
parent (potentially spanning multiple sessions, bounded by
HostConfiguration.max_relay_parent_session_age).
Additionally:
- fixes a bug when handling v3 candidates in statement distribution, which was wrongly comparing
the session index of the candidate with the scheduling session. backing_constraintsruntime API computes a more up to datemin_relay_parent_number, which is the maximum
between the oldest allowed RP based on the HostConfiguration and the relay parent of the most recently backed candidate
of the para.- assumes the ParachainHost runtime API is at least v13 (
BackingConstraints). The
ParaBackingStatefallback in prospective-parachains has been removed; against a runtime
with a lower version the subsystem will fail to process active leaves.
[#11387]: docs/async_back: Align docs to latest behavior
Tiny PR to update the async backing documentation / guidelines:
- older params are no longer used
async_backing_params - UNINCLUDED_SEGMENT_CAPACITY is now 3
- no need for
experimentalfeature flags
Closes: #8804
[#11621]: Enforce current relay parent to be available
Ensures that the current relay parent is always available when building
parachain blocks in the slot-based collator. The block builder task now
uses a relay chain notification stream to keep relay chain data
up-to-date and enforces that the current relay parent is present before
proceeding with block building.
[#11667]: statement-store: read allowance on best hash
Use the best block hash instead of the finalized hash when checking statement
allowances during statement submission. This makes the store more responsive to
recent allowance changes. Allowance enforcement during eviction still uses the
finalized hash to ensure correctness. A statement accepted against best-block
state may later be evicted if that block does not get finalized, which is an
acceptable tradeoff.
[#11381]: Fix slot-based collator panic during warp sync (#11072)
When a parachain collator starts with --authoring=slot-based and performs warp sync, the slot-based-block-builder essential task immediately calls slot_duration() which requires AuraApi_slot_duration. During warp sync the runtime isn't ready, so this fails and the task returns, shutting down the node.
The lookahead collator avoids this by calling wait_for_aura() before starting. This PR adds an equivalent guard to the slot-based collator.
Manual test
Before the fix the collator panicked after the relay chain warp sync with AuraApi_slot_duration not available, which does not occur anymore now.
./target/release/polkadot-parachain \
--chain asset-hub-polkadot \
--sync warp \
--authoring=slot-based \
--tmp -- --sync warp
Closes #11072.
[#10477]: Block Bundling Node Side
Implements the node-side logic for block bundling (aka 500ms blocks) in parachains.
The main changes are in the slot-based collator: instead of building one block per core,
blocks are built as requested and distributed over the available cores.
[#11691]: statement-store: test crash mid-sync
Adds a zombienet integration test that verifies statement store recovery after a node crash mid-sync. The test submits statements concurrently to multiple nodes, kills one mid-gossip, submits more while it's down, then asserts all recoverable statements converge after restart.
[#11820]: Fix statement-distribution request cleanup on leaf deactivation
handle_deactivate_leaves in statement-distribution v2 was cleaning up outgoing
attested-candidate requests using the wrong key: it called
remove_by_scheduling_parent(*leaf) once per pruned ancestor, instead of
remove_by_scheduling_parent(pruned_rp) once per pruned relay parent. As a result,
requests tied to ancestors pruned alongside a deactivated leaf were never cleaned up,
leaking entries in the RequestManager.
The bug was introduced in #1436 (vstaging rework, 2023) and was latent because the
stale entries only caused gradual memory growth and wasted retry cycles, not
correctness failures.
Also adds unit tests covering the full cleanup contract of handle_deactivate_leaves
(implicit view pruning, per_scheduling_parent, request manager, per_session,
and the "last session's topology is retained" edge case).
[#11962]: client/db, sp-transaction-storage-proof: preserve MultiRenew submit order
#11474 changed DbExtrinsic::MultiRenew::hashes from Vec<DbHash> to BTreeSet<DbHash>,
which reordered hashes by sort order. The runtime indexes TransactionInfo in dispatch
order, so build_proof and the runtime resolved selected_chunk_index to different
chunks, causing InvalidProof and chain halt on the first proof block after a multi-renew.
Reverted MultiRenew.hashes to Vec<DbHash>, preserving submission order and duplicates
so column refcount inc/dec stays symmetric.
[#11329]: statement-store: Allow light clients to specify topic affinity
Adds explicit topic affinity to the statement protocol via a new "statement/2" protocol.
Peers can advertise which topics they care about using a bloom filter. Only matching
statements are forwarded to peers with an active affinity filter. When affinity changes,
relevant statements are re-sent. Light clients must advertise affinity before receiving
statements. Includes rate limiting on affinity advertisements.
[#11237]: Add statement store e2e integration tests
Add 2 integration tests and 2 bench tests for the statement store subsystem:
- statement_store_basic_propagation: basic submission and cross-node propagation via genesis-injected allowances
- statement_store_check_propagation_and_quota_invariants: concurrent multi-account propagation, quota enforcement, and priority eviction via sudo allowances
- statement_store_memory_stress_bench: memory usage under extreme load
- statement_store_latency_bench: submission and propagation latency measurement
Addspawn_network_with_injected_allowances()helper for per-participant quota configuration.
[#11893]: Raise relay-chain BlockLength to 10 MiB on test runtimes; decouple AttestedCandidate response cap
polkadot-node-network-protocol decouples the libp2p response-size cap for the
AttestedCandidateV2 request-response protocol from polkadot_primitives::MAX_CODE_SIZE
and pins it at a flat 8 MiB.
Adds an end-to-end test in polkadot-test-service.
[#11617]: statement-store: add channel replacement logic tests
Description
Implement unit tests for "Channel replacement: verify only higher-priority statements replace existing entries, corner cases of replacement logic." #11534
Summary
-
channel_replacement_only_higher_priority_succeeds-> verifies lower/equal priority rejected withChannelPriorityTooLow, higher priority replaces, one-per-channel invariant preserved -
channel_replacement_with_size_increase_evicts_others-> verifies that replacing a channel message with a larger one triggers additional eviction of lowest-priority non-channel statements to satisfy size constraints
[#11456]: Collation generation: pass validation data from caller, fetch session from scheduling parent
Makes handle_submit_collation robust for V3 candidate descriptors where
the relay parent can be old/ancient (pruned state). Instead of fetching
PersistedValidationData and session_index from the relay parent's
runtime state (which may be unavailable), the caller now provides
PersistedValidationData directly in SubmitCollationParams, and the
session index is now fetched correctly from the scheduling parent (which
always has available state).
The parent_head field is removed from SubmitCollationParams — callers should set
validation_data.parent_head directly instead.
All runtime API calls in the submit collation path now correctly target the scheduling
parent rather than the relay parent.
CandidateDescriptorV2::new_v3 now takes an explicit scheduling_session_index
parameter instead of hardcoding the offset to 0.
[#11701]: statement-store: Test flooding detection
Fixed a bug with double peer state cleanup. Added two zombienet integration tests that verify statement-store flooding detection
[#11274]: Statement store: Latency bench cli sudo
Extend the statement-latency-bench CLI tool to automatically set up per-account statement allowances via sudo extrinsics.
Added a separate setup-allowances binary that issues subxt-based Sudo(batch_all(set_storage(...))) calls to write :statement_allowance: keys for each benchmark account. Extracted shared subxt configuration and test utilities into subxt_client and test_utils modules gated behind the test-helpers feature.
[#10468]: Publish indexed transactions with BLAKE2b hashes to IPFS DHT
Add --ipfs-bootnodes flag for specifying IPFS bootnodes. If passed along with --ipfs-server, the node will register as a content provider in IPFS DHT of indexed transactions with BLAKE2b hashes of the last two weeks (or pruning depth if smaller).
Changelog for Runtime Dev
ℹ️ These changes are relevant to: All of those who rely on the runtime. A parachain team that is using a pallet. A DApp that is using a pallet. These are people who care about the protocol (WASM, not the meta-protocol (client).)
[#11823]: Refactor asset-conversion tx payment fee correction
Fixes a bug where the AssetTxFeePaid event reported an incorrect actual_fee when paying
in the native asset via the asset-conversion extension (asset_id == A::get()). The returned
fee amount was double-subtracting the refund, under-reporting the fee in the event.
Refactors SwapAssetAdapter::correct_and_deposit_fee in pallet-asset-conversion-tx-payment
to handle all edge cases gracefully during post-dispatch fee correction. Adds comprehensive
test coverage for fee correction paths including account killed, account blocked, pool
drained, and native account with no free balance scenarios.
[#11354]: Snowbridge: API to Check Inbound Nonce Consumption
Adds a runtime API so off-chain callers can check whether an inbound message from Ethereum (by nonce) has already been relayed or consumed on Bridge Hub.
[#11432]: [frame-support] Add fungible metadata and lifetime traits with ItemOf support
Adds fungible::metadata and fungible::lifetime modules mirroring the corresponding
fungibles APIs:
- metadata:
InspectandMutatetraits for fungible token metadata (name, symbol,
decimals). - lifetime:
Createtrait for creating a new fungible asset.
These modules initially exist to support functionality in the ItemOf adapter for fungibles.
When a fungibles implementation provides the corresponding traits
(fungibles::metadata::Inspect, fungibles::metadata::Mutate, fungibles::lifetime::Create),
the ItemOf wrapper implements the fungible equivalents, allowing a single asset from a
fungibles set to be used where fungible metadata or create is expected.
[#11901]: UnionOf: implement metadata traits
Adds fungibles::metadata::Inspect and fungibles::metadata::Mutate impls
to both fungibles::UnionOf and fungible::UnionOf. Each dispatches to the
Left or Right backend via the existing Criterion.
Unblocks runtimes that need a metadata-aware fungibles surface across two
pallet instances, e.g., a pallet consuming
UnionOf<Assets, ForeignAssets, ...> that needs to read decimals/name/symbol.
metadata::MetadataDeposit is intentionally not implemented: its method
signature has no AssetId parameter and can't be dispatched via Criterion.
[#10482]: Recovery pallet modernization
Revamps the recovery pallet to support multiple recovery groups and many new features.
[#12123]: Fix migrations for recovery & parachain-system pallets
Changes:
- pallet-recovery: be more lenient towards broken accounts to keep their recovery config
- parachain-system: clear old storage value to prevent decoding error
[#11777]: eth-rpc: single-pass event processing for receipt extraction
Summary
- Process block events in a single pass instead of re-scanning per extrinsic, reducing O(N×E) to O(E)
- Merge two integration tests into one that validates revert and logs are correctly attributed within the same block
[#10726]: [Penpal] cleanup XCM config setup regarding assets
Closes #7314 by implementing all the subtasks mentioned in #7314 (comment).
Changes
Essentially, the main driver of all changes is that we adjust the Penpal runtime as follows:
- Make the native token the base token for buying weight (before it was a hybrid set up, probably not 100% intentional).
- Merge the
Assetsand theForeignAssetspallet into one pallet calledAssets, as the local assets can also be identified with a location starting withparents: 0. - Give the pallet-asset-conversion a genesis config so that we can easily set up pools at genesis instead of redundantly calling the setup macro with the same args.
Test Changes
I tried to keep the changes minimal in the tests in order to not harm any previously established invariants. Hence, in most cases I just did:
- Add a PEN<>WND pool in order to be able to pay xcm execution fees in WND
- Replaced the Penpal's teleportable asset with it's new location based version.
- In very few cases, I switched from WND to PEN to make the tests easier, when I was sure that no invariants would be harmed.
- The rest should only be renamings.
[#11650]: Update PolkaVM to latest version (0.31 → 0.33)
Updates all PolkaVM dependencies from 0.31.0 to 0.33.0.
[#11522]: eth-rpc: Add trace logging for receipt lookup debugging
Add trace logging to receipt handling to help diagnose intermittent receipt retrieval failures for finalized transactions, observed while running revive differential test benchmarks.
[#11604]: Refactor: candidate-validation fetches executor_params itself
Description
Remove executor_params from CandidateValidationMessage::ValidateFromExhaustive
and have candidate-validation derive the session index from the candidate
descriptor and fetch executor_params via the runtime API internally.
This simplifies backing, approval-voting, and dispute-coordinator by removing
executor_params threading through Action::LaunchApproval, RetryApprovalInfo,
ParticipationRequest, BackgroundValidationParams, and PerSessionCache.
This PR is a follow up of this comment
[#11641]: Fix runtime upgrade zombienet tests exceeding max_code_size
The wasm-builder's compaction and compression decision now uses the actual
blob build profile rather than the outer cargo profile. Since debug builds
already compile WASM blobs with the Release profile, they now also get
compacted and compressed, producing a properly sized WASM_BINARY.
Previously WASM_BINARY in debug builds was identical to WASM_BINARY_BLOATY.
[#11510]: frame-omni-bencher: better diagnostic on insufficient data points
When a benchmark is run with not enough steps and too many points are skipped then it can make the analysis panic. This PR improves the panic message and gives precise information about which benchmark is at fault.
[#11573]: Fix can_inc_consumer check blocking session key rotation in pallet_session
Check consumer capacity only when we actually increment the consumer count (first-time local registration or external-to-local transition), not on key rotation.
[#11804]: Runtime safety and fee precision fixes
This PR applies a set of small runtime-facing fixes across FRAME pallets.
pallet-asset-conversion
- Changes
Config::LPFeefromGet<u32>(tenths-of-a-percent encoding) toGet<Permill>. - Reworks swap math to use
Permill::ACCURACYandleft_from_one()so fee handling is explicit and avoids underflow-prone arithmetic. - Updates runtimes and mocks to configure LP fee via
Permill.
frame-system
- Extends
Event::CodeUpdatedto include the updated runtime code hash:CodeUpdated { hash }. - Emits this hash from
update_code_in_storagewhen scheduling or applying a runtime code update.
pallet-psm
- Adds a
try-runtimepre_upgradeguard that checks configured asset decimals before initialization.
pallet-balances
- Removes a tautological internal assertion in account mutation flow.
pallet-tips
- Hardens tip payout by returning
NoActiveTipperswhen all recorded tippers become inactive instead of indexing an empty tip set. - Adds a regression test for the
NoActiveTippersclose-path.
[#11793]: Updated wasmtime to 36.0.7
Wasmtime 35.0.0 → 36.0.7
[#11960]: sp-hop: HOP runtime API primitives crate
Adds the sp-hop crate, which defines the HopRuntimeApi runtime API
trait for the Hand-Off Protocol (HOP). HOP is an ephemeral, peer-to-peer
data pool service that holds data node-side until it is claimed or
promoted to on-chain storage.
HopRuntimeApi is the contract between the HOP node service (sc-hop,
added in a follow-up PR) and the runtime:
can_account_promote(who, data_len)— authorization check.create_promotion_extrinsic(data, signer, signature, submit_timestamp)—
constructs the unsigned promotion extrinsic carrying the user's
submit-time signer, signature, and timestamp so the runtime pallet can
re-verify consent on-chain.max_promotion_size()— runtime-defined upper bound on a single
promotion blob.is_promoted_on_chain(hash)— used by the node's maintenance task to
confirm on-chain inclusion before flagging an entry as promoted.
This PR introduces the crate only; it has no runtime or node consumers
yet and is therefore a no-op at runtime. The follow-up PR adds sc-hop
and the polkadot-omni-node integration that depend on this trait.
[#11395]: bandersnatch: extract vrf_sign_io/vrf_verify_io helpers
Extract vrf_sign_io and vrf_verify_io helper functions in the bandersnatch
VRF module and reuse them in both the VRF trait impls and the TraitPair
sign/verify methods (with zero input). This removes duplicated
proving/verifying logic and fixes the TraitPair::sign implementation which
was previously using a manual Schnorr scheme inconsistent with verification.
[#11726]: eth-rpc: Bulk INSERT/DELETE and commit per-block writes atomically
Summary
- Query SQLite's max variable limit at startup and use it to chunk bulk INSERTs and DELETEs, avoiding bind-parameter overflows
- Replace per-row individual inserts with batched bulk inserts
- Commit transaction hashes, logs, and block mapping in a single SQLite transaction per block, same for deletes
- Use
INSERT OR REPLACEfor logs (previously plain INSERT) to match transaction_hashes and prevent UNIQUE constraint failures if the EXISTS dedup guard is bypassed
[#11425]: fix(pallet-multi-asset-bounties): use non-destructive read in calculate_payout()
Fix calculate_payout() using ChildBountiesValuePerParent::take() instead of get().
The destructive take() deletes the storage entry on first call, causing
BountyPayoutProcessed to emit an incorrect payout value when check_status() calls
calculate_payout() a second time on the success path. Replaced take() with get()
and moved storage cleanup to remove_bounty().
[#11897]: Reorder VerifySignature extension variants so Disabled encodes to 0x00
Swap the order of the Disabled and Signed { .. } variants in
pallet_verify_signature::VerifySignature so that Disabled is now the first variant
and encodes as the SCALE byte 0x00, while Signed { .. } is the second variant and
encodes with tag 0x01.
The motivation is signer compatibility. Generic signers can default an extension to its
passthrough state when that state encodes to a single zero byte — the same convention used
by other simple, defaultable transaction extensions (CheckMetadataHash's Mode::Disabled,
Option::None, bool::false). Under the previous variant order, the disabled state of
VerifySignature encoded as 0x01, which a signer cannot produce without knowing the
enum's specific variant layout.
On-chain encoding change. This is a breaking change to the SCALE encoding of the
extension: the variant tags for Disabled and Signed are flipped. To my knowledge there
is no production runtime using this extension right now, but the breaking change is
reflected in the major bump of the pallet.
[#11806]: [Staking] Refactor reward mode selection to use storage
Refactor of the payout path in pallet-staking-async to select the reward mode
(DAP pot vs. legacy minting) based on the DisableMintingGuard storage value
instead of checking for the existence of the era's reward pot account.
[#11545]: Support State Overrides in ethCall
Description
Follow-up to #11075, scoped down to just eth_call state overrides per the feedback there. The original PR bundled state overrides together with eth_simulateV1 (multi-block simulation, block overrides, transfer tracing, validation), which reviewers felt added too much surface area at once. This PR extracts the state override piece on its own, which is the most immediately useful part for the ecosystem and for our team.
Adds state override support to the eth_call JSON-RPC method, matching the Geth state override specification. This lets callers temporarily modify account balance, nonce, code, and storage during eth_call simulations without touching on-chain state.
Tools like Foundry, Hardhat, Tenderly, and really any dApp doing pre-flight simulation rely on state overrides heavily. Without them, our eth_call was strictly less capable than what the ecosystem expects.
Why apply overrides in the runtime, not at the node level?
During the review of #11075, there was a suggestion to apply state overrides at the node level using OverlayedChanges, bypassing the runtime entirely. That approach works well if you control the node (Anvil does exactly this with a custom executor), but pallet-revive's eth-rpc is a standalone process that talks to the node over WebSocket via state_call. It has no access to OverlayedChanges or StateMachine. Adding a custom state_call_with_overrides RPC to substrate core would need buy-in from the SDK team and would be a much larger change. Applying overrides inside the runtime, within the dry-run's transactional context that always rolls back, keeps everything self-contained and works with any node out of the box.
Why extend DryRunConfig instead of adding a new runtime API method?
The codebase already had the pattern of eth_transact → eth_transact_with_config (a new method added when DryRunConfig was introduced). Adding eth_transact_with_state_overrides as yet another method felt like it would keep accumulating, and the fallback chain in the RPC layer was already two levels deep. Instead, we append state_overrides as a trailing field on DryRunConfig and rely on sp_api's existing behavior: runtime API argument decoding uses Decode::decode (not decode_all), so trailing bytes from a newer RPC are silently ignored by an older runtime. For the reverse direction, a custom Decode impl defaults the missing field. No new method, no new fallback layer, backwards compatible in both directions. The doc comments on DryRunConfig lay out the full reasoning and constraints.
Integration
This PR introduces a third optional parameter to eth_call:
The stateOverrides parameter is an address-keyed map where each entry can override:
balance: fake balance before executing the callnonce: fake noncecode: inject EVM or PVM bytecode (promotes EOAs to contracts if needed)state: full storage replacement (wipes everything, then writes the provided slots)stateDiff: partial storage patch (only touches specified slots)movePrecompileToAddress: not yet implemented, silently ignored
Existing callers that don't pass the third parameter are completely unaffected. The parameter is Option-typed and defaults to None.
If you're a downstream consumer of pallet-revive's runtime API: the DryRunConfig struct has a new state_overrides field. It's Option<StateOverrideSet> and defaults to None, so existing code that constructs DryRunConfig via Default or the builder pattern will continue to work without changes. The SCALE encoding is backwards-compatible in both directions as described above.
Review Notes
I'd suggest starting with the type definitions, then moving to the application logic, and finally the RPC wiring. The tests are worth skimming at the end to see the coverage.
Type definitions
The core types live in rpc_types_gen.rs:
StateOverrideSet: newtype aroundBTreeMap<Address, StateOverride>with builder methods and iterator impls. Both the set-level and per-account builder patterns are provided (e.g.StateOverrideSet::new().with_balance(addr, val)).StorageOverride: enum encoding the mutual exclusivity ofstatevsstateDiffat the type level. Uses#[serde(flatten)]with externally-tagged enum representation so the JSON stays flat ({"state": {...}}or{"stateDiff": {...}}), matching exactly what Geth produces.StateOverride: the per-account override struct. All fields optional.
DryRunConfig extension
DryRunConfig in rpc_types.rs has a new state_overrides: Option<StateOverrideSet> trailing field. The struct now has a manual Default (because perform_balance_checks defaults to Some(true)) and a custom Decode that tolerates missing bytes for the new field. The backwards compatibility story is documented extensively in the doc comments.
State override application logic
This is where most of the actual logic lives: state_overrides.rs. The entry point iterates the override set and dispatches to per-field handlers. The ordering (balance, nonce, code, storage) is intentional, because code overrides can promote an EOA to a contract, which needs to happen before storage overrides can write to the contract's child trie.
Key things to look at:
- Code override: detects PVM vs EVM via
BLOB_MAGIC, creates aContractBlob, stores code + metadata, promotes EOA to contract if needed. ChecksAllowEVMBytecodefor EVM code. - Storage override:
Stateclears the child trie then writes;StateDiffwrites without clearing.
All of this runs inside the dry-run's transactional context, so mutations are rolled back after the call.
RPC wiring
execution_apis.rs: addedstate_overridesas the third parameter to theeth_calltrait methodlib.rs: forwards the overrides toruntime_api.dry_run()client/runtime_api.rs:dry_run()now packs state overrides intoDryRunConfigand sends them througheth_transact_with_config
Call site ordering
In dry_run_eth_transact, state overrides are applied after prepare_dry_run (which only bumps the nonce). This ordering matters: if a user overrides the nonce of the from account, their override takes precedence over the nonce bump.
Tests
24 integration tests starting here, all going through alloy-provider's Provider::call().overrides(). This is deliberate, by going through alloy we're not just testing our logic but also verifying that the JSON-RPC wire format is compatible with what the wider Ethereum ecosystem actually sends. If alloy can talk to us, so can ethers.js, viem, Foundry, etc.
Coverage at a glance:
Balance: override unfunded account, override to zero, override on from account (interaction with prepare_dry_run), nonce override.
Code (full transition matrix): empty→EVM, empty→PVM, EOA→EVM, EOA→PVM, EVM→EVM, EVM→PVM, PVM→EVM. All call Callee.echo(N) and assert the return value, including PVM variants (Resolc-compiled Callee).
Storage: stateDiff patches a slot, state replaces a slot, state clears unspecified slots (new TwoSlots.sol fixture), stateDiff preserves unspecified slots, multiple slots, empty state map clears all, zero-value diff write, combined code+storage, storage on EOA fails.
Edge cases: overrides don't persist, empty override set, multiple accounts, combined balance+code.
New dependencies (dev-only)
alloy-network, alloy-primitives, alloy-provider, alloy-rpc-types, alloy-signer-local added to the workspace and inherited by the eth-rpc crate as dev-dependencies. Only compiled during cargo test.
Known limitations
movePrecompileToAddressis accepted but silently ignored.
[#11763]: [westend] Remove pallet_treasury from RC and clean up satellite matchers
Remove pallet_treasury entirely from Westend relay chain.
Drain residual balances from the legacy py/trsry-derived account into the local
DAP satellite buffer on the relay and on each Westend system parachain
(bridge-hub, collectives, coretime, people).
Remove RelayTreasuryLocation matchers from all Westend system parachains.
Closes #11705.
[#11538]: [eth-rpc] Detect and backfill gaps in finalized block subscriptions
Temporary connection drops can result in missed blocks, leaving gaps in the local database and causing incomplete results for RPC methods such as eth_getLogs and eth_getBlockByNumber. This PR introduces automatic gap detection in the finalized block subscription and backfills missing ranges via a background worker.
- Gap detection: When a newly finalized block arrives with a number higher than expected, the skipped range is queued for backfill.
- Gap-fill queue: A bounded in-memory channel (capacity: 32), non-blocking; a separate atomic counter tracks queued and in-flight requests.
- Gap-filler task: A background worker processes requests sequentially, reusing sync_backward_range; it does not update Head/Tail sync labels or the first_evm_block boundary.
- Sync state (Head advancement): The sync head does not advance while gap fills are in flight, ensuring continuity of the synced block range.
[#11912]: [pallet-assets-precompiles] Charge DepositEvent by data length, not topic count
Summary
deposit_event in the assets ERC-20 precompile passed topics.len() for both the num_topic and len fields of RuntimeCosts::DepositEvent. The len field is the byte length of the event data payload, so the per-byte data cost was charged against the topic count (always 3 for the ERC-20 events emitted here) instead of the actual payload size — undercharging every Transfer and Approval emitted via this precompile by 7,640,746 ref_time and making its metering inconsistent with the EVM LOG_n path in pallet-revive, which correctly passes the data byte length.
Changes
substrate/frame/assets/precompiles/src/lib.rs: passdata.len()toRuntimeCosts::DepositEvent { len }.substrate/frame/assets/precompiles/src/tests.rs: adddeposit_event_charges_data_byte_lengthregression test that asserts a precompiletransfer'sweight_consumedequalsWeightInfo::transfer() + DepositEvent{num_topic: 3, len: 32}.weight(). Verified to pass with the fix and fail without it (off by exactly the per-byte event-charge delta).
Test plan
- Verified the new regression test fails when the bug is reintroduced and passes when the fix is in place
[#11581]: Support State Overrides in Tracing
Description
Adds support for Geth-compatible state overrides in debug_traceCall, extending the state override support introduced in #11545 (which added them to eth_call).
Per the Geth specification, debug_traceCall accepts a config object that is a superset of the base tracer config, adding stateOverrides for ephemerally modifying account state during traced execution.
Changes
Pallet (pallet-revive)
TracingConfigtype — New backwards-compatible config type following the same pattern asDryRunConfigfrom #11545 (customDecodeimpl, append-only fields, must be the last runtime API argument).trace_call_with_configruntime API — New method that applies state overrides then delegates to the existingtrace_call. Implemented in the macro so it can callSelf::trace_calldirectly.state_overridesmodule made#[doc(hidden)] pubso the macro-generated code can access it from downstream runtime crates.
ETH-RPC (pallet-revive-eth-rpc)
TraceCallConfigtype — ExtendsTracerConfig(flattened) with an optionalstateOverridesfield, matching Geth'sTraceCallConfigschema.debug_traceCallsignature updated to acceptOption<TraceCallConfig>. When state overrides are present, the RPC usestrace_call_with_config; otherwise it falls back totrace_callfor backwards compatibility with older runtimes.
Integration
Existing debug_traceCall callers are unaffected — the config parameter remains optional, and omitting stateOverrides uses the original code path. Callers wanting state overrides pass them in the config object alongside the tracer settings:
{
"tracer": "callTracer",
"stateOverrides": {
"0x1234...": {
"balance": "0xDE0B6B3A7640000",
"code": "0x6080..."
}
}
}Review Notes
TracingConfigmirrorsDryRunConfig's backwards compatibility strategy documented in #11545. The customDecodeimpl defaults missing fields, andsp_api'sDecode::decode(notdecode_all) discards trailing bytes from newer encodings.- The macro impl applies overrides before delegating to
Self::trace_call, keeping the tracing logic in one place. - A single integration test (
test_state_override_trace_call) verifies end-to-end functionality using alloy'sDebugApi::debug_trace_call_callframewith state overrides.
[#11676]: [pallet-assets] Reject delegatecall into pallet-assets ERC20 precompile
There is no legitimate use case for delegatecalling into the asset precompile. This matches the precedent set by the Storage precompile, which already enforces a delegatecall check (in the opposite direction — it requires delegatecall).
Changes
lib.rs: AddERR_DELEGATECALL_DENIEDconst andis_delegate_call()guard before any dispatch logictests.rs: Adddelegatecall_is_rejectedtest using theCaller.solfixture
Test plan
-
cargo test -p pallet-assets-precompiles— all 67 tests pass -
delegatecall_is_rejectedverifies the guard rejects delegatecall via theCallerfixture contract
[#11881]: Make pallet-dap-satellite more generic
The DAP satellite pallet is being converted into a generic Accumulate-and-Forward pallet, with
the purpose of pooling tokens of a given type into an accumulation account, which is then
periodically sent (forwarded) to a specified destination.
Notable changes:
- Crate renamed from
pallet-dap-satellitetopallet-accumulate-and-forward - The
SendToDaptrait is replaced by theForwardertrait - The pallet IDs value changes from
*b"dap/satl"to*b"acf/dott"(at this point we
are only forwarding DOT tokens, but new IDs should be added for other future tokens) - Multiple other events and constants get renamed accordingly
- The old treasury is now drained via
DrainLegacyTreasuryToAccumulationAccount
[#11738]: MMR: check verify_ancestor() output
MMR: check verify_ancestor() output
[#11839]: pallet_revive: Fix dispatch_as_fallback_account
Without this fix we stripped any call filters existing on the origin.
[#11819]: pallet-psm: support external assets with different decimal precision
Previously pallet-psm rejected any external stablecoin whose decimals did
not match pUSD. This change normalizes to pUSD units internally so the PSM
can approve assets with arbitrary decimal precision within a safe range.
Core changes:
- New storage: per-asset
AssetDecimalssnapshot and pallet-wide
StableDecimalssnapshot. Storage version bumped to 2. - Conversion helpers
external_to_pusd/pusd_to_externalwith checked
arithmetic andMAX_DECIMALS_DIFF = 24to prevent overflow. mintandredeemuse round-trip rounding. Truncation dust stays in the
caller's wallet on both paths (symmetric behavior), no value is trapped
in the reserve and no hidden dust is routed to the fee destination.PsmDebtnow denominates in pUSD units so aggregate ceilings and issuance
checks are meaningful across mixed-decimal assets.- Runtime drift guard:
mint/redeemreturnDecimalsMismatchif live
metadata diverges from the registration snapshot; that asset halts until
governance intervenes. - New errors:
DecimalsRangeExceeded,ConversionOverflow,
AmountTooSmallAfterConversion.
Migrations:
InitializePsmnow also seedsStableDecimalsfrom live metadata if
missing, and snapshotsAssetDecimalsfor any new assets it adds.- New one-shot
PopulateDecimalsmigration backfillsStableDecimalsand
AssetDecimalsfor chains that approved external assets before this
upgrade. Out-of-range assets are auto-disabled (migration does not fail);
try-runtimepost_upgradesurfaces the anomaly to operators.
[#11507]: [asset-hub-westend] Add revive_debug cfg for DebugEnabled
debug_trace* RPCs (debug_traceTransaction, debug_traceBlockByNumber, debug_traceCall)
only work when pallet-revive's DebugEnabled config is set to true. Currently only the
dev-node has this enabled, but the dev-node is a simplified environment that doesn't fully
replicate parachain runtime behavior (e.g. no PoV deduplication). To get accurate debug
tracing data, it needs to run on actual parachain runtimes.
This PR uses a plain cfg flag so debug mode can be toggled at build
time without code changes.
Build with:
RUSTFLAGS="--cfg revive_debug" cargo build -p asset-hub-westend-runtime --releaseOther runtimes can adopt the same pattern by using
ConstBool<{ cfg!(revive_debug) }> for DebugEnabled.
[#11822]: Remove deprecated CurrencyAdapter from pallet-transaction-payment
Removes the deprecated CurrencyAdapter from pallet-transaction-payment. This adapter was
deprecated since March 2024 in favor of FungibleAdapter. Runtimes still using
CurrencyAdapter must migrate to FungibleAdapter.
[#11939]: Add TransactionStorageApi v2 with indexed_transactions function
This bumps TransactionStorageApi to v2 and offers the option to query data referenced in the blocks via indexed_transactions.
[#11811]: Wired pallet_dap weights on Asset Hub Westend
Replaced type WeightInfo = () with the generated weights::pallet_dap::WeightInfo
[#11690]: asset-conversion-precompiles expose pool management
Add createPool, addLiquidity, and removeLiquidity functions to the asset-conversion precompile, enabling EVM contracts to manage liquidity pools directly. Also refactors common helpers (caller lookup, path validation) for reuse across swap and liquidity operations.
[#11791]: Make block producer overridable for non-Aura chains
Introduces a BlockProducer trait used by xcm-emulator to drive slot duration
and pre-runtime digest construction when emulating a parachain block. The default
AuraBlockProducer<T> impl preserves the previous behaviour (slot derived from
pallet_aura::Pallet::<T>::slot_duration() and an Aura PreRuntime digest under
AURA_ENGINE_ID), so existing Aura-based decl_test_parachains! invocations are
unaffected.
The decl_test_parachains! macro gains an optional BlockProducer: field. Nimbus-based
parachains (e.g. Moonbeam) can now plug in a custom producer that emits a Nimbus
pre-runtime digest and a bespoke slot duration instead of being forced to implement
pallet_aura::Config just to participate in xcm-emulator integration tests.
Example:
decl_test_parachains! {
pub struct MyPara {
// ...
core = {
XcmpMessageHandler: my_runtime::XcmpQueue,
LocationToAccountId: my_runtime::LocationToAccountId,
ParachainInfo: my_runtime::ParachainInfo,
MessageOrigin: cumulus_primitives_core::AggregateMessageOrigin,
BlockProducer: MyNimbusBlockProducer,
},
// ...
}
}Downstream crates that implement Parachain directly (rather than via the macro)
must now also provide a type BlockProducer: BlockProducer associated type.
[#11655]: [eth-rpc] Handle event decode errors across runtime upgrades
Motivation
During backward sync, eth-rpc processes historical blocks using the current runtime's metadata. If the event layout differs between the current runtime and the runtime that produced those blocks (e.g., the Balances pallet gained new event variants in #7250), event decoding fails and receipts are lost.
Changes
Replace events.has::() with an event iterator that logs and skips decode errors, checks revert status by pallet/variant name, and collects ContractEmitted logs in a single pass — so that undecodable events no longer cause the entire receipt to be lost.
Behavior change: Previously, any undecodable event in a block caused the entire receipt to be lost. Now, decode errors are logged and skipped — the receipt is stored with best-effort revert status and logs.
[#11807]: PSM init: skip assets with mismatched decimals instead of panicking
Replaces the assert! in the PSM InitializePsm migration with a log and skip
when an asset's decimals don't match the stable asset. Panicking in a runtime
upgrade bricks the chain. Migrations must be infallible.
[#11818]: Add pallet-pgas-allowance with ChargePGAS transaction extension
- Introduces
pallet-pgas-allowanceproviding a newChargePGAS<T, S>transaction
extension that wraps an inner fee extensionS. - When a signed transaction dispatches a call matching
Config::CallFilterand the
signer holds enough PGAS (a trusted asset on Asset Hub), the fee is withdrawn as
afungibles::Credit. Inpost_dispatchtheactual_fee
portion is dropped and burned and the unused remainder refunded to the payer. - A
PGASFeePaid { who, actual_fee }event is emitted when the fee is paid in PGAS. - Wires the extension into
asset-hub-westend.
[#11398]: [pallet-revive] Add vesting precompile
Summary
- Add a new built-in precompile exposing Substrate's vesting pallet to EVM contracts
- Implement
IVesting.solSolidity interface with methods forvest,vestOther, andvestingBalance - Wire up the precompile in pallet-revive's builtin precompile registry and execution context
Changed files
IVesting.sol/precompiles/vesting.rs: Solidity interface and Rust implementation for vesting operationsprecompiles/builtin.rs/precompiles.rs: Register the new vesting precompileexec.rs: Expose vesting functionality to the execution contexttests.rs: Add tests for the vesting precompile
Test plan
- New vesting precompile tests pass (
vest,vestOther,vestingBalance) - Existing pallet-revive tests unaffected
[#11964]: Fix recursive XCM decoding
Fix XCM recursive decoding
[#11710]: pallet-revive: expose pre-dispatch weight runtime API
Description
This PR adds a new pallet-revive runtime API for computing the booked pre-dispatch weight of an
Ethereum transaction from its signed payload bytes.
The new API decodes the signed Ethereum transaction payload, converts it into the inner revive
call, and returns the same per-extrinsic weight contribution that frame_system::CheckWeight
books:
dispatch_info.total_weight()including extension weight- the dispatch class
base_extrinsic - the length-based proof-size charge
This is intended to expose the actual booked pre-dispatch weight for benchmarking and analysis,
without reconstructing the outer transaction from a GenericTransaction.
Integration
This changes the ReviveApi runtime API surface by adding:
fn eth_pre_dispatch_weight(tx: Vec<u8>) -> Result<Weight, EthTransactError>;Downstream consumers of ReviveApi will need regenerated metadata or updated runtime API bindings
to call the new method.
The method expects the signed Ethereum transaction payload bytes, not a GenericTransaction. This
is important because the outer transaction length contributes to proof-size booking and should come
from the real signed payload.
There is no storage migration and no change to dispatch behavior.
Review Notes
Implementation details:
- the new pallet helper decodes
TransactionSignedfrom the provided payload - it recovers the signer address and builds the
GenericTransactionfrom the signed tx - it computes the actual outer
eth_transactencoded length from the provided payload - it reuses
into_call(CreateCallMode::ExtrinsicExecution(...))to construct the inner revive call - it returns:
dispatch_info.total_weight()
+ base_extrinsic
+ Weight::from_parts(0, encoded_len)The PR also adds a regression test, eth_pre_dispatch_weight_matches_check_weight_booking, which
checks that the new API returns the same booked weight that CheckWeight would account for.
Example usage:
let weight = runtime_api.eth_pre_dispatch_weight(signed_tx_bytes)?;[#10535]: Set a proper proof size block limit
The current block limit of the revive dev node defined its proof_size as u64::MAX.
This is a reasonable setting for a standalone chain as the PoV as a limiting resource is only relevant for parachains.
However, this gives some confusing gas mapping calculations: they are correct and consistent but the resulting proof_size weights are unexpectedly high.
This PR sets the proof_size of the block limit to the same value as the Polkadot Asset Hub.
[#11716]: Add legacy NegativeImbalance support to DAP and DAP satellite
Add DapLegacyAdapter and DapSatelliteLegacyAdapter wrapper structs that implement OnUnbalanced<NegativeImbalance> from the legacy Currency trait, bridging pallets not yet migrated to fungible traits.
Wire Westend runtimes: AH referenda slash to DAP, collectives (fellowship, ambassador, alliance) and people identity slash to DAP satellite.
Closes #11704.
[#11434]: pallet-dap-satellite: Add token transfers via XCM to DAP from the satellites
Adds XCM-based transfer support to pallet-dap-satellite, enabling system parachains
that accumulate native token burns (fees, dust, coretime revenue) to periodically
teleport those funds to the central DAP buffer account on AssetHub.
New components:
sp-dap: Contains the SendToDap trait, to be implemented when funds need to be
sent to the central DAP buffer, as well as the DAP and DAP satellite pallet IDs.
xcm-builder: Two new adapters are added:
SendToDapViaTeleport— implementsSendToDapand wraps it in a storage transaction
so that any failure rolls back all local state changes.
Integration (Westend system parachains):
All five Westend system parachains (AssetHub, BridgeHub, Collectives, Coretime, People)
and the Westend relay chain are configured with SendToDapViaTeleport.
Testing:
Integration tests covering the full round-trip (satellite accumulates → on_idle fires →
XCM teleport → DAP buffer receives) are provided for the Westend relay chain and all
system parachains (via xcm-emulator). Additional unit tests are also provided.
[#11843]: Remove DDayBodyId from Westend relay chain
Post-AHM cleanup following #11796. The DDay plurality origin in AuthorizeCurrentCodeOrigin is no longer needed since governance lives on AssetHub. Simplified to EnsureRoot.
[#11755]: Bump ark-vrf to 0.5.0
Bumps ark-vrf from 0.2.2 to 0.5.0. The Bandersnatch VRF primitives in sp-core are updated
to use the new API. Plain sign/verify now uses thin VRF proofs instead of IETF proofs with a
dummy input. VRF sign and ring VRF sign/verify use the new VrfIo-based interface. No changes
to the public sp-core traits (VrfSecret, VrfPublic, RingVrfSign, RingVrfVerify).
[#11475]: revive: Skip redundant eth_block_hash RPC call in block subscription
Skip a redundant eth_block_hash RPC call during block subscription by reusing the block hash
already available in the subscription context. Adds extra logging for receipt extraction.
[#11930]: pallet-staking-async: Rotate era reward pots through a fixed-size pool
Era reward pot accounts are now drawn from a fixed pool of POT_POOL_SIZE = 200
accounts, indexed by era % POT_POOL_SIZE, instead of one fresh account per era.
This ensure we only use a fixed size of pot accounts for the lifetime of the
chain rather than growing per era.
An integrity_test enforces POT_POOL_SIZE > HistoryDepth so a slot is only
reused after its previous era has been pruned.
[#11529]: Westend Asset Hub: Integrate PSM pallet and add remote tests
Integrates the PSM pallet into the Asset Hub Westend runtime and adds remote
integration tests that run against live chain state.
Runtime changes:
- Configures
pallet-psmon Asset Hub Westend with pUSD (asset ID 50000342) - Adds
pallet-parametersfor governance-configurable maximum issuance (default 50 million pUSD) - Fee destination is the pUSD insurance fund account (
PalletId(*b"pusd/ins")) - Adds V1 migration to initialize USDT (1984) as the first external asset
with 0% minting fee and 0.01% redemption fee - Adds weights for the PSM pallet
Remote tests:
- Adds
pallet-psm-remote-testslibrary with reusable test functions - Adds
remote-ext-tests-psmbinary that fetches live Asset Hub state via RPC - Tests mint/redeem flow and circuit breaker mechanism against real asset data
- Uses snapshot caching to avoid redundant RPC requests
[#11000]: Revive, Estimate Gas with Binary Search
Description
This PR implements binary search for the gas estimation logic in the eth-rpc which means that gas estimations are no longer just simple dry runs but that binary search is now used to find the smallest gas limit at which the transaction would run.
This PR closes paritytech/contract-issues#217 and also kind of fixes paritytech/contract-issues#259 or at least makes it harder to trigger the case in which we observe it, but the underlying issue still exists.
The binary search algorithm implemented in this PR is as close as possible to that used in Geth
[#11481]: [pallet-revive] Add PVM fuel tracing
Add pvm_fuel trace steps to PVM execution traces, recording pvm fuel consumption between syscalls and after the execution loop exits.
Separate synthetic trace steps from the real syscall list: list_syscalls() now only contains syscalls contracts can actually import, while new list_trace_ops() / lookup_trace_op_index() include both real syscalls and synthetic steps like pvm_fuel.
Integration
Code using list_syscalls() for trace serialization should switch to list_trace_ops() / lookup_trace_op_index(). list_syscalls() and lookup_syscall_index() are unchanged for real syscalls.
Review Notes
- Proc-macro wraps
sync_from_executorwithenter_ecall/exit_steptracing hooks forpvm_fuel - PreparedCall::call adds a final
pvm_fueltrace after the execution loop exits - PVM JSON trace fixtures updated to include
pvm_fuelsteps
[#11815]: Parachain disputes: Add some checks and tests
Extend the parachain disputes logic with some extra plus some tests.
[#11770]: Fix PSM migration to run on first deployment
Replaces the versioned MigrateToV1 with an idempotent InitializePsm migration.
The previous MigrateToV1 used VersionedMigration<0, 1> which never ran on fresh
deployments because BeforeAllRuntimeMigrations initializes the on-chain storage
version to the in-code version (1) before migrations execute, causing the version
check to skip.
InitializePsm instead checks whether each external asset already exists and skips
it if so. This makes it safe to run multiple times with no storage version dependency.
[#11575]: [Penpal] fix genesis presets - assign proper ED to accounts'
Penpal had values below the ED for initializing asset balances for some accounts. This has not been detected as no unit tests actually use the presets. This PR fixes the invalid values, and it also adds some unit tests for validating that the presets build at least.
Closes #11558.
[#11279]: [pallet-assets] Fix ERC-20 approve semantics in precompile
The ERC-20 approve(spender, amount) spec sets the allowance to amount. The precompile was calling do_approve_transfer, which adds to the existing allowance — breaking ERC-20 compliance.
This PR fixes the precompile's approve to use set semantics by composing existing pallet-assets primitives: when overwriting a non-zero allowance, the existing approval is cancelled first so the new value replaces (not accumulates with) the old one.
Also extracts do_cancel_approval from pallet-assets for reuse by the precompile.
[#11739]: Snowbridge: tighten receipt proof validation path check
Hardens Snowbridge receipt proof verification by only accepting the
ValueMismatch { got: Some(_), expected: None } extraction path when the
mismatch path equals the queried trie key.
This prevents malformed proofs from being interpreted as valid inclusion
proofs for a different key.
Adds regression coverage in both unit and integration tests to ensure
forged receipt proofs are rejected.
[#11949]: Additional improvements for the DAP satellite pallet generalization
Additional fixes / improvements for the DAP satellite pallet generalization (#11881):
- Rename
TeleportForwardertoTeleportForwarderForAccountId32since it only works onAccountId32-type accounts
(used in all system parachains), but future users with different account types will need different trait implementations - Improved account migration testing
- Additional comments to clarify important corner-cases
[#7035]: Allow declaration and usage of multiple transaction extension versions in FRAME and primitives
This PR enhance UncheckedExtrinsic type with a new optional generic: ExtensionOtherVersions.
This generic defaults to InvalidVersion meaning there is not other version than the regular version 0. This is the same behavior as before this PR.
Breaking change
The types Preamble, CheckedExtrinsic and ExtrinsicFormat also have this new optional generic. Their type definitions also have changed a bit: the General variant was 2 fields, the version and the extension, it is now only one field, the extension, and the version can be retrieve by calling extension.version()
Some trait such as ExtrinsicMetadata and EthExtraImpl changed their associated type named Extension to ExtensionV0 and have new associated type ExtensionOtherVersions. This is because multiple version are now supported.
You can always use InvalidVersion for ExtensionOtherVersions and keep the old Extension for ExtensionV0 to keep the same behavior as before this PR.
The type inference for those types may fail because of this PR, to update the code by writing the concrete types.
New feature
To use this new feature, you can use the new types PipelineAtVers and MultiVersion to define a transaction extension with multiple version:
pub type TransactionExtensionV0 = ();
pub type TransactionExtensionV4 = ();
pub type TransactionExtensionV7 = ();
pub type OtherVersions = MultiVersion<
PipelineAtVers<4, TransactionExtensionV4>;
PipelineAtVers<7, TransactionExtensionV7>;
>;
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<
AccountId,
RuntimeCall,
UintAuthorityId,
TransactionExtensionV0, // The version 0, same as before
OtherVersions, // The other versions.
>;[#3520]: Add virtualization host functions
This PR adds experimental support for the virtualization host functions. Those allow the runtime to spawn and run PolkaVM instances. It is experimental because the behaviour is subject to change until PolkaVM and the host functions have a spec. However, we need to merge the code to go on with development. Docs and tests are all there and hence I argue it is good enough to be merged. I added a note that users should not use those functions in production.
This PR adds or changes the following components:
sc-executor-wasmtime: Just exposing our virtualization manager to host functions. Needs to be added here to be available for the whole lifetime of a runtime call.sp-virtualization: New crate that abstracts away the host functions. Meaning that a user (like pallet-contracts) will interface only with this crate and not with the host functions directly. This is necessary so that the natively running test code still works. The host functions also depend on this crate. Those also contain all the tests. Everything PolkaVM is neatly organized into one crate. It also contains the definition of the new host functions.sp-wasm-interface: We added an interface mirroring the host functions here. This is necessary in order for the host functions to be able to call into the executor.
[#11563]: [pallet-broker] introduce Market trait for a generic coretime market
Introduce a Market trait to decouple the sale mechanism from the rest of the broker logic.
This allows alternative market implementations (e.g. RFC-17) to be swapped in without
modifying the broker pallet itself.
The purpose of this trait is to be implemented by a pallet containing the
logic for RFC-17 market and be exposed as a configuration item in pallet-broker.
[#11772]: Prospective parachains support for older relay parents
Decouples a candidate's relay parent from its scheduling parent in the prospective-parachains
subsystem, enabling V3 descriptors whose relay parent may be much older than the scheduling
parent (potentially spanning multiple sessions, bounded by
HostConfiguration.max_relay_parent_session_age).
Additionally:
- fixes a bug when handling v3 candidates in statement distribution, which was wrongly comparing
the session index of the candidate with the scheduling session. backing_constraintsruntime API computes a more up to datemin_relay_parent_number, which is the maximum
between the oldest allowed RP based on the HostConfiguration and the relay parent of the most recently backed candidate
of the para.- assumes the ParachainHost runtime API is at least v13 (
BackingConstraints). The
ParaBackingStatefallback in prospective-parachains has been removed; against a runtime
with a lower version the subsystem will fail to process active leaves.
[#11809]: [DAP] Catch-up drip on V1->V2 migration
The DAP V2 migration seeded LastIssuanceTimestamp to a point in the past
(typically the active era start) so the next regular drip would credit
elapsed time back to that point. That elapsed is then clamped by
MaxElapsedPerDrip, so only up to one cap's worth of inflation is actually
credited on the first drip, and the rest is silently dropped.
This migration now performs a one-shot catch-up drip for the full
[last_inflation, now] window and seeds LastIssuanceTimestamp to now, so
regular drips start a fresh cadence from this point.
[#11922]: Update merkle mountain lib
Updates the merkle mountain crate to its latest version.
[#11416]: revive: Automatic address mapping via OnNewAccount/OnKilledAccount
Summary
- Add
AutoMapper<T>struct that implementsOnNewAccount/OnKilledAccountto automatically map accounts when created and unmap when killed - Add
AutoMapconfig constant to enable/disable the feature per-runtime - Guard
map_account/unmap_accountdispatchables withAutoMappingEnablederror when auto-mapping is active - Wire up
AutoMapperin Asset Hub Westend and dev-node runtimes withAutoMap = true - Add v3 multi-block migration to auto-map all existing accounts and release deposits for already-mapped accounts
[#11612]: fix(multi-asset-bounties): enforce authorization in unassign_curator when parent bounty is not Active
Fix an authorization bypass in unassign_curator where any signed account could forcibly
unassign an active child bounty's curator when the parent bounty was not in Active state.
The child curator's native balance hold (deposit) was also permanently leaked in this path.
The BountyStatus::Active catch-all arm now uses parent_curator.ok_or(BadOrigin)? to
explicitly reject callers when no parent curator is available, matching the defensive pattern
already used in the Funded arm. CuratorDeposit::take() is also moved after authorization
to prevent storage mutation on unauthorized calls.
A regression test is added covering the full attack scenario.
[#11817]: asset-conversion precompile: expose getReserves
Summary
- Add
getReserves(bytes asset1, bytes asset2)view function to the asset-conversion precompile, returning the reserve balances of both tokens in the pool - This exposes
pallet_asset_conversion::Pallet::get_reserves()to EVM/PVM contracts and frontends via the precompile interface
Motivation
The precompile already exposes quoteExactTokensForTokens and quoteTokensForExactTokens, which allow contracts to estimate swap outputs. However, there is no way to query the raw pool reserves directly. This forces frontends and contracts to probe with arbitrary amounts to infer pool state. Exposing getReserves gives direct access to pool balances, enabling:
- DEX UIs to display pool composition and depth
- Contracts to make routing decisions based on actual liquidity
- Parity with Uniswap V2's
getReservesinterface that Solidity developers expect
Test plan
-
get_reserves_works— verifies correct reserve values for an existing pool -
get_reserves_fails_for_nonexistent_pool— verifies revert for missing pool - All 23 existing tests continue to pass
[#11858]: pallet-revive: reserve on_finalize per-tx weight for eth extrinsics
Summary
Eth extrinsics using with_ethereum_context add per transaction work to on_finalize (closing out the Ethereum block). That cost should be reserved at dispatch via on_finalize_block_per_tx.
This PR adds the reservation to eth_substrate_call and eth_instantiate_with_code, matching eth_call.
[#11847]: [revive] pgas as storage deposit
Storage deposits backed by PGAS
PGAS is a protocol-level gas token or gas allowance mechanism for users verified through Polkadot's Proof of Personhood ecosystem.
This PR adds a second payment backend for pallet-revive storage deposits: instead of always charging the user in native currency (DOT), a runtime can opt in to having deposits denominated in PGAS.
New trait: Deposit
substrate/frame/revive/src/deposit_payment.rs
A new sealed Deposit<T: Config> trait abstracts over how storage deposits are charged, held, refunded. It has two implementations in-crate:
(): the default, charges and refunds the native currency. Identical to the existing pre-PR behavior.PGasDeposit<Mutator, Holder, Freezer, Id, RefundPercent>: the PGAS-backed backend.
The trait is wired into Config::Deposit and called from charge_deposit / refund_deposit in place of the direct T::Currency calls that used to live there.
Account lifecycle: init_account / deinit_account
The trait includes init_account(to) and deinit_account(contract) methods. Rather than transferring the ED from the origin at contract creation (and back to origin on destruction), the EDs are minted on init and burned on deinit:
New storage: NativeDepositOf
substrate/frame/revive/src/lib.rs
pub(crate) type NativeDepositOf<T: Config> = StorageDoubleMap<
_, Identity, T::AccountId,
Blake2_128Concat, T::AccountId,
BalanceOf<T>, ValueQuery,
>;Keyed (holder_account, user) -> native_amount. It records how much native currency a user has contributed to a given account's hold. The holder is either a contract (for storage deposits on contract accounts) or the pallet account (for code-upload deposits).
It exists because in the mixed PGAS/DOT world, a user's refund cap needs to be tracked explicitly. The map caps how much of a refund can come back as DOT ; anything beyond that is settled in PGAS.
PGasDeposit<Mutator, Holder, Freezer, Id, RefundPercent>
substrate/frame/revive/src/deposit_payment.rs
Parameterized by five type parameters that the runtime wires up:
Mutator: fungibles::Mutate— the fungibles impl backing PGAS (e.g.pallet-assets).Holder: fungibles::MutateHold— the holds backend (e.g.pallet-assets-holder).Freezer: fungibles::freeze::Mutate— the freezes backend (e.g.pallet-assets-freezer), used to pin each contract's PGAS ED.Id: Get<AssetId>— the PGAS asset id on that fungibles instance.RefundPercent: Get<Perbill>— the fraction of PGAS returned on refund/collect; the rest is burned.
Charge semantics:
- If the user has enough reducible PGAS, the full amount is paid in PGAS via
fungibles::MutateHold::transfer_and_hold, which emits theTransferOnHoldevent. No DOT is touched. - Otherwise the charge falls through to DOT, and the contribution is recorded in
NativeDepositOfso it can be refunded as DOT later.
Refund / collect semantics:
- DOT is returned first, capped by
NativeDepositOf[holder][user](and byPrecision::BestEfforton the actual DOT hold). - Any shortfall is taken from the PGAS hold.
RefundPercentof that PGAS is transferred to the user's free balance; the remainder is burned. - Sub-ED refunds: if the
RefundPercentportion would land below PGAS's ED on the user's account (e.g. the user has no PGAS account and the refund is too small to create one), that portion is folded into the burn rather than aborting the whole refund.
The RefundPercent burn is what prevents free-PGAS harvesting: a user can't deposit storage, release it, and walk away with an allowance they can spend on execution.
Migration (v4)
substrate/frame/revive/src/migrations/v4.rs
A three-phase multi-block migration brings live chains over:
- Phase 1: record each existing code-upload deposit under
NativeDepositOf[pallet_account][owner]so it can still be refunded in DOT. - Phase 2: flip each contract's storage deposit from DOT to PGAS via
Deposit::migrate_native_to_pgas— mint + freeze the PGAS ED underFreezeReason::PGasMinBalance, burn the nativeStorageDepositReservehold, re-hold the same amount in PGAS. Needed because pre-PR DOT deposits weren't tracked per-contributor. - Phase 3: rewrite
DeletionQueuefromTrieIdtoDeletionQueueItem { trie_id, account_id }so the on-idle sweep can also clear the contract'sNativeDepositOfrows. Runs on every runtime.
[#10195]: Added
Description
This PR introduces a new #[stored] attribute macro that simplifies the definition of storage types in FRAME pallets.
By automatically generating consistent field-based trait bounds for Encode, Decode, MaxEncodedLen, Clone, Eq, PartialEq, Debug, and TypeInfo, it reduces boilerplate and ensures robust trait implementations for generic storage structures.
[#11527]: Add issuance drip and budget distribution to pallet-dap
Adds issuance drip and budget distribution to pallet-dap. DAP mints new tokens on a
configurable cadence via IssuanceCurve and distributes them to registered
BudgetRecipients according to a governance-updatable allocation map.
Includes set_budget_allocation extrinsic, OnUnbalanced slash handling with buffer
deactivation, safety ceiling on elapsed time, and a V1→V2 migration struct (not yet applied).
All runtimes are configured with a noop IssuanceCurve (() impl that returns 0) so there
is no behavior change. Minting will be enabled when staking is integrated with DAP.
[#11513]: Add issuance/budget traits
Moves EraPayout trait from pallet-staking and pallet-staking-async to sp-staking,
eliminating duplicate definitions. Adds a new budget module with stake independent
traits for issuance computation (IssuanceCurve) and budget distribution
(BudgetRecipient, BudgetRecipientList). Also adds StakerRewardCalculator trait for
customizing validator incentive weights and staker reward splits.
No behavior changes. Existing EraPayout re-exports from both staking pallets are preserved.
[#11860]: pallet-revive: align eth_substrate_call origin check with other eth dispatchables
Summary
Adds ensure_non_contract_if_signed to eth_substrate_call, matching eth_call and eth_instantiate_with_code.
[#11215]: try state hook for pallet authorship
This PR introduces the try_state hook to pallet-authorship to verify a key storage invariant.
closes part of #239
[#11567]: Align Tally::approval() with VoteTally trait semantics
Tally::approval() in pallet-conviction-voting now returns Perbill::zero()
when no aye or nay votes have been cast, consistent with the pallet-referenda
test mock. The VoteTally::approval() trait doc is also updated
to document this expected behavior.
[#12135]: revive automap: batch map accounts for free
Changes:
- Add TX to pallet revive to allow anyone to map accounts
The new TX batch_map_accounts:
- Maps a batch of accounts without taking a deposit
- Releases existing mapping deposits of accounts
- Is free to send when at least 90% of accounts were newly mapped or had an existing deposit released
[#11068]: FRAME: Add Peg Stability Module (PSM) pallet
Introduces pallet-psm, a Peg Stability Module that enables 1:1 swaps between a native
stablecoin (pUSD) and pre-approved external stablecoins (e.g. USDC, USDT). The PSM
strengthens the stablecoin peg by creating arbitrage opportunities bounded by configurable
minting and redemption fees.
Key features:
- Minting: deposit an external stablecoin to receive pUSD
- Redemption: burn pUSD to receive an external stablecoin
- Per-asset circuit breakers to disable minting or all swaps
- Configurable ceiling weights and maximum PSM debt ratio
[#11649]: Consolidate try-state warnings into summary counts
Aggregates repetitive try-state warnings (min bond violations, pool ED imbalance, depositor
insufficient stake) into single summary lines with counts and up to 10 example accounts.
Reduces log noise from hundreds of expected per-item warnings on production runtimes.
Closes #11646.
[#11902]: pallet-psm: rename pUSD/stable to internal
Renames pallet-psm's "pUSD" / "stable" vocabulary to a generic "internal"
role, paired with the existing "external" terminology, so the pallet reads
as a generic peg stability module rather than one tied to a specific
stablecoin.
Public API changes:
Config::StableAsset->Config::InternalAssetStableDecimalsstorage ->InternalDecimalsAssetDecimalsstorage ->ExternalDecimalsEvent::Minted.pusd_received->receivedEvent::Redeemed.pusd_paid->paidredeem(.., pusd_amount)parameter ->amount
Runtime impls of pallet_psm::Config need to update the associated type
name; the storage rename is reflected directly in the v1→v2
PopulateDecimals migration (no separate migration needed).
All internal helpers, locals, comments, mock fixtures and prose are also
updated. The module docs and README gain a Terminology section explaining
"Internal" vs "External".
[#10952]: Fix claim_rewards_to benchmark to enable Snowbridge reward claims
The prepare_rewards_account benchmark helper was returning None, causing claim_rewards_to to be assigned Weight::MAX and effectively disabling the extrinsic. This fix returns a valid beneficiary account, enabling Snowbridge relayers to claim rewards to AssetHub as intended.
[#12075]: frame-benchmarking: allow 2-point slope fits in min_squares_iqr
Description
a33b7c2 short-circuits min_squares_iqr to median_value whenever r.len() <= 2 to avoid OLS panics on --steps=1 --repeats=1. That fallback also catches the case where two samples sit at different x-values, where the slope is exactly determined. However, benchmarks whose valid sample set is narrowed to two values (e.g. by BenchmarkError::Skip filtering a Linear<lo, hi> parameter) lose their linear component.
Route the r.len() <= 2 fallback through median_slopes instead, which fits a slope exactly from two samples at different x-values. The degenerate "all samples share one x" case now surfaces an explicit error from median_slopes's own check.
Integration
No API or storage changes. Downstream runtimes do not need to update code.
Regenerating weights may produce different values for benchmarks whose valid sample set is exactly 2 points at distinct x-values. The resulting weight expression will now include a linear component fitted via median_slopes instead of falling back to the constant median.
Benchmarks whose 2 remaining samples share an x-value will now fail with an explicit median_slopes error rather than returning the median. If you see this, broaden the parameter range or increase --steps/--repeats so more than one distinct x-value is sampled.
Review Notes
min_squares_iqr had a single guard combining two unrelated short-circuits:
if r[0].components.is_empty() || r.len() <= 2 {
return Self::median_value(r, selector);
}The components.is_empty() branch is correct: a benchmark with no parameters has no slope to fit, so a constant median should be returned. The r.len() <= 2 branch was a workaround for linregress's OLS fit, which is under-determined with two samples and one intercept + one slope variable. Both cases fell through to the same median_value call, which drops slopes entirely.
The no-components fallback is kept on median_value, while r.len() <= 2 is routed to median_slopes instead. median_slopes is well-suited: it forms the pairwise slope list (y_i - y_j) / (x_i - x_j) over samples with distinct x-values, takes the median, then derives the intercept from per-sample offsets. With exactly two distinct-x samples the slope list has length 1, so the median is the exact slope. No regression is needed.
[#11736]: Asset Hub Westend: Add MonetaryGuard governance track for PSM emergency actions
Adds a MonetaryGuard custom origin and governance track (ID 16) to Asset Hub
Westend for PSM emergency actions.
Changes:
- Adds
MonetaryGuardorigin topallet_custom_origins - Adds
monetary_guardtrack with fast confirm/enactment periods - Updates
EnsurePsmManagerorigin mapping:- Root -> Full (all PSM operations)
- MonetaryGuard -> Emergency (circuit breaker only)
Track parameters are relaxed for testnet purposes (low deposit, short periods,
0% support floor). Production values will differ.
[#11590]: Add asset-conversion precompile
Adds a precompile that exposes pallet-asset-conversion (Asset Hub DEX) to Solidity contracts running on pallet-revive. This enables smart contracts to swap tokens through the on-chain DEX and query swap prices.
The primary use case is W3S products (e.g. ticketing app) where contracts accept payment in one asset (e.g. USDC) and convert it to DOT/PUSD via the Asset Hub DEX, rather than holding arbitrary tokens directly.
[#11647]: Fix try-state warning for LastValidatorEra in staking-async
Fixes a false try-state warning where LastValidatorEra was flagged as incorrect for active
validators. After the election for the next era completes but before that era becomes active,
LastValidatorEra is correctly set to active_era + 1. The previous check only accepted
active_era, causing spurious warnings.
Adds a test verifying LastValidatorEra transitions from active_era to active_era + 1
once the next era's election results are stored.
[#11512]: Vested Payout trait and implementation
Introduces a new VestedPayout trait in frame-support for transferring funds with a linear
vesting schedule. Unlike the existing VestedTransfer trait, callers only specify the total
amount and duration, and the implementor handles per-block computation internally.
pallet-vesting provides the implementation. The per-block unlock rate is rounded up so that
vesting always completes within the specified duration, never longer.
[#11893]: Raise relay-chain BlockLength to 10 MiB on test runtimes; decouple AttestedCandidate response cap
Raises the relay-chain block-length cap from 5 MiB to 10 MiB on westend-runtime,
rococo-runtime, and polkadot-test-runtime.
[#10150]: Deprecate ValidateUnsigned trait and #[pallet::validate_unsigned] attribute.
Deprecate the ValidateUnsigned trait and #[pallet::validate_unsigned] attribute as part of phase 2 of Extrinsic Horizon.
[#11761]: asset-conversion-pallet: quote respects minimum balance
Quote functions (quote_price_exact_tokens_for_tokens, quote_price_tokens_for_exact_tokens)
now return None when the computed output exceeds what the pool can actually withdraw while
staying alive.
Swap execution withdraws from pools with Preserve preservation, meaning the pool must retain
at least min_balance. The quote functions did not account for this, so they could return a
price for a swap that would fail at execution.
Both quote functions now check the output against reducible_balance(Preserve, Polite) — the
same preservation level the swap uses.
[#11877]: pallet-dap: expose budget recipients and staging account as view functions
Adds two view functions to pallet-dap:
budget_recipients()returnsVec<(BudgetKey, AccountId, Perbill)>: every registered recipient joined with its
current allocation share.staging()returns the sub-account that collects burns/slashes beforeon_idledrains them into the buffer.
Needed by off-chain clients (e.g. pjs app) that would otherwise re-derive sub-accounts and join recipient lists
client-side.
[#11651]: Validator self-stake incentive curve (non-vested)
Adds a separate validator incentive reward track funded from a second DAP budget pot.
Each validator's share is determined by a sqrt-based piecewise weight function of their
self-stake, with governance-configurable parameters (optimum, cap, slope factor).
Payout is a direct liquid transfer from the era incentive pot.
New extrinsic: set_validator_self_stake_incentive_config (AdminOrigin).
[#11921]: pallet-psm: switch Westend Asset Hub to Location AssetId backed by LocalAndForeignAssets
On Asset Hub Westend, pallet-psm is switched from u32 (trust-backed
asset id) to xcm::v5::Location as its AssetId, and from Assets to
LocalAndForeignAssets as its Fungibles. PSM can now mint and redeem
against both trust-backed and foreign-registered external stablecoins,
addressed uniformly by Location.
Storage migration: every AssetId-keyed PSM storage item is encoded under
the old u32 key. The runtime wipes the existing PSM storage with
frame_support::migrations::RemovePallet and re-seeds the pallet from
PsmInitialConfig via InitializePsm. USDT (trust-backed asset 1984)
is reseeded under its Location representation as the first external
asset.
pallet-psm's BenchmarkHelper trait gains a get_asset_id(index: u32)
method so benchmark scenarios can derive a runtime-specific AssetId
(e.g. a Location) from a u32 index. Existing impls need to add this
method.
[#11081]: Implement eth_subscribe
Description
Implemented eth_subscribe in the eth-rpc. The subscription kinds implemented is newHeads and logs.
[#11052]: update multi asset bounties pallet account derivation logic
Bounty and child-bounty account derivation now uses raw-byte [u8; 3] prefixes b"mbt"
(multi-asset bounty) and b"mcb" (multi-asset child bounty) instead of the &str literals
"bt" and "cb" used by the legacy pallets. This avoids collisions with the old bounties
pallet (same account for both pallets).
Encoding note: The prefixes are SCALE-encoded as three raw bytes with no length prefix
(e.g. b"mbt" → [0x6d, 0x62, 0x74]). This differs from the legacy &str encoding which
includes a compact length prefix (e.g. "bt" → [0x08, 0x62, 0x74]).
BountySourceFromPalletId and ChildBountySourceFromPalletId now take a Prefix type
parameter (Get<[u8; 3]>). This is a breaking API change — downstream runtimes must
update their configuration to supply the prefix type.
Module and type docs were updated to document the derivation.
[#11630]: [pallet-revive] Add vestedTransfer to vesting precompile
Summary
Add vestedTransfer(address, uint256, uint256, uint256) to the vesting precompile, allowing Solidity contracts to create vesting schedules for target accounts via pallet_vesting::vested_transfer. Updates the IVesting.sol interface with the new function signature and NatSpec docs. Includes tests covering success, below-minimum revert, insufficient balance revert, and read-only/delegate-call guards.
Test plan
-
vested_transfer_succeeds— verifies schedule creation on target -
vested_transfer_reverts_below_min— reverts when locked <MinVestedTransfer -
vested_transfer_reverts_insufficient_balance— reverts when caller lacks funds - Guard test cases — rejects in read-only and delegate-call contexts
[#11798]: pallet-asset-conversion: distinguish PoolEmpty from PoolNotFound
Summary
- Adds a new
PoolEmptyerror variant topallet-asset-conversion PoolNotFoundnow only means the pool does not exist in storagePoolEmptyis returned when the pool exists but has zero reserves
Motivation
When a pool exists but has no liquidity, get_reserves() returned PoolNotFound. This is misleading — the pool is in storage, it just has empty reserves. Users and frontends cannot distinguish between "you need to create a pool" and "you need to add liquidity."
Changes
substrate/frame/asset-conversion/src/lib.rs: AddedPoolEmptyerror variant, changed the zero-reserves check inget_reserves()to use itsubstrate/frame/asset-conversion/src/tests.rs: Updatedcan_not_swap_in_pool_with_no_liquidity_added_yetto expectPoolEmpty
[#11801]: eth-rpc: skip receipt extraction for finalized blocks already processed as best
Motivation
Both the best and finalized block subscriptions extract receipts independently, so every block is processed twice. This skips redundant extraction on the finalized path when the block was already handled by the best block subscription.
Summary
- Skip redundant receipt extraction on finalized blocks already processed by the best block subscription
- Read logs from DB for skipped blocks only when log subscribers exist
- Refactor: extract process_block helper, parse_log_row shared function, advance_sync_head
- Add unit tests for get_processed_eth_block_hash and logs_by_block_number
[#11750]: Post 2603 cleanup
- make clone of AssetsInHolding available for std
- Make pallet assets try-state check more lenient
[#11919]: Snowbridge: Set default asset claimer to local network
The inbound-queue v2 message converter falls back to the Snowbridge sovereign
account on AssetHub as the asset claimer when no explicit claimer is supplied.
Previously this fallback used AccountId32 { network: None, .. }, which did
not match the location AssetHub's signed-origin converter produces (it sets
network: Some(LocalNetwork)). The trap-key hash stored on AssetsTrapped
therefore could not be matched by a signed polkadotXcm.claim_assets call,
making default-claimer trapped funds effectively unrecoverable without a
runtime upgrade.
This PR sets network: Some(LocalNetwork::get()) on the fallback claimer so
its Location agrees with what SignedToAccountId32<_, _, LocalNetwork>
yields on AssetHub, and adds a test covering the no-claimer-supplied path.
[#11905]: pallet-psm: relax AssetId from Copy to Clone
Relaxes the T::AssetId bound on pallet-psm's Config from Copy to
Clone. Lets runtimes wire pallet-psm with a non-Copy AssetId.
The most relevant example is using XCM Location as AssetId.
No semantic changes; only the Config bound and ownership at use sites.
For AssetIds that are Copy (e.g. u32), the added .clone() calls
are free since Copy types implement Clone trivially.
[#11700]: Redirect XCM delivery fees to DAP / DAP satellite on Westend chains
Route XCM delivery fees to DAP satellite (or DAP on Asset Hub) instead of treasury on all Westend system chains.
[#11795]: Harden asset-conversion quote functions against zero amounts
Hardens quote_price_exact_tokens_for_tokens and quote_price_tokens_for_exact_tokens in
pallet-asset-conversion to return None for zero input amounts and when integer rounding
produces a zero output. Previously, zero inputs could propagate through the AMM math and
zero outputs from small-input rounding were returned as Some(0).
[#12005]: Make TargetBlockRate runtime API match BLOCK_PROCESSING_VELOCITY on test parachains
This PR aligns the TargetBlockRate implementation on every affected runtime with
BLOCK_PROCESSING_VELOCITY.
[#11796]: Removed OpenGov pallets from Westend relay chain post-AHM
Removed pallet_conviction_voting, pallet_referenda, pallet_custom_origins
and pallet_whitelist from the Westend relay chain runtime. Post-AHM, governance
lives on AssetHub.
[#11715]: Reject delegatecall into precompiles via PrecompileDelegateDenied
Summary
- Add delegatecall guard to the ERC20 assets precompile and XCM precompile, matching the existing pattern in the vesting and asset-conversion precompiles
- Converge asset-conversion precompile from
Error::Revert(string)toError::Error(PrecompileDelegateDenied)for consistency across all precompiles - Add delegatecall rejection test for the XCM precompile
Motivation
Delegatecall to precompiles allows a malicious contract to execute precompile logic in a misleading caller context. The precompiles derive caller identity from env.caller(), which during delegatecall returns the original caller — letting the intermediary contract act on the caller's assets or send XCM on their behalf. There is no legitimate use case for delegatecalling into these precompiles.
Changes
substrate/frame/assets/precompiles/src/lib.rs— addPrecompileDelegateDeniedguardsubstrate/frame/asset-conversion/precompiles/src/lib.rs— replaceError::Revert(ERR_DELEGATE_CALL)withPrecompileDelegateDenied, remove unused constpolkadot/xcm/pallet-xcm/precompiles/src/lib.rs— addPrecompileDelegateDeniedguardpolkadot/xcm/pallet-xcm/precompiles/src/tests.rs— adddelegatecall_is_rejectedtestpolkadot/xcm/pallet-xcm/precompiles/Cargo.toml— addpallet-revive-fixturesdev-dependency
Test plan
-
cargo test -p pallet-xcm-precompiles— 13 tests pass, including newdelegatecall_is_rejected -
cargo test -p pallet-asset-conversion-precompiles— 18 tests pass -
cargo test -p pallet-assets-precompiles— 66 tests pass
[#12003]: emulated integration tests cleanup
This PR mostly consists of minor refactoring like introducing/centralize some helpers and replacing some inlined functions with these helpers.
[#11694]: Make pallet_xcm_bridge_hub_router exporter configurable for paid/unpaid
Add type Exporter: SendXcm to the pallet's Config trait so runtimes can choose between SovereignPaidRemoteExporter (paid) and UnpaidRemoteExporter (unpaid) bridging. Provide convenience type aliases PaidRemoteExporter and UnpaidRemoteExporterAdapter.
Asset Hub runtimes are configured to now use UnpaidRemoteExporter to reduce deployment complexity
(no more need to manage/top-up AH sov account on BH).
[#11816]: pallet-beefy: Allow unsigned execution of an unsigned method
Allow unsigned execution of report_future_block_voting_unsigned in beefy.
[#11616]: Move era reward minting from staking to DAP
Introduces dual-mode era rewards in pallet-staking-async, controlled by
Config::DisableMinting:
true(non-minting): staking expects an external source to fund a general reward
pot. At era boundary, the balance is snapshotted into an era-specific pot. Payouts
transfer from the pot. Unclaimed rewards returned viaUnclaimedRewardHandler.false(legacy minting):EraPayoutcomputes inflation, tokens minted on payout,
remainder sent toRewardRemainder. Kept for Kusama compatibility.
Switching from legacy to non-minting is irreversible.
New config: DisableMinting, UnclaimedRewardHandler, GeneralPots, EraPots,
StakerRewardCalculator. New storage: MaxCommission, DisableMintingGuard.
New extrinsic: set_max_commission.
Runtimes using non-minting mode provide an IssuanceCurve impl to DAP and
register StakerRewardRecipient as a budget recipient. DAP storage version
bumped to V2 with migration.
[#11778]: Set Aura slot duration to 24s for all Westend parachains
Increases the Aura slot duration from 6s to 24s for all Westend system
parachains (asset-hub, bridge-hub, coretime, people, collectives) via the
shared testnet constants, and for glutton-westend and YAP individually.
Additionally, glutton-westend is updated to use named elastic scaling
constants (RELAY_PARENT_OFFSET, BLOCK_PROCESSING_VELOCITY,
UNINCLUDED_SEGMENT_CAPACITY) instead of hardcoded values
[#11999]: pallet-staking-async: Use offence era for proportional slash distribution
Fixes the proportional slash split between active stake and unlocking chunks.
The ledger now uses the offence era (not the slash application era) to decide
which unlocking chunks are still in range, restoring the intended proportional
distribution. No funds previously escaped slashing — the active balance was just
taking a disproportionate share.
[#11594]: Bags-list on_idle: per-item weight consumption via WeightMeter
Replaces the bulk on_idle benchmark with a per-item on_idle_rebag benchmark that
measures the worst-case cost of a single rebag. on_idle now consumes weight per
iteration via WeightMeter instead of reserving a single bulk weight upfront.
This decouples the benchmark from MaxAutoRebagPerBlock — changing the config no
longer requires re-running benchmarks.
[#11908]: UncheckedExtrinsic: Improve memory usage
Improves the memory usage of the unchecked extrinsic by pre-allocating some buffers and preventing e.g. printing huge calls.
[#11460]: [pallet-assets-precompiles] add foreign assets instance to kitchensink
Summary
- Set
CallbackHandle = (pallet_assets_precompiles::ForeignAssetId<Runtime, Instance1>,)
inpallet_assets::Config<Instance1>for the kitchensink runtime. - Asset creation (
create,force_create) now automatically populates a sequential
foreign asset index mapping. Asset destruction cleans it up.
Test plan
- Run end-to-end tests (requires substrate-node, eth-rpc, node, cast)
- Revert CallbackHandle to
()and confirm end-to-end tests fail
[#12035]: Expose BLS host functions to the omni-bencher
Expose BLS host functions to frame-omni-bencher.
[#11856]: Snowbridge: halt the Ethereum verifier when the bridge is in emergency stop
When pallet-ethereum-client is in Halted operating mode, its Verifier::verify
implementation now short-circuits with the new VerificationError::Halted instead of
attempting to verify Ethereum-side proofs.
Previously, halting the light client only blocked new beacon header updates via
EthereumBeaconClient::submit. Proof verification still ran, which meant
inbound_queue_v2::submit and outbound_queue_v2::submit_delivery_receipt could
continue to process receipts and pay out relayer rewards from PendingOrders while
governance had halted the bridge (e.g. after a suspected beacon light client compromise).
Halting the verifier closes that gap in one place — covering both inbound dispatch and
outbound delivery-receipt reward payments.
[#11435]: Raise offence queue eras bound limit
Fixes a bug where OffenceQueueEras bound (BondingDuration) was incorrect when SlashDeferDuration=0. The oldest reportable offence era formula allowed more eras than the bound could hold.
Changelog for Node Operator
ℹ️ These changes are relevant to: Those who don't write any code and only run code.
[#10468]: Publish indexed transactions with BLAKE2b hashes to IPFS DHT
Add --ipfs-bootnodes flag for specifying IPFS bootnodes. If passed along with --ipfs-server, the node will register as a content provider in IPFS DHT of indexed transactions with BLAKE2b hashes of the last two weeks (or pruning depth if smaller).
Changelog for Runtime User
ℹ️ These changes are relevant to: Anyone using the runtime. This can be a token holder or a dev writing a front end for a chain.
[#11823]: Refactor asset-conversion tx payment fee correction
The AssetTxFeePaid event now reports the correct fee amount when paying transaction fees
in the native asset through the asset-conversion extension.
[#11822]: Remove deprecated CurrencyAdapter from pallet-transaction-payment
The deprecated CurrencyAdapter type has been removed from pallet-transaction-payment.
Use FungibleAdapter instead.
[#11529]: Westend Asset Hub: Integrate PSM pallet and add remote tests
Adds the PSM pallet to Asset Hub Westend, enabling 1:1 minting and redemption
of pUSD against USDT.
[#11668]: Remove Meta Tx from Westend RC
Removal of the Meta Transaction from the Westend Relay Chain.
With this removal, pallet Verify Signature is removed as well.
[#11612]: fix(multi-asset-bounties): enforce authorization in unassign_curator when parent bounty is not Active
Previously, any account could unassign a child bounty curator without authorization when the
parent bounty's curator had been unassigned. This is now correctly rejected. If you were
affected by a permanently locked curator deposit from this bug, a migration or manual
intervention may be needed to release the held balance.
[#11847]: [revive] pgas as storage deposit
Storage deposits backed by PGAS
PGAS is a protocol-level gas token or gas allowance mechanism for users verified through Polkadot's Proof of Personhood ecosystem.
This PR adds a second payment backend for pallet-revive storage deposits: instead of always charging the user in native currency (DOT), a runtime can opt in to having deposits denominated in PGAS.
New trait: Deposit
substrate/frame/revive/src/deposit_payment.rs
A new sealed Deposit<T: Config> trait abstracts over how storage deposits are charged, held, refunded. It has two implementations in-crate:
(): the default, charges and refunds the native currency. Identical to the existing pre-PR behavior.PGasDeposit<Mutator, Holder, Freezer, Id, RefundPercent>: the PGAS-backed backend.
The trait is wired into Config::Deposit and called from charge_deposit / refund_deposit in place of the direct T::Currency calls that used to live there.
Account lifecycle: init_account / deinit_account
The trait includes init_account(to) and deinit_account(contract) methods. Rather than transferring the ED from the origin at contract creation (and back to origin on destruction), the EDs are minted on init and burned on deinit:
New storage: NativeDepositOf
substrate/frame/revive/src/lib.rs
pub(crate) type NativeDepositOf<T: Config> = StorageDoubleMap<
_, Identity, T::AccountId,
Blake2_128Concat, T::AccountId,
BalanceOf<T>, ValueQuery,
>;Keyed (holder_account, user) -> native_amount. It records how much native currency a user has contributed to a given account's hold. The holder is either a contract (for storage deposits on contract accounts) or the pallet account (for code-upload deposits).
It exists because in the mixed PGAS/DOT world, a user's refund cap needs to be tracked explicitly. The map caps how much of a refund can come back as DOT ; anything beyond that is settled in PGAS.
PGasDeposit<Mutator, Holder, Freezer, Id, RefundPercent>
substrate/frame/revive/src/deposit_payment.rs
Parameterized by five type parameters that the runtime wires up:
Mutator: fungibles::Mutate— the fungibles impl backing PGAS (e.g.pallet-assets).Holder: fungibles::MutateHold— the holds backend (e.g.pallet-assets-holder).Freezer: fungibles::freeze::Mutate— the freezes backend (e.g.pallet-assets-freezer), used to pin each contract's PGAS ED.Id: Get<AssetId>— the PGAS asset id on that fungibles instance.RefundPercent: Get<Perbill>— the fraction of PGAS returned on refund/collect; the rest is burned.
Charge semantics:
- If the user has enough reducible PGAS, the full amount is paid in PGAS via
fungibles::MutateHold::transfer_and_hold, which emits theTransferOnHoldevent. No DOT is touched. - Otherwise the charge falls through to DOT, and the contribution is recorded in
NativeDepositOfso it can be refunded as DOT later.
Refund / collect semantics:
- DOT is returned first, capped by
NativeDepositOf[holder][user](and byPrecision::BestEfforton the actual DOT hold). - Any shortfall is taken from the PGAS hold.
RefundPercentof that PGAS is transferred to the user's free balance; the remainder is burned. - Sub-ED refunds: if the
RefundPercentportion would land below PGAS's ED on the user's account (e.g. the user has no PGAS account and the refund is too small to create one), that portion is folded into the burn rather than aborting the whole refund.
The RefundPercent burn is what prevents free-PGAS harvesting: a user can't deposit storage, release it, and walk away with an allowance they can spend on execution.
Migration (v4)
substrate/frame/revive/src/migrations/v4.rs
A three-phase multi-block migration brings live chains over:
- Phase 1: record each existing code-upload deposit under
NativeDepositOf[pallet_account][owner]so it can still be refunded in DOT. - Phase 2: flip each contract's storage deposit from DOT to PGAS via
Deposit::migrate_native_to_pgas— mint + freeze the PGAS ED underFreezeReason::PGasMinBalance, burn the nativeStorageDepositReservehold, re-hold the same amount in PGAS. Needed because pre-PR DOT deposits weren't tracked per-contributor. - Phase 3: rewrite
DeletionQueuefromTrieIdtoDeletionQueueItem { trie_id, account_id }so the on-idle sweep can also clear the contract'sNativeDepositOfrows. Runs on every runtime.
[#11736]: Asset Hub Westend: Add MonetaryGuard governance track for PSM emergency actions
Adds a new MonetaryGuard governance track to Asset Hub Westend for fast PSM
circuit breaker activation in case of a depeg or exploit.
[#10150]: Deprecate ValidateUnsigned trait and #[pallet::validate_unsigned] attribute.
Deprecate the ValidateUnsigned trait and #[pallet::validate_unsigned] attribute as part of phase 2 of Extrinsic Horizon.
[#11761]: asset-conversion-pallet: quote respects minimum balance
Quote functions (quote_price_exact_tokens_for_tokens, quote_price_tokens_for_exact_tokens)
now return None when the computed output exceeds what the pool can actually withdraw while
staying alive.
Swap execution withdraws from pools with Preserve preservation, meaning the pool must retain
at least min_balance. The quote functions did not account for this, so they could return a
price for a swap that would fail at execution.
Both quote functions now check the output against reducible_balance(Preserve, Polite) — the
same preservation level the swap uses.
[#11052]: update multi asset bounties pallet account derivation logic
The account IDs for bounty and child-bounty funding accounts have changed. Off-chain code that
derives or hardcodes these addresses must use the new derivation: raw-byte prefix b"mbt" for
bounties and b"mcb" for child bounties, SCALE-encoded as fixed [u8; 3] arrays (no length
prefix), with the same pallet ID and index encoding as before.
[#11469]: Westend: Support Meta Transactions
Support Meta Transactions via meta-tx pallet for next Westend runtimes:
- Asset Hub
- Coretime
- People
- Collectives
Rust compiler versions
This release was built and tested against the following versions of rustc.
Other versions may work.
- Rust Stable:
1.93.0
Docker images
The docker images for the polkadot node binary and the polkadot-parachain binary can be found at Docker hub (will be available a few minutes after the release has been published):
You may also pull it with:
docker pull parity/polkadot:unstable2604
or
docker pull parity/polkadot-parachain:unstable2604