Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
Co-authored-by: Cursor <cursoragent@cursor.com>
403 lines
11 KiB
Markdown
403 lines
11 KiB
Markdown
# 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):**
|
|
1. **CoinGecko API** - Primary source (requires token listing)
|
|
2. **Token Lists** - Limited price metadata support
|
|
3. **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:**
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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:**
|
|
```javascript
|
|
// 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```javascript
|
|
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:
|
|
|
|
1. **Submit tokens to CoinGecko**
|
|
2. **Provide market data**
|
|
3. **Wait for listing approval**
|
|
|
|
### CoinGecko Submission Process
|
|
|
|
1. **Visit:** https://www.coingecko.com/en/coins/new
|
|
|
|
2. **Required Information:**
|
|
- Token name and symbol
|
|
- Contract address
|
|
- Chain ID (138)
|
|
- Decimals
|
|
- Logo URL
|
|
- Website and social links
|
|
- Market data sources (DEX, liquidity pools)
|
|
|
|
3. **Market Data Requirements:**
|
|
- Trading volume
|
|
- Liquidity pools
|
|
- DEX listings
|
|
- Price history
|
|
|
|
4. **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
|
|
|
|
```typescript
|
|
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:**
|
|
1. Check Oracle Publisher service is running
|
|
2. Verify service has correct oracle address
|
|
3. Check service logs for errors
|
|
4. Verify RPC endpoint is accessible
|
|
5. Check service has permission to update oracle
|
|
|
|
### Price is Stale
|
|
|
|
**Problem:** Oracle price hasn't updated in >5 minutes
|
|
|
|
**Solutions:**
|
|
1. Check Oracle Publisher service status
|
|
2. Verify update interval is set correctly
|
|
3. Check service logs for update errors
|
|
4. Verify API keys (if required)
|
|
5. Check network connectivity
|
|
|
|
### MetaMask Not Showing USD
|
|
|
|
**Problem:** MetaMask doesn't display USD values
|
|
|
|
**Solutions:**
|
|
1. **For native tokens (ETH):** Usually works automatically
|
|
2. **For custom tokens:** Submit to CoinGecko
|
|
3. **For dApps:** Query oracle directly and display USD
|
|
4. **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
|