XCM Broadcasting
Polkadot's Cross-Consensus Messaging (XCM) allows PolkaOracle to broadcast verified price feeds directly from the Polkadot Hub to any connected parachain seamlessly, without requiring the parachain to query the Hub manually.
How Broadcasting Works
The PriceOracle.sol contract on the Polkadot Hub leverages an EVM precompile mapped to the XCM pallet. Once the median price is aggregated, it can be pushed over XCM to destination parachains that have deployed an XcmPriceReceiver contract.
XCM Broadcast Flow
Price Aggregated
median(submissions) → aggregatedPrice
SCALE Encode Payload
feedId + price + timestamp → bytes
XCM Precompile Send
IXcm(0x...0a0000).send(dest, message)
Relay Chain Routes Message
XCM V3 Transact instruction forwarded to destination parachain
Parachain Receives Price
XcmPriceReceiver.updatePrice(feedId, price)
Multi-Chain Distribution
Polkadot Hub
PriceOracle
DOT · BTC · ETH
- XCM Precompile Address:
0x00000000000000000000000000000000000a0000 - broadcastPrice(bytes32, uint32): Owner-callable function to broadcast a single feed to a specific
destParaId. - broadcastAllPrices(uint32): Broadcasts all active feeds to the destination.
Live Cross-Chain Deployment
An XcmPriceReceiver is deployed and receiving live price relays on Moonbase Alpha:
- Contract: 0xad81...D0cB
- Chain: Moonbase Alpha (Chain ID: 1287)
- Feeds: DOT/USD, BTC/USD, ETH/USD — relayed from Polkadot Hub
See relay status on the Dashboard.
Parachain Receiver Implementation
Destination parachains deploy an XcmPriceReceiver contract that accepts price updates from an authorized relayer or XCM origin. The receiver validates freshness, rejects stale updates, and supports both single and batch price relays.
contract XcmPriceReceiver {
struct PriceUpdate {
uint256 price; // 8 decimals
uint256 timestamp; // source chain timestamp
uint256 receivedAt; // destination chain timestamp
}
address public owner;
address public authorizedOrigin; // relayer or XCM sovereign account
mapping(bytes32 => PriceUpdate) public prices;
// Single price relay
function receivePrice(
bytes32 feedId, uint256 price, uint256 sourceTimestamp
) external onlyAuthorized {
require(sourceTimestamp > prices[feedId].timestamp, "Stale");
prices[feedId] = PriceUpdate(price, sourceTimestamp, block.timestamp);
}
// Batch relay (gas efficient)
function receivePriceBatch(
bytes32[] calldata feedIds,
uint256[] calldata _prices,
uint256[] calldata timestamps
) external onlyAuthorized { /* ... */ }
// Read price
function getPrice(bytes32 feedId) external view
returns (uint256 price, uint256 timestamp);
}