Files
proxmox/docs/04-configuration/metamask/FIX_WETH9_WETH10_DECIMALS_AND_ORACLE.md
defiQUG fbda1b4beb
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
- 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>
2026-02-12 15:46:57 -08:00

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:

  1. WETH9/WETH10 Decimals: WETH9 contract returns decimals() = 0 instead of 18, causing display issues
  2. 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.


Status: Token lists already updated with correct decimals

All token list files have been updated to include WETH9 and WETH10 with decimals: 18:

  1. metamask-integration/docs/METAMASK_TOKEN_LIST.json
  2. docs/04-configuration/metamask/METAMASK_TOKEN_LIST.json
  3. 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:

  1. Host the token list JSON file on a public URL (GitHub, IPFS, or your domain)
  2. Add to MetaMask: Settings → Security & Privacy → Token Lists → Add custom token list
  3. Enter the URL of your hosted token list
  4. 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:

  1. Remove the token:

    • Open MetaMask
    • Find WETH9 in token list
    • Click three dots (⋮) → "Hide token"
  2. Re-import with correct decimals:

    • Click "Import tokens"
    • Enter:
      • Token Contract Address: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
      • Token Symbol: WETH
      • Decimals of Precision: 18 ⚠️ IMPORTANT: Manually enter 18
    • Click "Add Custom Token"
  3. 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:

  1. CoinGecko API (primary) - For tokens listed on CoinGecko
  2. Token lists - May include price metadata (limited support)
  3. 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:

  1. Fetch ETH/USD price from CoinGecko or Binance API
  2. Update the oracle contract every 60 seconds
  3. 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

  1. Token Information:

    • Token contract address
    • Token symbol and name
    • Decimals
    • Chain ID
  2. Market Data:

    • Trading volume
    • Liquidity pools
    • DEX listings
  3. Submission Process:

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)

  1. metamask-integration/docs/METAMASK_TOKEN_LIST.json
  2. docs/04-configuration/metamask/METAMASK_TOKEN_LIST.json
  3. 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

  1. Host Token List

    • Upload token list JSON to public URL
    • Add to MetaMask token lists
    • Test token import with correct decimals
  2. Verify Oracle Publisher

    • Check Oracle Publisher service status
    • Verify prices are updating
    • Test oracle contract queries
  3. Update Documentation

    • Provide user instructions for manual token import
    • Document oracle integration for dApps
    • Create CoinGecko submission guide (if needed)

Long-Term Actions

  1. CoinGecko Listing (Optional)

    • Submit tokens to CoinGecko
    • Provide market data
    • Wait for listing approval
  2. Custom MetaMask Extension (Advanced)

    • Develop custom extension that queries oracle
    • Submit to MetaMask for review
    • Enable native USD price display

  • 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