Cross-Chain

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

1

Price Aggregated

median(submissions) → aggregatedPrice

2

SCALE Encode Payload

feedId + price + timestamp → bytes

3

XCM Precompile Send

IXcm(0x...0a0000).send(dest, message)

4

Relay Chain Routes Message

XCM V3 Transact instruction forwarded to destination parachain

5

Parachain Receives Price

XcmPriceReceiver.updatePrice(feedId, price)

Multi-Chain Distribution

Polkadot Hub

PriceOracle

DOT · BTC · ETH

XCM
Moonbase Alpha
LIVE
Moonbeam
Para ID: 2004
Astar
Para ID: 2006
Acala
Para ID: 2000
  • 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);
}