Messaging: the Future of Cross-Chain Smart Contracts on Polkadot
Polkadot has always carried a powerful promise: a network of specialised chains that can interoperate seamlessly, letting developers compose logic, assets, and services across the ecosystem. However, in practice building smart contracts that live up to this vision has been much harder. Contracts today sit in silos, cut off from the full potential of cross-chain state and execution, because using protocols like XCM or ISMP is complex or rarely accessible from the contract layer.
The Messaging feature, built by R0GUE, is designed to change that. Its vision is to make interoperability a first-class primitive for contracts. Developers get APIs that bring XCM and Polytope’s ISMP into the contract layer, complete with response handling and automatic callbacks. This means a contract doesn’t just fire off a cross-chain request, it can also receive and react to the result as soon as it arrives, as naturally as calling a local function.
Messaging is a building block of the Polkadot Cloud vision: Polkadot evolves into a Web3 cloud where execution, storage, and data availability become services that can be combined like APIs. In this model, Messaging is the connective tissue: the developer-facing way to orchestrate workflows across rollups and tap into the Cloud as if it were one coherent system.

XCM and ISMP
In Polkadot, XCM (Cross-Consensus Messaging) and ISMP (Interoperable State Machine Protocol) both enable cross-chain communication but differ in philosophy and mechanics.
XCM is a language and instruction set for rollups to send programs to each other, with the Relay Chain guaranteeing delivery. It excels at executing actions on remote chains, with only needing an HRMP channel open between the chains.
ISMP is a transport protocol based on state proofs. It excels at querying state from other chains because only the origin chain needs ISMP implemented. When an ISMP GET request is made, an event is omitted and a relayer retrieves the requested value from the destination chain along with a state proof, and submits it back. The origin chain then verifies the proof against the destination’s finalised state before accepting the data.
In summary, XCM provides a secure pathway for cross-chain execution, while ISMP enables verifiable cross-chain queries. Used together, they give developers the full toolkit for both action and data across the Polkadot ecosystem.
How Messaging Works
Today, Messaging exposes ISMP GET (state queries) and XCM execution. The interface lets contracts send a request, track it, and handle the response. Either by polling or via automatic callbacks, with deposits and gas/weight accounted for. Messaging is protocol‑agnostic by design. New features of the existing protocols (e.g. ISMP POST), or new cross chain protocols can easily be added and benefit from the same response‑handling flow.

Querying State Cross Chain
For a contract to query state it has to provide the destination rollup ID, storage keys to query, and optional callback parameters. The system holds the required deposit (D1) that covers the cost of storing your ISMP message data on-chain until the message is processed (or timed out) and cleaned up. In addition, if a callback is requested, funds are held (D2) to cover the gas costs of executing the callback function when the ISMP response arrives (the max. gas is specified by the contract). Then the request is dispatched through ISMP’s offchain infrastructure.
// Create ISMP GET request
let request = Get::new(
POLKADOT_HUB, // Destination chain
height, // Block height
timeout, // Timeout
b"context".to_vec().into(), // Context
vec![storage_key].into() // Storage keys
);
// With Callback
let callback = Callback::new(
contract_address, Encoding::SolidityAbi, 0x9bf78ffb, gas_limit, storage_deposit_limit
);
// Submit a GET request
let message_id = ismp::get(request, fee, Some(callback))?;The response arrives when the relayer submits a transaction to the ISMP pallet with the retrieved data. The relayer pays the fees for response handling and the call to the contract (not the gas consumed by the contract). If requested, the system checks for enough blockspace, executes the callback and registers the gas consumed to ensure proper block weight accounting. Its respective funds are released (D2) and the actual gas consumed is withdrawn from the contract’s account. The callback function receives the storage values, the state is cleaned up and the remaining funds are released (D1). Without callback, the response is stored. By manual polling the message status can be checked, the response data can be retrieved, and ultimately state is cleaned up and the deposit is released (D1).
Executing Cross Chain
In order for a contract to get the result back from executing an XCM program cross chain, it has to create an XCM query to register a response handler. The system will hold the required deposit from the contract’s account (D1) to cover the storage costs of maintaining the query message until it’s processed (or timed out) and cleaned up. If a callback is requested, additional funds will be held (D2) from the contract (like querying state with ISMP). With current XCM (2506) responses are dispatched without fee charging or weight tracking, requiring the Messaging pallet to manually handle fees and weight accounting. Therefore, on creating an XCM query the contract pays for the XCM response handling and calling a contract, in advance. After the XCM query is made the contract constructs the message with the response reporting instructions (example). Then the system dispatches the XCM message to the destination chain.
// Register query for response (with callback)
let (message_id, query_id) = xcm::new_query(HUB, timeout, Some(callback))?;
// Create the response
let response = QueryResponseInfo {
// Route back to this parachain.
destination: Location::new(1, Parachain(api::id())),
query_id,
max_weight: Weight::from_parts(1_000_000, 5_000),
};
// Build XCM with response reporting
let message = Xcm::builder_unsafe()
.withdraw_asset(fees.clone())
.buy_execution(fees, WeightLimit::Unlimited)
.transact(OriginKind::SovereignAccount, weight, call)
.report_transact_status(response_info) // ← Reports result back!
.build();
xcm::send(dest, message)?;When the XCM Message executes on the destination chain, it reports the result back through the registered query. When the response arrives, the system checks for sufficient blockspace to then process the response and manually register the weight consumed by the response handling and contract call (for ISMP the relayer pays for this, here it is paid for in advance). As follows, the system either executes the callback or stores the response for manual retrieval. This is handled the same as for ISMP: when a callback is executed both deposits (D1 and D2) are released, with the actual callback gas withdrawn from the contract’s account; if no callback is used, the response is stored until manually retrieved and the deposit (D1) is then released.

