feat: refresh token list and oracle provider wiring
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
This repository contains all MetaMask integration components for ChainID 138, including:
|
This repository contains all MetaMask integration components for ChainID 138, including:
|
||||||
- Network configuration
|
- Network configuration
|
||||||
- Token lists
|
- Token lists (**`config/token-list.json`** mirrors the parent repo **`token-lists/lists/dbis-138.tokenlist.json`**; live apps typically use **`https://explorer.d-bis.org/api/config/token-list`** for the multichain DUAL list)
|
||||||
- Price feed integration
|
- Price feed integration
|
||||||
- **Smart Accounts Kit integration** ✅
|
- **Smart Accounts Kit integration** ✅
|
||||||
- Documentation
|
- Documentation
|
||||||
|
|||||||
Submodule chain138-snap-minimal updated: 5293b62a61...8c52fea845
File diff suppressed because one or more lines are too long
@@ -22,7 +22,9 @@ export {
|
|||||||
ORACLES_CHAIN_138,
|
ORACLES_CHAIN_138,
|
||||||
ORACLES_MAINNET,
|
ORACLES_MAINNET,
|
||||||
ORACLE_ABI,
|
ORACLE_ABI,
|
||||||
|
CHAIN138_STABLE_USD_1,
|
||||||
getEthUsdPrice,
|
getEthUsdPrice,
|
||||||
|
getAssetUsdPrice,
|
||||||
getOracleConfig,
|
getOracleConfig,
|
||||||
} from './oracles.js'
|
} from './oracles.js'
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,48 @@
|
|||||||
/**
|
/**
|
||||||
* Oracle addresses and helpers for Chain 138 and Ethereum Mainnet.
|
* Oracle addresses and helpers for Chain 138 and Ethereum Mainnet.
|
||||||
* Use these to read price feeds so MetaMask and dApps can display USD values.
|
* Use these to read price feeds so dApps (and optional wallet overlays) can show USD values.
|
||||||
|
*
|
||||||
|
* Chain 138: MetaMask’s **native** token/fiat column uses a **central price service**, not
|
||||||
|
* your RPC or on-chain feeds — custom chains often stay unmapped until third-party listings exist.
|
||||||
|
* Use `getEthUsdPrice` / `getAssetUsdPrice` in **your dApp UI**, explorer Snap flows, or
|
||||||
|
* token-aggregation APIs for multi-asset USD.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Chain 138: ETH/USD proxy and aggregator */
|
/** Chain 138: ETH/USD reads (prefer keeper-synced mock; legacy proxy often returns zero) */
|
||||||
export const ORACLES_CHAIN_138 = {
|
export const ORACLES_CHAIN_138 = {
|
||||||
chainId: 138,
|
chainId: 138,
|
||||||
|
/** Legacy proxy — do not rely on for live reads */
|
||||||
ethUsdProxy: '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6',
|
ethUsdProxy: '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6',
|
||||||
aggregator: '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506',
|
/** Keeper-synced MockPriceFeed (8 decimals); same as `CHAIN138_WETH_MOCK_PRICE_FEED` */
|
||||||
|
ethUsdAggregator: '0x3e8725b8De386feF3eFE5678c92eA6aDB41992B2',
|
||||||
|
/** Managed aggregator slot (Chainlink-style staleness rules; can lag on Besu) */
|
||||||
|
legacyEthUsdAggregator: '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506',
|
||||||
decimals: 8,
|
decimals: 8,
|
||||||
rpcUrl: 'https://rpc-http-pub.d-bis.org',
|
rpcUrl: 'https://rpc-http-pub.d-bis.org',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Chain 138 tokens we treat as ~$1 USD for dApp / Snap UX hints (not MetaMask’s built-in column).
|
||||||
|
* Includes compliant **cUSDT/cUSDC**, **V2** mints, and official-mirror **USDT/USDC** used in D3 routing.
|
||||||
|
*/
|
||||||
|
export const CHAIN138_STABLE_USD_1 = new Set(
|
||||||
|
[
|
||||||
|
'0x93E66202A11B1772E55407B32B44e5Cd8eda7f22', // cUSDT
|
||||||
|
'0xf22258f57794CC8E06237084b353Ab30fFfa640b', // cUSDC
|
||||||
|
'0x9FBfab33882Efe0038DAa608185718b772EE5660', // cUSDT V2
|
||||||
|
'0x219522c60e83dEe01FC5b0329d6fA8fD84b9D13d', // cUSDC V2
|
||||||
|
'0x004b63A7B5b0E06f6bB6adb4a5F9f590BF3182D1', // USDT (official mirror, D3)
|
||||||
|
'0x71D6687F38b93CCad569Fa6352c876eea967201b', // USDC (official mirror, D3)
|
||||||
|
].map((a) => a.toLowerCase()),
|
||||||
|
)
|
||||||
|
|
||||||
|
const WETH_VARIANTS = new Set(
|
||||||
|
[
|
||||||
|
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH9
|
||||||
|
'0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f', // WETH10
|
||||||
|
].map((a) => a.toLowerCase()),
|
||||||
|
)
|
||||||
|
|
||||||
/** Ethereum Mainnet: Chainlink ETH/USD */
|
/** Ethereum Mainnet: Chainlink ETH/USD */
|
||||||
export const ORACLES_MAINNET = {
|
export const ORACLES_MAINNET = {
|
||||||
chainId: 1,
|
chainId: 1,
|
||||||
@@ -27,31 +58,80 @@ export const ORACLE_ABI = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get ETH/USD price from the appropriate oracle for the given chain.
|
* Read ETH/USD from a Chainlink-compatible feed contract.
|
||||||
|
* @param {import('ethers').Provider} provider
|
||||||
|
* @param {string} feedAddress
|
||||||
|
*/
|
||||||
|
async function readEthUsdFromFeed(provider, feedAddress) {
|
||||||
|
const { ethers } = await import('ethers')
|
||||||
|
const contract = new ethers.Contract(feedAddress, ORACLE_ABI, provider)
|
||||||
|
const [roundId, answer, , updatedAt] = await contract.latestRoundData()
|
||||||
|
const decimals = Number(await contract.decimals())
|
||||||
|
const price = Number(answer) / 10 ** decimals
|
||||||
|
return {
|
||||||
|
price,
|
||||||
|
updatedAt: new Date(Number(updatedAt) * 1000),
|
||||||
|
decimals,
|
||||||
|
roundId: Number(roundId),
|
||||||
|
feedAddress,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ETH/USD price for the given chain (138 uses keeper-synced mock first).
|
||||||
* @param {import('ethers').Provider} provider - ethers v6 JsonRpcProvider or BrowserProvider
|
* @param {import('ethers').Provider} provider - ethers v6 JsonRpcProvider or BrowserProvider
|
||||||
* @param {number} chainId - 138 or 1
|
* @param {number} chainId - 138 or 1
|
||||||
* @returns {Promise<{ price: number, updatedAt: Date, decimals: number } | null>}
|
* @returns {Promise<{ price: number, updatedAt: Date, decimals: number, feedAddress?: string } | null>}
|
||||||
*/
|
*/
|
||||||
export async function getEthUsdPrice(provider, chainId) {
|
export async function getEthUsdPrice(provider, chainId) {
|
||||||
const oracleConfig = chainId === 138 ? ORACLES_CHAIN_138 : chainId === 1 ? ORACLES_MAINNET : null
|
if (chainId === 1) {
|
||||||
if (!oracleConfig) return null
|
try {
|
||||||
|
return await readEthUsdFromFeed(provider, ORACLES_MAINNET.ethUsdProxy)
|
||||||
try {
|
} catch (err) {
|
||||||
const { ethers } = await import('ethers')
|
console.error('getEthUsdPrice mainnet error:', err)
|
||||||
const contract = new ethers.Contract(oracleConfig.ethUsdProxy, ORACLE_ABI, provider)
|
return null
|
||||||
const [roundId, answer, startedAt, updatedAt] = await contract.latestRoundData()
|
|
||||||
const decimals = Number(await contract.decimals())
|
|
||||||
const price = Number(answer) / Math.pow(10, decimals)
|
|
||||||
return {
|
|
||||||
price,
|
|
||||||
updatedAt: new Date(Number(updatedAt) * 1000),
|
|
||||||
decimals,
|
|
||||||
roundId: Number(roundId),
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.error('getEthUsdPrice error:', err)
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chainId !== 138) return null
|
||||||
|
|
||||||
|
const cfg = ORACLES_CHAIN_138
|
||||||
|
const tryFeeds = [cfg.ethUsdAggregator, cfg.legacyEthUsdAggregator, cfg.ethUsdProxy]
|
||||||
|
|
||||||
|
for (const addr of tryFeeds) {
|
||||||
|
try {
|
||||||
|
const out = await readEthUsdFromFeed(provider, addr)
|
||||||
|
if (out.price > 0 && !Number.isNaN(out.price)) {
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// try next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* USD hint for a token on Chain 138 (dApp use). Returns null if unknown.
|
||||||
|
* Stablecoins (~$1): cUSDT, cUSDC, their V2 mints, and mirror USDT/USDC (D3 routing addresses).
|
||||||
|
* WETH9/WETH10: ETH/USD from on-chain feeds.
|
||||||
|
* @param {import('ethers').Provider} provider
|
||||||
|
* @param {number} chainId
|
||||||
|
* @param {string} tokenAddress - ERC-20 (checksummed or not)
|
||||||
|
* @returns {Promise<{ usd: number, source: string } | null>}
|
||||||
|
*/
|
||||||
|
export async function getAssetUsdPrice(provider, chainId, tokenAddress) {
|
||||||
|
if (!tokenAddress || chainId !== 138) return null
|
||||||
|
const a = tokenAddress.toLowerCase()
|
||||||
|
if (CHAIN138_STABLE_USD_1.has(a)) {
|
||||||
|
return { usd: 1, source: 'policy:GRU_USD_stable_1' }
|
||||||
|
}
|
||||||
|
if (WETH_VARIANTS.has(a)) {
|
||||||
|
const eth = await getEthUsdPrice(provider, 138)
|
||||||
|
if (!eth) return null
|
||||||
|
return { usd: eth.price, source: `eth_usd:${eth.feedAddress ?? 'feed'}` }
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
7
provider/types.d.ts
vendored
7
provider/types.d.ts
vendored
@@ -27,4 +27,11 @@ export interface OraclePriceResult {
|
|||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
decimals: number
|
decimals: number
|
||||||
roundId?: number
|
roundId?: number
|
||||||
|
/** Chain 138: which feed answered (mock, legacy aggregator, or proxy) */
|
||||||
|
feedAddress?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetUsdHint {
|
||||||
|
usd: number
|
||||||
|
source: string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user