Chainlink hasn't deployed here. We did.
Polkadot Hub has no price oracle. Chainlink, Pyth, DIA, Band -none of them have deployed to Hub's PVM. Every DeFi primitive -lending, stablecoins, derivatives, liquidation engines -requires reliable on-chain price data to function. Without an oracle, no DeFi can exist on Hub.
Parachains face the same gap. There's no native way to get a trusted price feed from Hub and distribute it across the ecosystem. Projects either build their own fragile price feeds or don't build at all.
PolkaOracle is the first decentralized price oracle built natively for Polkadot Hub. Independent reporters stake PAS tokens, fetch prices from multiple exchanges (CoinGecko, CoinPaprika, Binance), and submit them on-chain. The contract aggregates via median -computed by a Rust contract compiled to PolkaVM (RISC-V) -and distributes prices cross-chain to any parachain via XCM.
Reporters who deviate from the median get slashed. No trust required -just math and stake.
| Live App | polkaoracle.vercel.app |
| Dashboard | polkaoracle.vercel.app/dashboard |
| Docs | polkaoracle.vercel.app/docs |
| Repo | github.com/IronicDeGawd/polkaoracle-dorahacks-2026 |
Polkadot Hub TestNet · Chain ID 420420417 · Explorer
| Contract | Address |
|---|---|
| PriceOracle | 0xaf781298fE769738eF529816c11C121Ea8887Fc9 |
| OracleStaking | 0xd2A807a574CAEa31e6DA7102feA1b2071A6a3321 |
| OracleConsumer | 0xa442FAdAa070fB1C89d0dBFBfa0B488110E25146 |
| RustMedian | 0x454d0fB0BBB30c08f05e2DfEF94E0fe106eDFADA |
Moonbase Alpha · Chain ID 1287 · Explorer
| Contract | Address |
|---|---|
| XcmPriceReceiver | 0xad81ed5a1e0c8Ea28E8a52Fee6aC1D51131DD0cB |
Feeds: DOT/USD, BTC/USD, ETH/USD (8 decimal precision)
CoinGecko ──► Reporter 1 ─┐ ┌─ RustMedian (RISC-V)
CoinPaprika ──► Reporter 2 ─┤──► submitPrice() ──► PriceOracle ──┤ (staticcall + fallback)
Binance ──► Reporter 3 ─┘ │ └─ Solidity median
OracleStaking ──► Relay ──► Moonbase Alpha
(10 PAS min stake, 50% slash)
- PriceOracle -Submission rounds, median aggregation, TWAP (60-entry circular buffer), deviation checks, XCM broadcast
- OracleStaking -Stake/unstake with cooldown, slashing, pause, reentrancy guard
- OracleConsumer -Reference dApp: getDOTPrice, calculateDOTValue, collateral checks
- XcmPriceReceiver -Cross-chain receiver with batch relay and staleness validation
PolkaOracle isn't a Chainlink fork redeployed on a new chain. Its design depends on primitives that only exist in Polkadot's architecture:
- No competing oracle on Hub -Chainlink, Pyth, DIA, and Band have not deployed to Polkadot Hub. PolkaOracle is the first and only oracle available to PVM smart contracts.
- XCM-native price distribution -Prices broadcast to any parachain via the XCM precompile (
0x0...0a0000). No bridge contracts, no third-party relay protocols -this is a Polkadot-only messaging primitive. - Native token staking for accountability -Reporters stake PAS (Hub's native token) directly. Slashing is enforced on-chain without deploying a separate staking token. On EVM L1s, this requires a custom ERC-20 + staking contract.
- PVM execution -Compiled to PolkaVM (RISC-V), not the EVM. Same Solidity source, different runtime with Polkadot-native 3-dimensional gas metering (ref_time + proof_size + storage_deposit).
PolkaOracle's median aggregation is also implemented as a native Rust contract compiled to PolkaVM (RISC-V). The Solidity oracle calls the Rust contract via standard ABI -demonstrating cross-language smart contract interop on PVM.
PriceOracle.sol (Solidity) ──► IRustMedian.median() ──► RustMedian (Rust/RISC-V)
| Implementation | median(3 values) Gas |
Binary Size | Notes |
|---|---|---|---|
| Solidity | ~2,400 (est.) | - | Insertion sort + median, EVM-compiled to PVM |
| Rust | 1,829 | 15 KB | Native RISC-V, same ABI, #![no_std] |
The Rust contract is compiled with cargo build --release using pallet-revive-uapi for host functions and alloy's sol! macro for ABI compatibility. Source: rust-median/.
Deployed: 0x454d0fB0BBB30c08f05e2DfEF94E0fe106eDFADA on Paseo Hub TestNet
PriceOracle oracle = PriceOracle(ORACLE_ADDRESS);
bytes32 DOT_USD = keccak256(abi.encodePacked("DOT/USD"));
// Spot price
(uint256 price, uint256 timestamp) = oracle.getPrice(DOT_USD);
// Fresh price (reverts if stale)
(uint256 fresh, ) = oracle.getFreshPrice(DOT_USD, 1 hours);
// TWAP (manipulation resistant)
uint256 twap = oracle.getTWAP(DOT_USD, 5 minutes);
// XCM broadcast to parachain
oracle.broadcastPrice(DOT_USD, 2000);Real-world example -a lending protocol using PolkaOracle for collateral valuation:
contract LendingPool {
PriceOracle oracle = PriceOracle(0xaf781298fE769738eF529816c11C121Ea8887Fc9);
bytes32 constant DOT_USD = keccak256(abi.encodePacked("DOT/USD"));
function borrow(uint256 dotCollateral, uint256 borrowUSD) external {
uint256 dotPrice = oracle.getTWAP(DOT_USD, 5 minutes); // manipulation-resistant
uint256 collateralUSD = (dotCollateral * dotPrice) / 1e8;
require(collateralUSD >= borrowUSD * 150 / 100, "Under-collateralized");
// ... execute borrow
}
}See OracleConsumer.sol for a full reference implementation, or the integration docs for JavaScript examples.
git clone https://github.com/IronicDeGawd/polkaoracle-dorahacks-2026.git
cd polkadot-hackathon
# Contracts
cd contracts && npm install
npx hardhat test # 91 tests
# Reporter Bot
cd ../reporter && npm install
npm run reporter:1 # CoinGecko
npm run reporter:2 # CoinPaprika
npm run reporter:3 # Binance
# Frontend
cd ../frontend && npm install && npm run dev91 tests across 3 suites covering price submission, median aggregation, TWAP calculation, deviation rejection, stale data handling, slashing, round management, XCM relay, and Rust median integration (fallback, CEI ordering, multi-feed).
cd contracts && npx hardhat test # 91 passing- Reentrancy guard on all staking operations
- 10% deviation cap (5x wider band for stale prices to prevent freeze)
- Configurable max price age (1 hour default)
- Time-bounded submission rounds with monotonic round counter
- 50% slashing for reporters deviating from median
- SCALE-encoded XCM with conservative weight estimates
| Operation | Gas (ref_time) | Notes |
|---|---|---|
submitPrice() |
66,586 | Write: stores price, triggers median aggregation |
getPrice() |
1,316 | Read: single storage lookup |
getTWAP() |
~2,390 | Read: iterates circular buffer (estimate) |
Rust vs Solidity Median (pure compute)
| Implementation | Gas (ref_time) | Binary | Notes |
|---|---|---|---|
| Rust (RISC-V) | 1,829 | 15 KB | PolkaVM-native, called via staticcall |
| Solidity | ~2,400 | - | Inline in PriceOracle, used as fallback |
PriceOracle tries the Rust contract first; if the staticcall fails or returns unexpected data, it falls back to the Solidity implementation automatically.
PVM uses 3-dimensional gas: ref_time (compute), proof_size (state proof), and storage_deposit (new storage). The numbers above reflect ref_time only. Measured on live testnet with benchmark script.
Solidity ^0.8.28 · Rust (PVM-native RISC-V) · PVM (PolkaVM) · Hardhat · ethers.js v6 · Next.js · Recharts · XCM V3
MIT