Timeouts, Polling and Removing
Timeouts ensure system reliability by preventing resource exhaustion from abandoned cross-chain requests while providing users with a clear mechanism to reclaim their funds. ISMP timeouts are handled by the ISMP protocol itself, when the specified timeout duration expires ISMP triggers Messaging’s timeout processing. XCM timeouts are block-based and are processed during block initialisation. Timeout processing works similar, the message state is changed to Timeout and deposits are maintained for potential refund. The contract can then remove messages to clean up timed-out (or finished) messages and recover their deposits.
// Check message status
match api::poll_status(message_id) {
MessageStatus::Pending => {
// Still waiting for response
},
MessageStatus::Complete => {
// Response received, get data
let response = api::get_response(message_id)?;
},
MessageStatus::Timeout => {
// Request timed out
},
MessageStatus::NotFound => {
// Message doesn't exist
}
}
// Remove from runtime storage (releases deposits)
api::remove(message_id)?;The Road Ahead
Messaging lays the low-level foundations for cross-chain smart contracts, giving developers the building blocks to compose logic and state across the Polkadot ecosystem. By providing simple, versioned, developer-friendly APIs that abstract over XCM and ISMP, it aims to make building cross-chain contracts feel as natural as writing a local one.

But there are still challenges that must be overcome:
- Latency & Suitability: Cross-chain calls take time. Not every dApp can tolerate that. Part of the journey will be discovering which use cases thrive with asynchronous cross-chain logic (e.g., DeFi automation, governance, asset orchestration) and which do not.
- Tooling & Testing: Developers need confidence to build cross chain contracts. Pop CLI already offers multichain network bootstrapping with one command, but on top of that a new testing framework is needed. Blending the best of DRink! and the XCM emulator, so contracts can assert against local state, remote chain state, and cross-chain interactions in one unified test.
- Complexity of Abstractions: Stable higher-level abstractions for cross-chain interactions need to be carefully designed and assessed before they can be relied upon. Until then, developers are required to construct their own messages (e.g., ISMP storage keys or XCM programs), which can be difficult. To ease this, the creation of a shared hub of cross-chain contracts should be created, a growing collection of proven cross chain contract that the community can contribute to and use. Some example use cases have already been started here!
- Ecosystem Stability: While Messaging can guarantee stable APIs, external chains may introduce breaking changes. Building strategies to insulate developers from upstream changes, ecosystem wide, will be critical for long-term resilience.
- Relayer Economics: ISMP introduces questions around relayer incentivisation. Sustainable models will need to be explored and tested in practice to ensure good developer experience.
The immediate focus should be on creating a thriving testnet playground. By giving developers all the resources and tooling to experiment fearlessly, it can be discovered what works, refine the developer experience, and seed the first generation of cross-chain contracts. From there, a shared library of primitives will emerge, making interoperability a natural part of building on Polkadot.
The long-term vision is bold: turn Polkadot into the place where cross-chain dApps are not just possible, but effortless. This is where Polkadot can shine compared to ecosystems like Ethereum rollups or Solana, which still struggle with interoperability. If successful, the Messaging feature could be a turning point, the moment Polkadot demonstrates its true power and composability to the wider Web3 world.
This is only the beginning. But the path forward is clear: provide a testnet and tooling, experiment, gather feedback, and improve. The future of cross-chain smart contracts is unwritten, and R0GUE is here to help write it.
