Co-authored-by: Cursor <cursoragent@cursor.com>
11 KiB
Oracle Price Feed Setup for MetaMask and Wallets
Last Updated: 2026-01-31
Document Version: 1.0
Status: Active Documentation
Date: 2026-01-26
Purpose: Complete guide for setting up oracle price feeds to provide market data to MetaMask and other wallets
📋 Overview
This guide explains how to configure the oracle price feed system to provide accurate ETH/USD pricing and market data to MetaMask and other wallets on ChainID 138.
🔗 Oracle Contract Information
| Property | Value |
|---|---|
| Oracle Proxy Address | 0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 |
| Oracle Aggregator | 0x99b3511a2d315a497c8112c1fdd8d508d4b1e506 |
| Price Feed | ETH/USD |
| Decimals | 8 |
| Update Frequency | 60 seconds (heartbeat) |
| ChainID | 138 |
| RPC Endpoint | https://rpc-http-pub.d-bis.org |
⚠️ MetaMask Price Feed Limitation
Important: MetaMask does NOT automatically query oracle contracts for USD prices on custom chains.
MetaMask Price Sources (in order):
- CoinGecko API - Primary source (requires token listing)
- Token Lists - Limited price metadata support
- Oracle Contracts - NOT automatically queried
Implication: Even with a working oracle, MetaMask may not display USD values unless tokens are listed on CoinGecko.
✅ Solution 1: Oracle Publisher Service
Overview
The Oracle Publisher Service fetches prices from external APIs (CoinGecko, Binance) and updates the oracle contract on-chain.
Service Configuration
Service Location: VMID 3500 (Oracle Publisher Container)
Environment Variables:
# Oracle Contract Addresses
ORACLE_ADDRESS=0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6
AGGREGATOR_ADDRESS=0x99b3511a2d315a497c8112c1fdd8d508d4b1e506
# Network Configuration
RPC_URL=https://rpc-http-pub.d-bis.org
CHAIN_ID=138
# Update Configuration
UPDATE_INTERVAL=60 # seconds
PRICE_SOURCE=coingecko # or binance, coinbase
# API Keys (if needed)
COINGECKO_API_KEY=your-coingecko-api-key # ✅ Configured - Demo API key
BINANCE_API_KEY= # Optional
Service Status Check
# Check if container exists
ssh root@192.168.11.10 "pct list | grep 3500"
# Check service status
ssh root@192.168.11.10 "pct exec 3500 -- systemctl status oracle-publisher.service"
# Check service logs
ssh root@192.168.11.10 "pct exec 3500 -- journalctl -u oracle-publisher.service -n 50"
Service Setup
If the service doesn't exist, create it:
# 1. Create container (VMID 3500)
# 2. Install Node.js/Python runtime
# 3. Install Oracle Publisher service
# 4. Configure environment variables
# 5. Start service
Service Script Example:
// oracle-publisher.js
const { ethers } = require('ethers');
const axios = require('axios');
const ORACLE_ADDRESS = process.env.ORACLE_ADDRESS;
const RPC_URL = process.env.RPC_URL;
const UPDATE_INTERVAL = parseInt(process.env.UPDATE_INTERVAL || '60');
const COINGECKO_API_KEY = process.env.COINGECKO_API_KEY; // ✅ Configured
const provider = new ethers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const oracleABI = [
"function updateAnswer(int256 answer) external",
"function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80)"
];
const oracle = new ethers.Contract(ORACLE_ADDRESS, oracleABI, signer);
async function fetchETHPrice() {
try {
// Fetch from CoinGecko (with API key for higher rate limits)
const apiKeyParam = COINGECKO_API_KEY ? `&x_cg_demo_api_key=${COINGECKO_API_KEY}` : '';
const response = await axios.get(
`https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd${apiKeyParam}`
);
const price = response.data.ethereum.usd;
return Math.round(price * 1e8); // Convert to 8 decimals
} catch (error) {
console.error('Error fetching price:', error);
return null;
}
}
async function updateOracle() {
const price = await fetchETHPrice();
if (!price) {
console.error('Failed to fetch price');
return;
}
try {
const tx = await oracle.updateAnswer(price);
await tx.wait();
console.log(`Oracle updated: ETH/USD = $${price / 1e8}`);
} catch (error) {
console.error('Error updating oracle:', error);
}
}
// Update every 60 seconds
setInterval(updateOracle, UPDATE_INTERVAL * 1000);
updateOracle(); // Initial update
✅ Solution 2: Verify Oracle Price Data
On-Chain Verification
# Get latest price from oracle
cast call 0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 \
"latestRoundData()" \
--rpc-url https://rpc-http-pub.d-bis.org
# Expected output:
# (roundId, answer, startedAt, updatedAt, answeredInRound)
# answer is in 8 decimals (e.g., 3000000000 = $3000.00)
JavaScript Verification
const { ethers } = require('ethers');
async function verifyOraclePrice() {
const provider = new ethers.JsonRpcProvider('https://rpc-http-pub.d-bis.org');
const oracleABI = [
"function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80)",
"function decimals() external view returns (uint8)"
];
const oracle = new ethers.Contract(
'0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6',
oracleABI,
provider
);
const [roundId, answer, startedAt, updatedAt, answeredInRound] = await oracle.latestRoundData();
const decimals = await oracle.decimals();
const price = Number(answer) / Math.pow(10, decimals);
const lastUpdate = new Date(Number(updatedAt) * 1000);
const now = new Date();
const ageMinutes = (now - lastUpdate) / 1000 / 60;
console.log('Oracle Price Feed Status:');
console.log(` ETH/USD Price: $${price.toFixed(2)}`);
console.log(` Round ID: ${roundId}`);
console.log(` Last Update: ${lastUpdate.toISOString()}`);
console.log(` Age: ${ageMinutes.toFixed(1)} minutes`);
console.log(` Status: ${ageMinutes < 5 ? '✅ Fresh' : '❌ Stale'}`);
return { price, lastUpdate, ageMinutes, fresh: ageMinutes < 5 };
}
verifyOraclePrice();
✅ Solution 3: CoinGecko Listing (For Native MetaMask Support)
Why CoinGecko?
MetaMask primarily uses CoinGecko API for USD price display. To get native MetaMask support:
- Submit tokens to CoinGecko
- Provide market data
- Wait for listing approval
CoinGecko Submission Process
-
Required Information:
- Token name and symbol
- Contract address
- Chain ID (138)
- Decimals
- Logo URL
- Website and social links
- Market data sources (DEX, liquidity pools)
-
Market Data Requirements:
- Trading volume
- Liquidity pools
- DEX listings
- Price history
-
Review Process:
- Typically 1-2 weeks
- CoinGecko team reviews submission
- May request additional information
Current Token Status
| Token | CoinGecko Listed | Action Required |
|---|---|---|
| ETH | ✅ Yes | None |
| WETH9 | ❌ No | Submit for listing |
| WETH10 | ❌ No | Submit for listing |
| cUSDT | ❌ No | Submit for listing |
| cUSDC | ❌ No | Submit for listing |
✅ Solution 4: dApp Integration (Query Oracle Directly)
For dApps, you can query the oracle contract directly and display USD values:
React Example
import { ethers } from 'ethers';
import { useEffect, useState } from 'react';
const ORACLE_ADDRESS = '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6';
const RPC_URL = 'https://rpc-http-pub.d-bis.org';
const oracleABI = [
"function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80)"
];
export function useETHPrice() {
const [price, setPrice] = useState<number | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchPrice() {
try {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const oracle = new ethers.Contract(ORACLE_ADDRESS, oracleABI, provider);
const [, answer] = await oracle.latestRoundData();
const ethPrice = Number(answer) / 1e8;
setPrice(ethPrice);
} catch (error) {
console.error('Error fetching ETH price:', error);
} finally {
setLoading(false);
}
}
fetchPrice();
const interval = setInterval(fetchPrice, 60000); // Update every minute
return () => clearInterval(interval);
}, []);
return { price, loading };
}
// Usage in component
function BalanceDisplay({ balance }: { balance: string }) {
const { price, loading } = useETHPrice();
const ethBalance = parseFloat(balance);
const usdValue = price ? ethBalance * price : null;
return (
<div>
<p>{ethBalance} ETH</p>
{usdValue && <p>${usdValue.toFixed(2)} USD</p>}
{loading && <p>Loading price...</p>}
</div>
);
}
📋 Complete Setup Checklist
Oracle Publisher Service
- Service Exists - Verify VMID 3500 exists
- Service Running - Check service status
- Configuration - Verify environment variables
- Price Updates - Verify prices updating every 60 seconds
- Price Accuracy - Compare with CoinGecko/Binance
Oracle Contract
- Contract Deployed - Verify oracle proxy at
0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 - Price Data - Verify contract has price data
- Price Freshness - Verify prices updated within last 5 minutes
- Decimals - Verify oracle returns 8 decimals
CoinGecko Listing (Optional)
- Token Information - Prepare token details
- Market Data - Gather trading volume and liquidity data
- Submission - Submit tokens to CoinGecko
- Follow-up - Respond to CoinGecko requests
dApp Integration
- Oracle Integration - Add oracle querying to dApps
- Price Display - Display USD values in UI
- Error Handling - Handle oracle query failures
- Caching - Cache prices to reduce RPC calls
🔍 Troubleshooting
Oracle Returns Zero Price
Problem: Oracle contract returns all zeros
Solutions:
- Check Oracle Publisher service is running
- Verify service has correct oracle address
- Check service logs for errors
- Verify RPC endpoint is accessible
- Check service has permission to update oracle
Price is Stale
Problem: Oracle price hasn't updated in >5 minutes
Solutions:
- Check Oracle Publisher service status
- Verify update interval is set correctly
- Check service logs for update errors
- Verify API keys (if required)
- Check network connectivity
MetaMask Not Showing USD
Problem: MetaMask doesn't display USD values
Solutions:
- For native tokens (ETH): Usually works automatically
- For custom tokens: Submit to CoinGecko
- For dApps: Query oracle directly and display USD
- Alternative: Use token list with price metadata (limited support)
📚 Related Documentation
- Oracle Integration:
metamask-integration/docs/METAMASK_ORACLE_INTEGRATION.md - Contract Addresses:
docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md - Token List Guide:
docs/11-references/TOKEN_LIST_AUTHORING_GUIDE.md - WETH9/WETH10 Fix:
docs/04-configuration/metamask/FIX_WETH9_WETH10_DECIMALS_AND_ORACLE.md
Last Updated: 2026-01-26
Status: ✅ Complete oracle setup guide