- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands - CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround - CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check - NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere - MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates - LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference Co-authored-by: Cursor <cursoragent@cursor.com>
13 KiB
Fix WETH9/WETH10 Decimals and Oracle Pricing for MetaMask
Last Updated: 2026-01-31
Document Version: 1.0
Status: Active Documentation
Date: 2026-01-26
Status: ✅ Complete Fix Guide
📋 Overview
This guide addresses two issues:
- WETH9/WETH10 Decimals: WETH9 contract returns
decimals() = 0instead of 18, causing display issues - Oracle Pricing: Ensuring price feeds provide correct market data to MetaMask and other wallets
🔧 Part 1: Fix WETH9 and WETH10 Decimals
Issue Summary
| Token | Contract Address | Expected Decimals | Contract Returns | Status |
|---|---|---|---|---|
| WETH9 | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 |
18 | 0 ❌ | Needs fix |
| WETH10 | 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f |
18 | 18 ✅ | Correct |
Root Cause: WETH9 is a pre-deployed contract (genesis) that doesn't implement the standard decimals() function correctly. It returns 0 instead of 18.
Solution 1: Use Updated Token List (Recommended)
Status: ✅ Token lists already updated with correct decimals
All token list files have been updated to include WETH9 and WETH10 with decimals: 18:
- ✅
metamask-integration/docs/METAMASK_TOKEN_LIST.json - ✅
docs/04-configuration/metamask/METAMASK_TOKEN_LIST.json - ✅
token-lists/lists/dbis-138.tokenlist.json
Token List Entry for WETH9:
{
"chainId": 138,
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"name": "Wrapped Ether",
"symbol": "WETH",
"decimals": 18,
"logoURI": "https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png",
"tags": ["defi", "wrapped"]
}
Token List Entry for WETH10:
{
"chainId": 138,
"address": "0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f",
"name": "Wrapped Ether v10",
"symbol": "WETH10",
"decimals": 18,
"logoURI": "https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png",
"tags": ["defi", "wrapped"]
}
How to Use:
- Host the token list JSON file on a public URL (GitHub, IPFS, or your domain)
- Add to MetaMask: Settings → Security & Privacy → Token Lists → Add custom token list
- Enter the URL of your hosted token list
- MetaMask will automatically use the correct decimals from the token list
Solution 2: Manual Token Import (For Users)
If users have already imported WETH9 with incorrect decimals, they need to:
-
Remove the token:
- Open MetaMask
- Find WETH9 in token list
- Click three dots (⋮) → "Hide token"
-
Re-import with correct decimals:
- Click "Import tokens"
- Enter:
- Token Contract Address:
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 - Token Symbol:
WETH - Decimals of Precision:
18⚠️ IMPORTANT: Manually enter 18
- Token Contract Address:
- Click "Add Custom Token"
-
Verify:
- Balance should display correctly (e.g., "6 WETH" instead of "6,000,000,000.0T WETH")
Solution 3: Programmatic Fix (For dApps)
If you're building a dApp, you can override the decimals when displaying balances:
// WETH9 address
const WETH9_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const WETH9_DECIMALS = 18; // Override contract's incorrect decimals()
// Get balance
const balance = await contract.balanceOf(userAddress);
// Format with correct decimals
const formattedBalance = ethers.formatUnits(balance, WETH9_DECIMALS);
console.log(`${formattedBalance} WETH`);
💰 Part 2: Oracle Pricing and Market Data
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 |
⚠️ Important: MetaMask Price Feed Limitation
MetaMask does NOT automatically query oracle contracts for USD prices on custom chains.
MetaMask uses:
- CoinGecko API (primary) - For tokens listed on CoinGecko
- Token lists - May include price metadata (limited support)
- Oracle contracts - NOT automatically queried by MetaMask
This means: Even if your oracle contract has correct price data, MetaMask may not display USD values unless:
- The token is listed on CoinGecko
- OR you use a custom MetaMask extension/plugin (requires development)
Solution 1: Ensure Oracle Has Correct Price Data
For dApps and custom integrations, ensure the oracle contract is updated with current prices:
Step 1: Verify Oracle Publisher Service
# Check if Oracle Publisher service exists (VMID 3500)
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"
Step 2: Configure Oracle Publisher
The Oracle Publisher service should:
- Fetch ETH/USD price from CoinGecko or Binance API
- Update the oracle contract every 60 seconds
- Maintain heartbeat to ensure price freshness
Configuration:
# Environment variables for Oracle Publisher
ORACLE_ADDRESS=0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6
AGGREGATOR_ADDRESS=0x99b3511a2d315a497c8112c1fdd8d508d4b1e506
RPC_URL=https://rpc-http-pub.d-bis.org
CHAIN_ID=138
UPDATE_INTERVAL=60 # seconds
PRICE_SOURCE=coingecko # or binance
Step 3: Verify Oracle Price Data
# Check latest price from oracle
cast call 0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 \
"latestRoundData()" \
--rpc-url https://rpc-http-pub.d-bis.org
# Expected output format:
# (roundId, answer, startedAt, updatedAt, answeredInRound)
# answer is in 8 decimals (e.g., 3000000000 = $3000.00)
JavaScript Example:
const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider('https://rpc-http-pub.d-bis.org');
const oracleABI = [
"function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80)"
];
const oracle = new ethers.Contract(
'0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6',
oracleABI,
provider
);
async function getETHPrice() {
const result = await oracle.latestRoundData();
const price = Number(result.answer) / 1e8; // Convert from 8 decimals
console.log(`ETH/USD: $${price}`);
return price;
}
getETHPrice();
Solution 2: CoinGecko Listing (For MetaMask Native Support)
To get MetaMask to display USD prices automatically, tokens need to be listed on CoinGecko:
Requirements for CoinGecko Listing
-
Token Information:
- Token contract address
- Token symbol and name
- Decimals
- Chain ID
-
Market Data:
- Trading volume
- Liquidity pools
- DEX listings
-
Submission Process:
- Visit: https://www.coingecko.com/en/coins/new
- Fill out token information
- Provide market data sources
- Wait for review (typically 1-2 weeks)
Current Tokens Status
| Token | CoinGecko Listed | MetaMask USD Display |
|---|---|---|
| ETH | ✅ Yes (native) | ✅ Works |
| WETH9 | ⚠️ May need listing | ❌ May not show USD |
| WETH10 | ⚠️ May need listing | ❌ May not show USD |
| cUSDT | ⚠️ May need listing | ❌ May not show USD |
| cUSDC | ⚠️ May need listing | ❌ May not show USD |
Action Required: Submit tokens to CoinGecko for listing if you want native MetaMask USD support.
Solution 3: Custom dApp Price Display
For dApps, you can query the oracle and display USD values:
import { ethers } from 'ethers';
// Oracle configuration
const ORACLE_ADDRESS = '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6';
const RPC_URL = 'https://rpc-http-pub.d-bis.org';
// Get ETH price from oracle
async function getETHPrice() {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const oracleABI = [
"function latestRoundData() external view returns (uint80, int256, uint256, uint256, uint80)"
];
const oracle = new ethers.Contract(ORACLE_ADDRESS, oracleABI, provider);
const result = await oracle.latestRoundData();
const price = Number(result.answer) / 1e8;
return price;
}
// Display balance with USD value
async function displayBalanceWithUSD(userAddress) {
const provider = new ethers.JsonRpcProvider(RPC_URL);
const balance = await provider.getBalance(userAddress);
const ethBalance = ethers.formatEther(balance);
const ethPrice = await getETHPrice();
const usdValue = parseFloat(ethBalance) * ethPrice;
console.log(`${ethBalance} ETH ($${usdValue.toFixed(2)})`);
return { ethBalance, usdValue };
}
Solution 4: Token List with Price Metadata
Limited Support: Some wallets may read price metadata from token lists, but MetaMask has limited support.
Token List Entry with Price:
{
"chainId": 138,
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"name": "Wrapped Ether",
"symbol": "WETH",
"decimals": 18,
"logoURI": "...",
"extensions": {
"price": {
"usd": 3000.00,
"source": "oracle",
"oracleAddress": "0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6"
}
}
}
Note: This is experimental and may not work with all wallets.
📋 Complete Fix Checklist
WETH9/WETH10 Decimals
- Token Lists Updated - All token lists include decimals: 18
- Token List Hosted - Host token list on public URL
- User Instructions - Provide manual import instructions
- dApp Integration - Update dApps to use correct decimals
Oracle Pricing
- Oracle Publisher Running - Verify service is active (VMID 3500)
- Oracle Price Updates - Verify prices are updating every 60 seconds
- Price Accuracy - Verify prices match CoinGecko/Binance
- CoinGecko Listing - Submit tokens for CoinGecko listing (optional)
- dApp Integration - Update dApps to query oracle for prices
🔍 Verification Steps
Verify WETH9/WETH10 Decimals
# Check token list includes correct decimals
cat metamask-integration/docs/METAMASK_TOKEN_LIST.json | jq '.tokens[] | select(.symbol == "WETH" or .symbol == "WETH10") | {symbol, decimals}'
# Expected output:
# {
# "symbol": "WETH",
# "decimals": 18
# }
# {
# "symbol": "WETH10",
# "decimals": 18
# }
Verify Oracle Price Data
# Check oracle has price data
cast call 0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6 \
"latestRoundData()" \
--rpc-url https://rpc-http-pub.d-bis.org
# Check price is recent (updatedAt should be within last 5 minutes)
# Convert answer from 8 decimals to USD
JavaScript Verification:
const { ethers } = require('ethers');
async function verifyOracle() {
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(`ETH/USD Price: $${price}`);
console.log(`Last Update: ${lastUpdate.toISOString()} (${ageMinutes.toFixed(1)} minutes ago)`);
console.log(`Price Fresh: ${ageMinutes < 5 ? '✅ Yes' : '❌ No (stale)'}`);
return { price, lastUpdate, ageMinutes };
}
verifyOracle();
📝 Files Updated
Token List Files (Already Updated)
- ✅
metamask-integration/docs/METAMASK_TOKEN_LIST.json - ✅
docs/04-configuration/metamask/METAMASK_TOKEN_LIST.json - ✅
token-lists/lists/dbis-138.tokenlist.json
All files include:
- WETH9 with
decimals: 18 - WETH10 with
decimals: 18 - Oracle price feed entry
🚀 Next Steps
Immediate Actions
-
Host Token List
- Upload token list JSON to public URL
- Add to MetaMask token lists
- Test token import with correct decimals
-
Verify Oracle Publisher
- Check Oracle Publisher service status
- Verify prices are updating
- Test oracle contract queries
-
Update Documentation
- Provide user instructions for manual token import
- Document oracle integration for dApps
- Create CoinGecko submission guide (if needed)
Long-Term Actions
-
CoinGecko Listing (Optional)
- Submit tokens to CoinGecko
- Provide market data
- Wait for listing approval
-
Custom MetaMask Extension (Advanced)
- Develop custom extension that queries oracle
- Submit to MetaMask for review
- Enable native USD price display
📚 Related Documentation
- Token List Guide:
docs/11-references/TOKEN_LIST_AUTHORING_GUIDE.md - Oracle Integration:
metamask-integration/docs/METAMASK_ORACLE_INTEGRATION.md - Contract Addresses:
docs/11-references/CONTRACT_ADDRESSES_REFERENCE.md - WETH9 Fix Instructions:
metamask-integration/docs/METAMASK_WETH9_FIX_INSTRUCTIONS.md
Last Updated: 2026-01-26
Status: ✅ Complete fix guide created