chore: sync submodule state (parent ref update)
Made-with: Cursor
This commit is contained in:
129
provider/README.md
Normal file
129
provider/README.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# MetaMask Multi-Chain Provider
|
||||
|
||||
Connect **MetaMask** and other Web3 providers to **ChainID 138** (DeFi Oracle Meta Mainnet), **Ethereum Mainnet** (1), and **ALL Mainnet** (651940). Includes chain configs, combined token list, and oracle helpers so tokens and price feeds work across chains.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multi-chain**: Add and switch between Chain 138, Ethereum Mainnet, and ALL Mainnet in one flow.
|
||||
- **Token list**: All tokens from all three chains (Chain 138: WETH, WETH10, cUSDT, cUSDC, ETH/USD oracle; Mainnet: WETH, USDT, USDC, DAI, ETH/USD oracle; ALL Mainnet: USDC).
|
||||
- **Oracles**: Read ETH/USD from Chain 138 oracle and from Chainlink on Mainnet so dApps can display USD values.
|
||||
|
||||
## Installation
|
||||
|
||||
Use as a local module (no publish required):
|
||||
|
||||
```bash
|
||||
# From your app (e.g. metamask-integration/examples/react-example)
|
||||
npm install ethers # peer dependency for getEthUsdPrice
|
||||
```
|
||||
|
||||
Import from the provider path:
|
||||
|
||||
```javascript
|
||||
import { addChainsToWallet, switchChain, getEthUsdPrice, getTokensByChain } from '../provider/index.js'
|
||||
```
|
||||
|
||||
Or copy the `provider/` folder into your project and import from `./provider`.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Add both chains to MetaMask
|
||||
|
||||
```javascript
|
||||
import { addChainsToWallet } from './provider/index.js'
|
||||
|
||||
const ethereum = window.ethereum
|
||||
if (ethereum) {
|
||||
const result = await addChainsToWallet(ethereum)
|
||||
console.log('Added:', result.added, 'Skipped:', result.skipped, 'Errors:', result.errors)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Switch chain
|
||||
|
||||
```javascript
|
||||
import { switchChain, ensureChain } from './provider/index.js'
|
||||
|
||||
await switchChain(window.ethereum, 138) // DeFi Oracle Meta Mainnet
|
||||
await switchChain(window.ethereum, 1) // Ethereum Mainnet
|
||||
|
||||
// Or ensure current chain (switch if needed, add if missing)
|
||||
await ensureChain(window.ethereum, 138)
|
||||
```
|
||||
|
||||
### 3. Add a token to the wallet
|
||||
|
||||
```javascript
|
||||
import { addTokenToWallet, getToken, getTokensByChain } from './provider/index.js'
|
||||
|
||||
const tokens138 = getTokensByChain(138)
|
||||
for (const token of tokens138) {
|
||||
if (token.tags?.includes('oracle')) continue
|
||||
await addTokenToWallet(window.ethereum, token)
|
||||
}
|
||||
|
||||
// Or a single token by address
|
||||
const weth = getToken(138, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')
|
||||
if (weth) await addTokenToWallet(window.ethereum, weth)
|
||||
```
|
||||
|
||||
### 4. Read oracle price (ETH/USD)
|
||||
|
||||
Requires `ethers` v6. Works on both Chain 138 and Mainnet.
|
||||
|
||||
```javascript
|
||||
import { ethers } from 'ethers'
|
||||
import { getEthUsdPrice } from './provider/index.js'
|
||||
|
||||
const provider = new ethers.BrowserProvider(window.ethereum)
|
||||
const chainId = (await provider.getNetwork()).chainId
|
||||
|
||||
const result = await getEthUsdPrice(provider, Number(chainId))
|
||||
if (result) {
|
||||
console.log('ETH/USD:', result.price, 'Updated:', result.updatedAt)
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `CHAINS`, `CHAIN_138`, `CHAIN_MAINNET` | Chain params for `wallet_addEthereumChain` |
|
||||
| `getChainById(chainIdDecimal)`, `getChainByHex(chainIdHex)` | Look up chain config |
|
||||
| `addChainsToWallet(ethereum, options?)` | Add Chain 138 and/or Mainnet |
|
||||
| `switchChain(ethereum, chainIdDecimal)` | Switch to chain (adds if missing) |
|
||||
| `ensureChain(ethereum, chainIdDecimal)` | Switch to chain if not already on it |
|
||||
| `addTokenToWallet(ethereum, token)` | Add token via `wallet_watchAsset` |
|
||||
| `TOKEN_LIST`, `getTokensByChain(chainId)`, `getToken(chainId, address)` | Token list for both chains |
|
||||
| `TOKEN_LIST_URL` | URL/path to host the combined token list JSON |
|
||||
| `getEthUsdPrice(provider, chainId)` | Read ETH/USD from oracle (needs ethers) |
|
||||
| `ORACLES_CHAIN_138`, `ORACLES_MAINNET`, `getOracleConfig(chainId)` | Oracle addresses and config |
|
||||
|
||||
## Config files
|
||||
|
||||
- **Networks**: `docs/04-configuration/metamask/DUAL_CHAIN_NETWORKS.json` — both chain params.
|
||||
- **Token list**: `docs/04-configuration/metamask/DUAL_CHAIN_TOKEN_LIST.tokenlist.json` — tokens for Chain 138 and Mainnet.
|
||||
|
||||
Host the token list at a public URL and set your app’s token list URL to it so MetaMask can fetch the list (or use the inline `TOKEN_LIST` / `getTokensByChain` in your UI).
|
||||
|
||||
## Downloads
|
||||
|
||||
| Asset | In-repo path | Use |
|
||||
|-------|--------------|-----|
|
||||
| **Multi-chain networks** | `docs/04-configuration/metamask/DUAL_CHAIN_NETWORKS.json` | `wallet_addEthereumChain` params for Chain 138, Ethereum Mainnet, and ALL Mainnet. If bundled: `config/DUAL_CHAIN_NETWORKS.json`. |
|
||||
| **Dual-chain token list** | `docs/04-configuration/metamask/DUAL_CHAIN_TOKEN_LIST.tokenlist.json` | MetaMask token list URL; Uniswap token list format. If bundled: `config/DUAL_CHAIN_TOKEN_LIST.tokenlist.json`. **Hosted:** `https://explorer.d-bis.org/api/config/token-list` (explorer API). `TOKEN_LIST_URL` in code points to this by default. |
|
||||
| **Feature parity and recommendations** | `docs/04-configuration/metamask/METAMASK_CHAIN138_FEATURE_PARITY_ANALYSIS.md` | Plugin/snap requirements, gaps, and build/integration options. |
|
||||
| **Oracle and pricing** | `docs/04-configuration/metamask/ORACLE_PRICE_FEED_SETUP.md`, `WETH_ORACLE_QUICK_REFERENCE.md` | Oracle addresses and Oracle Publisher setup. |
|
||||
| **Token-aggregation REST API** | `smom-dbis-138/services/token-aggregation/docs/REST_API_REFERENCE.md` | Tokens, pools, prices, volume, OHLCV for Chain 138 and ALL Mainnet (discovery without CMC/CoinGecko). |
|
||||
| **Optional next steps** | `docs/04-configuration/metamask/METAMASK_CHAIN138_FEATURE_PARITY_ANALYSIS.md` §7, `SNAP_IMPLEMENTATION_ROADMAP.md` | Custom Snap roadmap, CoinGecko submission, Consensys outreach. |
|
||||
|
||||
## Oracles
|
||||
|
||||
- **Chain 138**: ETH/USD at `0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6` (8 decimals). Keep the Oracle Publisher service running so the feed stays updated.
|
||||
- **Ethereum Mainnet**: Chainlink ETH/USD at `0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419`.
|
||||
|
||||
MetaMask does not read these oracles by default; use `getEthUsdPrice(provider, chainId)` in your dApp to show USD values.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
176
provider/chains.js
Normal file
176
provider/chains.js
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Multi-chain network params for MetaMask and Web3 providers.
|
||||
* 13 chains: 138, 1, 651940, 25, 56, 100, 137, 10, 42161, 8453, 43114, 42220, 1111.
|
||||
* Use with wallet_addEthereumChain and wallet_switchEthereumChain.
|
||||
*
|
||||
* @typedef {Object} ChainParams
|
||||
* @property {string} chainId
|
||||
* @property {number} chainIdDecimal
|
||||
* @property {string} chainName
|
||||
* @property {{ name: string, symbol: string, decimals: number }} nativeCurrency
|
||||
* @property {string[]} rpcUrls
|
||||
* @property {string[]} blockExplorerUrls
|
||||
* @property {string[]} [iconUrls]
|
||||
*/
|
||||
|
||||
/** @type {ChainParams[]} */
|
||||
export const CHAINS = [
|
||||
{
|
||||
chainId: '0x8a',
|
||||
chainIdDecimal: 138,
|
||||
chainName: 'DeFi Oracle Meta Mainnet',
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: [
|
||||
'https://rpc-http-pub.d-bis.org',
|
||||
'https://rpc.d-bis.org',
|
||||
'https://rpc2.d-bis.org',
|
||||
'https://rpc.defi-oracle.io',
|
||||
],
|
||||
blockExplorerUrls: ['https://explorer.d-bis.org'],
|
||||
iconUrls: [
|
||||
'https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png',
|
||||
],
|
||||
},
|
||||
{
|
||||
chainId: '0x1',
|
||||
chainIdDecimal: 1,
|
||||
chainName: 'Ethereum Mainnet',
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: [
|
||||
'https://eth.llamarpc.com',
|
||||
'https://rpc.ankr.com/eth',
|
||||
'https://ethereum.publicnode.com',
|
||||
'https://1rpc.io/eth',
|
||||
],
|
||||
blockExplorerUrls: ['https://etherscan.io'],
|
||||
iconUrls: [
|
||||
'https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png',
|
||||
],
|
||||
},
|
||||
{
|
||||
chainId: '0x9f2c4',
|
||||
chainIdDecimal: 651940,
|
||||
chainName: 'ALL Mainnet',
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: ['https://mainnet-rpc.alltra.global'],
|
||||
blockExplorerUrls: ['https://alltra.global'],
|
||||
iconUrls: [
|
||||
'https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png',
|
||||
],
|
||||
},
|
||||
{
|
||||
chainId: '0x19',
|
||||
chainIdDecimal: 25,
|
||||
chainName: 'Cronos Mainnet',
|
||||
nativeCurrency: { name: 'CRO', symbol: 'CRO', decimals: 18 },
|
||||
rpcUrls: ['https://evm.cronos.org', 'https://cronos-rpc.publicnode.com'],
|
||||
blockExplorerUrls: ['https://cronos.org/explorer'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0x38',
|
||||
chainIdDecimal: 56,
|
||||
chainName: 'BNB Smart Chain',
|
||||
nativeCurrency: { name: 'BNB', symbol: 'BNB', decimals: 18 },
|
||||
rpcUrls: ['https://bsc-dataseed.binance.org', 'https://bsc-dataseed1.defibit.io'],
|
||||
blockExplorerUrls: ['https://bscscan.com'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0x64',
|
||||
chainIdDecimal: 100,
|
||||
chainName: 'Gnosis Chain',
|
||||
nativeCurrency: { name: 'xDAI', symbol: 'xDAI', decimals: 18 },
|
||||
rpcUrls: ['https://rpc.gnosischain.com', 'https://gnosis-rpc.publicnode.com'],
|
||||
blockExplorerUrls: ['https://gnosisscan.io'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0x89',
|
||||
chainIdDecimal: 137,
|
||||
chainName: 'Polygon',
|
||||
nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
|
||||
rpcUrls: ['https://polygon-rpc.com', 'https://polygon.llamarpc.com'],
|
||||
blockExplorerUrls: ['https://polygonscan.com'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0xa',
|
||||
chainIdDecimal: 10,
|
||||
chainName: 'Optimism',
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: ['https://mainnet.optimism.io', 'https://optimism.llamarpc.com'],
|
||||
blockExplorerUrls: ['https://optimistic.etherscan.io'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0xa4b1',
|
||||
chainIdDecimal: 42161,
|
||||
chainName: 'Arbitrum One',
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: ['https://arb1.arbitrum.io/rpc', 'https://arbitrum.llamarpc.com'],
|
||||
blockExplorerUrls: ['https://arbiscan.io'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0x2105',
|
||||
chainIdDecimal: 8453,
|
||||
chainName: 'Base',
|
||||
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
||||
rpcUrls: ['https://mainnet.base.org', 'https://base.llamarpc.com'],
|
||||
blockExplorerUrls: ['https://basescan.org'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0xa86a',
|
||||
chainIdDecimal: 43114,
|
||||
chainName: 'Avalanche C-Chain',
|
||||
nativeCurrency: { name: 'AVAX', symbol: 'AVAX', decimals: 18 },
|
||||
rpcUrls: ['https://api.avax.network/ext/bc/C/rpc'],
|
||||
blockExplorerUrls: ['https://snowtrace.io'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0xa4ec',
|
||||
chainIdDecimal: 42220,
|
||||
chainName: 'Celo',
|
||||
nativeCurrency: { name: 'CELO', symbol: 'CELO', decimals: 18 },
|
||||
rpcUrls: ['https://forno.celo.org'],
|
||||
blockExplorerUrls: ['https://celoscan.io'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
{
|
||||
chainId: '0x457',
|
||||
chainIdDecimal: 1111,
|
||||
chainName: 'Wemix',
|
||||
nativeCurrency: { name: 'WEMIX', symbol: 'WEMIX', decimals: 18 },
|
||||
rpcUrls: ['https://api.wemix.com'],
|
||||
blockExplorerUrls: ['https://scan.wemix.com'],
|
||||
iconUrls: ['https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png'],
|
||||
},
|
||||
]
|
||||
|
||||
/** Chain 138 only (for wallet_addEthereumChain single chain) */
|
||||
export const CHAIN_138 = CHAINS[0]
|
||||
|
||||
/** Ethereum Mainnet only */
|
||||
export const CHAIN_MAINNET = CHAINS[1]
|
||||
|
||||
/** ALL Mainnet only (651940) */
|
||||
export const CHAIN_ALL_MAINNET = CHAINS[2]
|
||||
|
||||
/**
|
||||
* @param {number} chainIdDecimal - 138, 1, 651940, 25, 56, 100, 137, 10, 42161, 8453, 43114, 42220, 1111
|
||||
* @returns {ChainParams | undefined}
|
||||
*/
|
||||
export function getChainById(chainIdDecimal) {
|
||||
return CHAINS.find((c) => c.chainIdDecimal === chainIdDecimal)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} chainIdHex - e.g. '0x8a', '0x1', '0x9f2c4', '0x19', etc.
|
||||
* @returns {ChainParams | undefined}
|
||||
*/
|
||||
export function getChainByHex(chainIdHex) {
|
||||
return CHAINS.find((c) => c.chainId === chainIdHex)
|
||||
}
|
||||
19
provider/config/DUAL_CHAIN_NETWORKS.json
Normal file
19
provider/config/DUAL_CHAIN_NETWORKS.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "MetaMask Multi-Chain Networks (13 chains)",
|
||||
"version": {"major": 1, "minor": 2, "patch": 0},
|
||||
"chains": [
|
||||
{"chainId":"0x8a","chainIdDecimal":138,"chainName":"DeFi Oracle Meta Mainnet","rpcUrls":["https://rpc-http-pub.d-bis.org","https://rpc.d-bis.org","https://rpc2.d-bis.org","https://rpc.defi-oracle.io"],"nativeCurrency":{"name":"Ether","symbol":"ETH","decimals":18},"blockExplorerUrls":["https://explorer.d-bis.org"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0x1","chainIdDecimal":1,"chainName":"Ethereum Mainnet","rpcUrls":["https://eth.llamarpc.com","https://rpc.ankr.com/eth","https://ethereum.publicnode.com","https://1rpc.io/eth"],"nativeCurrency":{"name":"Ether","symbol":"ETH","decimals":18},"blockExplorerUrls":["https://etherscan.io"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0x9f2c4","chainIdDecimal":651940,"chainName":"ALL Mainnet","rpcUrls":["https://mainnet-rpc.alltra.global"],"nativeCurrency":{"name":"Ether","symbol":"ETH","decimals":18},"blockExplorerUrls":["https://alltra.global"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0x19","chainIdDecimal":25,"chainName":"Cronos Mainnet","rpcUrls":["https://evm.cronos.org","https://cronos-rpc.publicnode.com"],"nativeCurrency":{"name":"CRO","symbol":"CRO","decimals":18},"blockExplorerUrls":["https://cronos.org/explorer"],"iconUrls":["https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong"]},
|
||||
{"chainId":"0x38","chainIdDecimal":56,"chainName":"BNB Smart Chain","rpcUrls":["https://bsc-dataseed.binance.org","https://bsc-dataseed1.defibit.io","https://bsc-dataseed1.ninicoin.io"],"nativeCurrency":{"name":"BNB","symbol":"BNB","decimals":18},"blockExplorerUrls":["https://bscscan.com"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0x64","chainIdDecimal":100,"chainName":"Gnosis Chain","rpcUrls":["https://rpc.gnosischain.com","https://gnosis-rpc.publicnode.com","https://1rpc.io/gnosis"],"nativeCurrency":{"name":"xDAI","symbol":"xDAI","decimals":18},"blockExplorerUrls":["https://gnosisscan.io"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0x89","chainIdDecimal":137,"chainName":"Polygon","rpcUrls":["https://polygon-rpc.com","https://polygon.llamarpc.com","https://polygon-bor-rpc.publicnode.com"],"nativeCurrency":{"name":"MATIC","symbol":"MATIC","decimals":18},"blockExplorerUrls":["https://polygonscan.com"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0xa","chainIdDecimal":10,"chainName":"Optimism","rpcUrls":["https://mainnet.optimism.io","https://optimism.llamarpc.com","https://optimism-rpc.publicnode.com"],"nativeCurrency":{"name":"Ether","symbol":"ETH","decimals":18},"blockExplorerUrls":["https://optimistic.etherscan.io"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0xa4b1","chainIdDecimal":42161,"chainName":"Arbitrum One","rpcUrls":["https://arb1.arbitrum.io/rpc","https://arbitrum.llamarpc.com","https://arbitrum-one-rpc.publicnode.com"],"nativeCurrency":{"name":"Ether","symbol":"ETH","decimals":18},"blockExplorerUrls":["https://arbiscan.io"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0x2105","chainIdDecimal":8453,"chainName":"Base","rpcUrls":["https://mainnet.base.org","https://base.llamarpc.com","https://base-rpc.publicnode.com"],"nativeCurrency":{"name":"Ether","symbol":"ETH","decimals":18},"blockExplorerUrls":["https://basescan.org"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0xa86a","chainIdDecimal":43114,"chainName":"Avalanche C-Chain","rpcUrls":["https://api.avax.network/ext/bc/C/rpc","https://avalanche-c-chain-rpc.publicnode.com","https://1rpc.io/avax/c"],"nativeCurrency":{"name":"AVAX","symbol":"AVAX","decimals":18},"blockExplorerUrls":["https://snowtrace.io"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0xa4ec","chainIdDecimal":42220,"chainName":"Celo","rpcUrls":["https://forno.celo.org","https://celo-mainnet-rpc.publicnode.com","https://1rpc.io/celo"],"nativeCurrency":{"name":"CELO","symbol":"CELO","decimals":18},"blockExplorerUrls":["https://celoscan.io"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]},
|
||||
{"chainId":"0x457","chainIdDecimal":1111,"chainName":"Wemix","rpcUrls":["https://api.wemix.com","https://wemix-mainnet-rpc.publicnode.com"],"nativeCurrency":{"name":"WEMIX","symbol":"WEMIX","decimals":18},"blockExplorerUrls":["https://scan.wemix.com"],"iconUrls":["https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png"]}
|
||||
]
|
||||
}
|
||||
859
provider/config/DUAL_CHAIN_TOKEN_LIST.tokenlist.json
Normal file
859
provider/config/DUAL_CHAIN_TOKEN_LIST.tokenlist.json
Normal file
@@ -0,0 +1,859 @@
|
||||
{
|
||||
"name": "Multi-Chain Token List (13 chains, 138 base)",
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 3,
|
||||
"patch": 0
|
||||
},
|
||||
"timestamp": "2026-02-28T00:00:00.000Z",
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tokens": [
|
||||
{
|
||||
"chainId": 138,
|
||||
"address": "0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6",
|
||||
"name": "ETH/USD Price Feed",
|
||||
"symbol": "ETH-USD",
|
||||
"decimals": 8,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmPZuycjyJEe2otREuQ5HirvPJ8X6Yc6MBtwz1VhdD79pY",
|
||||
"tags": [
|
||||
"oracle",
|
||||
"price-feed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 138,
|
||||
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 138,
|
||||
"address": "0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F",
|
||||
"name": "Wrapped Ether v10",
|
||||
"symbol": "WETH10",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmanDFPHxnbKd6SSNzzXHf9GbpL9dLXSphxDZSPPYE6ds4",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 138,
|
||||
"address": "0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 138,
|
||||
"address": "0x93E66202A11B1772E55407B32B44e5Cd8eda7f22",
|
||||
"name": "Compliant Tether USD",
|
||||
"symbol": "cUSDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi",
|
||||
"compliant"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 138,
|
||||
"address": "0xf22258f57794CC8E06237084b353Ab30fFfa640b",
|
||||
"name": "Compliant USD Coin",
|
||||
"symbol": "cUSDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmNPq4D5JXzurmi9jAhogVMzhAQRk1PZ1r9H3qQUV9gjDm",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi",
|
||||
"compliant"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 1,
|
||||
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 1,
|
||||
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 1,
|
||||
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 1,
|
||||
"address": "0x514910771AF9Ca656af840dff83E8264EcF986CA",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 1,
|
||||
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 1,
|
||||
"address": "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419",
|
||||
"name": "ETH/USD Price Feed",
|
||||
"symbol": "ETH-USD",
|
||||
"decimals": 8,
|
||||
"logoURI": "https://raw.githubusercontent.com/ethereum/ethereum.org/main/static/images/eth-diamond-black.png",
|
||||
"tags": [
|
||||
"oracle",
|
||||
"price-feed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 651940,
|
||||
"address": "0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 651940,
|
||||
"address": "0x015B1897Ed5279930bC2Be46F661894d219292A6",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0xc21223249CA28397B4B6541dfFaEcC539BfF0c59",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0x66e4286603D22FF153A6547700f37C7Eae42F8E2",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0x99B3511A2d315A497C8112C1fdd8D508d4B1E506",
|
||||
"name": "Wrapped Ether (WETH9)",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0x3304b747E565a97ec8AC220b0B6A1f6ffDB837e6",
|
||||
"name": "Wrapped Ether v10",
|
||||
"symbol": "WETH10",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmanDFPHxnbKd6SSNzzXHf9GbpL9dLXSphxDZSPPYE6ds4",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0x8c80A01F461f297Df7F9DA3A4f740D7297C8Ac85",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0x948690147D2e50ffe50C5d38C14125aD6a9FA036",
|
||||
"name": "USD W Token",
|
||||
"symbol": "USDW",
|
||||
"decimals": 2,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmNPq4D5JXzurmi9jAhogVMzhAQRk1PZ1r9H3qQUV9gjDm",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"iso4217w"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0x58a8D8F78F1B65c06dAd7542eC46b299629A60dd",
|
||||
"name": "EUR W Token",
|
||||
"symbol": "EURW",
|
||||
"decimals": 2,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmPh16PY241zNtePyeK7ep1uf1RcARV2ynGAuRU8U7sSqS",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"iso4217w"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0xFb4B6Cc81211F7d886950158294A44C312abCA29",
|
||||
"name": "GBP W Token",
|
||||
"symbol": "GBPW",
|
||||
"decimals": 2,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmT2nJ6WyhYBCsYJ6NfS1BPAqiGKkCEuMxiC8ye93Co1hF",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"iso4217w"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0xf9f5D0ACD71C76F9476F10B3F3d3E201F0883C68",
|
||||
"name": "AUD W Token",
|
||||
"symbol": "AUDW",
|
||||
"decimals": 2,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qmb9JmuD9ehaQtTLBBZmAoiAbvE53e3FMjkEty8rvbPf9K",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"iso4217w"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0xeE17bB0322383fecCA2784fbE2d4CD7d02b1905B",
|
||||
"name": "JPY W Token",
|
||||
"symbol": "JPYW",
|
||||
"decimals": 2,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qmb9JmuD9ehaQtTLBBZmAoiAbvE53e3FMjkEty8rvbPf9K",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"iso4217w"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0xc9750828124D4c10e7a6f4B655cA8487bD3842EB",
|
||||
"name": "CHF W Token",
|
||||
"symbol": "CHFW",
|
||||
"decimals": 2,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qmb9JmuD9ehaQtTLBBZmAoiAbvE53e3FMjkEty8rvbPf9K",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"iso4217w"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 25,
|
||||
"address": "0x328Cd365Bb35524297E68ED28c6fF2C9557d1363",
|
||||
"name": "CAD W Token",
|
||||
"symbol": "CADW",
|
||||
"decimals": 2,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qmb9JmuD9ehaQtTLBBZmAoiAbvE53e3FMjkEty8rvbPf9K",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"iso4217w"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 56,
|
||||
"address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 56,
|
||||
"address": "0x55d398326f99059fF775485246999027B3197955",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 56,
|
||||
"address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 56,
|
||||
"address": "0x404460C6A5EdE2D891e8297795264fDe62ADBB75",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 56,
|
||||
"address": "0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 100,
|
||||
"address": "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 100,
|
||||
"address": "0x4ECaBa5870353805a9F068101A40E0f32ed605C6",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 100,
|
||||
"address": "0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 100,
|
||||
"address": "0xE2e73A1c69ecF83F464EFCE6A5be353a37cA09b2",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 100,
|
||||
"address": "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 137,
|
||||
"address": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c1369",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 137,
|
||||
"address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 137,
|
||||
"address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 137,
|
||||
"address": "0xb0897686c545045aFc77CF20eC7A532E3120E0F1",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 137,
|
||||
"address": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 10,
|
||||
"address": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 10,
|
||||
"address": "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 10,
|
||||
"address": "0x4200000000000000000000000000000000000006",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 10,
|
||||
"address": "0x350a791Bfc2C21F9Ed5d10980Dad2e2638ffa7f6",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 10,
|
||||
"address": "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42161,
|
||||
"address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42161,
|
||||
"address": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42161,
|
||||
"address": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42161,
|
||||
"address": "0xf97f4df75117a78c1A5a0DBb814Af92458539FB4",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42161,
|
||||
"address": "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 8453,
|
||||
"address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 8453,
|
||||
"address": "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 8453,
|
||||
"address": "0x4200000000000000000000000000000000000006",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 8453,
|
||||
"address": "0x88Fb150BDc53A65fe94Dea0c9BA0a6dAf8C6e196",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 8453,
|
||||
"address": "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 43114,
|
||||
"address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 43114,
|
||||
"address": "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 43114,
|
||||
"address": "0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 43114,
|
||||
"address": "0x5947BB275c521040051D82396192181b413227A3",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 43114,
|
||||
"address": "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70",
|
||||
"name": "Dai Stablecoin",
|
||||
"symbol": "DAI",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42220,
|
||||
"address": "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42220,
|
||||
"address": "0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42220,
|
||||
"address": "0x122013fd7dF1C6F636a5bb8f03108E876548b455",
|
||||
"name": "Wrapped Ether",
|
||||
"symbol": "WETH",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong",
|
||||
"tags": [
|
||||
"defi",
|
||||
"wrapped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 42220,
|
||||
"address": "0xd07294e6E917e07dfDcee882dd1e2565085C2ae0",
|
||||
"name": "Chainlink Token",
|
||||
"symbol": "LINK",
|
||||
"decimals": 18,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmenWcmfNGfssz4HXvrRV912eZDiKqLTt6z2brRYuTGz9A",
|
||||
"tags": [
|
||||
"defi",
|
||||
"oracle",
|
||||
"ccip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 1111,
|
||||
"address": "0xE3F5a90F9cb311505cd691a46596599aA1A0AD7D",
|
||||
"name": "USD Coin",
|
||||
"symbol": "USDC",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": 1111,
|
||||
"address": "0xA649325Aa7C5093d12D6F98EB4378deAe68CE23F",
|
||||
"name": "Tether USD",
|
||||
"symbol": "USDT",
|
||||
"decimals": 6,
|
||||
"logoURI": "https://ipfs.io/ipfs/QmRfhPs9DcyFPpGjKwF6CCoVDWUHSxkQR34n9NK7JSbPCP",
|
||||
"tags": [
|
||||
"stablecoin",
|
||||
"defi"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tags": {
|
||||
"defi": {
|
||||
"name": "DeFi",
|
||||
"description": "Decentralized Finance tokens"
|
||||
},
|
||||
"wrapped": {
|
||||
"name": "Wrapped",
|
||||
"description": "Wrapped tokens representing native assets"
|
||||
},
|
||||
"oracle": {
|
||||
"name": "Oracle",
|
||||
"description": "Oracle price feed contracts"
|
||||
},
|
||||
"price-feed": {
|
||||
"name": "Price Feed",
|
||||
"description": "Price feed oracle contracts"
|
||||
},
|
||||
"stablecoin": {
|
||||
"name": "Stablecoin",
|
||||
"description": "Stable value tokens pegged to fiat"
|
||||
},
|
||||
"compliant": {
|
||||
"name": "Compliant",
|
||||
"description": "Regulatory compliant tokens"
|
||||
},
|
||||
"iso4217w": {
|
||||
"name": "ISO4217W",
|
||||
"description": "ISO 4217 compliant wrapped fiat tokens"
|
||||
}
|
||||
}
|
||||
}
|
||||
36
provider/index.js
Normal file
36
provider/index.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* MetaMask Dual-Chain Provider
|
||||
* Connect MetaMask and other Web3 providers to ChainID 138 (DeFi Oracle Meta Mainnet)
|
||||
* and Ethereum Mainnet (1). Includes chain configs, token list, and oracle helpers.
|
||||
*
|
||||
* Usage:
|
||||
* import { addChainsToWallet, switchChain, getEthUsdPrice, getTokensByChain } from './provider'
|
||||
* await addChainsToWallet(window.ethereum)
|
||||
* const price = await getEthUsdPrice(provider, 138)
|
||||
*/
|
||||
|
||||
export {
|
||||
CHAINS,
|
||||
CHAIN_138,
|
||||
CHAIN_MAINNET,
|
||||
CHAIN_ALL_MAINNET,
|
||||
getChainById,
|
||||
getChainByHex,
|
||||
} from './chains.js'
|
||||
|
||||
export {
|
||||
ORACLES_CHAIN_138,
|
||||
ORACLES_MAINNET,
|
||||
ORACLE_ABI,
|
||||
getEthUsdPrice,
|
||||
getOracleConfig,
|
||||
} from './oracles.js'
|
||||
|
||||
export {
|
||||
TOKEN_LIST,
|
||||
TOKEN_LIST_URL,
|
||||
getTokensByChain,
|
||||
getToken,
|
||||
} from './tokens.js'
|
||||
|
||||
export { addChainsToWallet, switchChain, addTokenToWallet, ensureChain } from './wallet.js'
|
||||
65
provider/oracles.js
Normal file
65
provider/oracles.js
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Oracle addresses and helpers for Chain 138 and Ethereum Mainnet.
|
||||
* Use these to read price feeds so MetaMask and dApps can display USD values.
|
||||
*/
|
||||
|
||||
/** Chain 138: ETH/USD proxy and aggregator */
|
||||
export const ORACLES_CHAIN_138 = {
|
||||
chainId: 138,
|
||||
ethUsdProxy: '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6',
|
||||
aggregator: '0x99b3511a2d315a497c8112c1fdd8d508d4b1e506',
|
||||
decimals: 8,
|
||||
rpcUrl: 'https://rpc-http-pub.d-bis.org',
|
||||
}
|
||||
|
||||
/** Ethereum Mainnet: Chainlink ETH/USD */
|
||||
export const ORACLES_MAINNET = {
|
||||
chainId: 1,
|
||||
ethUsdProxy: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419',
|
||||
decimals: 8,
|
||||
rpcUrl: 'https://eth.llamarpc.com',
|
||||
}
|
||||
|
||||
/** Minimal ABI for latestRoundData (Chainlink-compatible) */
|
||||
export const ORACLE_ABI = [
|
||||
'function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)',
|
||||
'function decimals() external view returns (uint8)',
|
||||
]
|
||||
|
||||
/**
|
||||
* Get ETH/USD price from the appropriate oracle for the given chain.
|
||||
* @param {import('ethers').Provider} provider - ethers v6 JsonRpcProvider or BrowserProvider
|
||||
* @param {number} chainId - 138 or 1
|
||||
* @returns {Promise<{ price: number, updatedAt: Date, decimals: number } | null>}
|
||||
*/
|
||||
export async function getEthUsdPrice(provider, chainId) {
|
||||
const oracleConfig = chainId === 138 ? ORACLES_CHAIN_138 : chainId === 1 ? ORACLES_MAINNET : null
|
||||
if (!oracleConfig) return null
|
||||
|
||||
try {
|
||||
const { ethers } = await import('ethers')
|
||||
const contract = new ethers.Contract(oracleConfig.ethUsdProxy, ORACLE_ABI, provider)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get oracle config for a chain (for custom contract usage).
|
||||
* @param {number} chainId - 138 or 1
|
||||
*/
|
||||
export function getOracleConfig(chainId) {
|
||||
if (chainId === 138) return ORACLES_CHAIN_138
|
||||
if (chainId === 1) return ORACLES_MAINNET
|
||||
return null
|
||||
}
|
||||
19
provider/package.json
Normal file
19
provider/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@proxmox/metamask-dual-chain-provider",
|
||||
"version": "1.0.0",
|
||||
"description": "MetaMask provider for ChainID 138 and Ethereum Mainnet",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"files": ["*.js", "*.d.ts", "config/"],
|
||||
"keywords": ["metamask", "web3", "chainid-138", "ethereum-mainnet", "oracle", "token-list"],
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"ethers": ">=6.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ethers": { "optional": true }
|
||||
},
|
||||
"scripts": {
|
||||
"test:integration": "node test-integration.mjs"
|
||||
}
|
||||
}
|
||||
98
provider/test-integration.mjs
Normal file
98
provider/test-integration.mjs
Normal file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Provider integration test (Node).
|
||||
* Tests chain configs, token list, and exports without window.ethereum.
|
||||
* Run: node test-integration.mjs
|
||||
*/
|
||||
|
||||
const assert = (ok, msg) => {
|
||||
if (!ok) throw new Error(msg)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Provider integration test (Node)\n')
|
||||
|
||||
const { CHAINS, CHAIN_138, CHAIN_MAINNET, CHAIN_ALL_MAINNET, getChainById, getChainByHex } = await import('./chains.js')
|
||||
const { getTokensByChain, getToken, TOKEN_LIST_URL } = await import('./tokens.js')
|
||||
const { addChainsToWallet, switchChain, addTokenToWallet, ensureChain } = await import('./wallet.js')
|
||||
const { ORACLES_CHAIN_138, ORACLES_MAINNET, getOracleConfig } = await import('./oracles.js')
|
||||
|
||||
let passed = 0
|
||||
let failed = 0
|
||||
|
||||
// Chains
|
||||
try {
|
||||
assert(Array.isArray(CHAINS) && CHAINS.length >= 3, 'CHAINS has at least 3 entries')
|
||||
assert(CHAIN_138 && CHAIN_138.chainIdDecimal === 138, 'CHAIN_138 has chainIdDecimal 138')
|
||||
assert(CHAIN_MAINNET && CHAIN_MAINNET.chainIdDecimal === 1, 'CHAIN_MAINNET has chainIdDecimal 1')
|
||||
assert(CHAIN_ALL_MAINNET && CHAIN_ALL_MAINNET.chainIdDecimal === 651940, 'CHAIN_ALL_MAINNET has chainIdDecimal 651940')
|
||||
assert(getChainById(138) === CHAIN_138, 'getChainById(138) returns CHAIN_138')
|
||||
assert(getChainById(1) === CHAIN_MAINNET, 'getChainById(1) returns CHAIN_MAINNET')
|
||||
assert(getChainByHex('0x8a') === CHAIN_138, 'getChainByHex(0x8a) returns CHAIN_138')
|
||||
console.log(' ✓ Chains (CHAINS, CHAIN_138, CHAIN_MAINNET, CHAIN_ALL_MAINNET, getChainById, getChainByHex)')
|
||||
passed++
|
||||
} catch (e) {
|
||||
console.log(' ✗ Chains:', e.message)
|
||||
failed++
|
||||
}
|
||||
|
||||
// Tokens
|
||||
try {
|
||||
const t138 = getTokensByChain(138)
|
||||
const t1 = getTokensByChain(1)
|
||||
const t651940 = getTokensByChain(651940)
|
||||
assert(Array.isArray(t138) && t138.length > 0, 'getTokensByChain(138) returns non-empty array')
|
||||
assert(Array.isArray(t1) && t1.length > 0, 'getTokensByChain(1) returns non-empty array')
|
||||
assert(Array.isArray(t651940), 'getTokensByChain(651940) returns array')
|
||||
assert(typeof TOKEN_LIST_URL === 'string' && TOKEN_LIST_URL.length > 0, 'TOKEN_LIST_URL is set')
|
||||
const weth138 = getToken(138, '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2')
|
||||
assert(weth138 && weth138.symbol, 'getToken(138, WETH9) returns token with symbol')
|
||||
console.log(' ✓ Tokens (getTokensByChain, getToken, TOKEN_LIST_URL)')
|
||||
passed++
|
||||
} catch (e) {
|
||||
console.log(' ✗ Tokens:', e.message)
|
||||
failed++
|
||||
}
|
||||
|
||||
// Wallet (exports only; no window.ethereum)
|
||||
try {
|
||||
assert(typeof addChainsToWallet === 'function', 'addChainsToWallet is function')
|
||||
assert(typeof switchChain === 'function', 'switchChain is function')
|
||||
assert(typeof addTokenToWallet === 'function', 'addTokenToWallet is function')
|
||||
assert(typeof ensureChain === 'function', 'ensureChain is function')
|
||||
console.log(' ✓ Wallet exports (addChainsToWallet, switchChain, addTokenToWallet, ensureChain)')
|
||||
passed++
|
||||
} catch (e) {
|
||||
console.log(' ✗ Wallet:', e.message)
|
||||
failed++
|
||||
}
|
||||
|
||||
// Oracles
|
||||
try {
|
||||
assert(ORACLES_CHAIN_138 && ORACLES_CHAIN_138.ethUsdProxy, 'ORACLES_CHAIN_138 has ethUsdProxy')
|
||||
assert(ORACLES_MAINNET && ORACLES_MAINNET.ethUsdProxy, 'ORACLES_MAINNET has ethUsdProxy')
|
||||
const cfg138 = getOracleConfig(138)
|
||||
const cfg1 = getOracleConfig(1)
|
||||
assert(cfg138 && cfg138.ethUsdProxy, 'getOracleConfig(138) has ethUsdProxy')
|
||||
assert(cfg1 && cfg1.ethUsdProxy, 'getOracleConfig(1) has ethUsdProxy')
|
||||
console.log(' ✓ Oracles (ORACLES_CHAIN_138, ORACLES_MAINNET, getOracleConfig)')
|
||||
passed++
|
||||
} catch (e) {
|
||||
console.log(' ✗ Oracles:', e.message)
|
||||
failed++
|
||||
}
|
||||
|
||||
console.log('')
|
||||
if (failed === 0) {
|
||||
console.log(`Result: ${passed} passed, 0 failed`)
|
||||
process.exit(0)
|
||||
} else {
|
||||
console.log(`Result: ${passed} passed, ${failed} failed`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
46
provider/tokens.js
Normal file
46
provider/tokens.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Multi-chain token list (Chain 138 + Ethereum Mainnet + ALL Mainnet).
|
||||
* Use with wallet_watchAsset and for displaying tokens in dApps.
|
||||
*/
|
||||
|
||||
/** Inline token list for Chain 138, Mainnet, and ALL Mainnet. Matches DUAL_CHAIN_TOKEN_LIST.tokenlist.json */
|
||||
export const TOKEN_LIST = [
|
||||
// Chain 138
|
||||
{ chainId: 138, address: '0x3304b747e565a97ec8ac220b0b6a1f6ffdb837e6', name: 'ETH/USD Price Feed', symbol: 'ETH-USD', decimals: 8, tags: ['oracle', 'price-feed'] },
|
||||
{ chainId: 138, address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', name: 'Wrapped Ether', symbol: 'WETH', decimals: 18, tags: ['defi', 'wrapped'] },
|
||||
{ chainId: 138, address: '0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f', name: 'Wrapped Ether v10', symbol: 'WETH', decimals: 18, tags: ['defi', 'wrapped'] },
|
||||
{ chainId: 138, address: '0x93E66202A11B1772E55407B32B44e5Cd8eda7f22', name: 'Compliant Tether USD', symbol: 'cUSDT', decimals: 6, tags: ['stablecoin', 'defi', 'compliant'] },
|
||||
{ chainId: 138, address: '0xf22258f57794CC8E06237084b353Ab30fFfa640b', name: 'Compliant USD Coin', symbol: 'cUSDC', decimals: 6, tags: ['stablecoin', 'defi', 'compliant'] },
|
||||
// Ethereum Mainnet
|
||||
{ chainId: 1, address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', name: 'Wrapped Ether', symbol: 'WETH', decimals: 18, tags: ['defi', 'wrapped'] },
|
||||
{ chainId: 1, address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', name: 'Tether USD', symbol: 'USDT', decimals: 6, tags: ['stablecoin', 'defi'] },
|
||||
{ chainId: 1, address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', name: 'USD Coin', symbol: 'USDC', decimals: 6, tags: ['stablecoin', 'defi'] },
|
||||
{ chainId: 1, address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', name: 'Dai Stablecoin', symbol: 'DAI', decimals: 18, tags: ['stablecoin', 'defi'] },
|
||||
{ chainId: 1, address: '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419', name: 'ETH/USD Price Feed', symbol: 'ETH-USD', decimals: 8, tags: ['oracle', 'price-feed'] },
|
||||
// ALL Mainnet (651940)
|
||||
{ chainId: 651940, address: '0xa95EeD79f84E6A0151eaEb9d441F9Ffd50e8e881', name: 'USD Coin', symbol: 'USDC', decimals: 6, tags: ['stablecoin', 'defi'] },
|
||||
]
|
||||
|
||||
/**
|
||||
* @param {number} chainId - 138, 1, or 651940
|
||||
* @returns {Array<{ chainId: number, address: string, name: string, symbol: string, decimals: number, tags?: string[] }>}
|
||||
*/
|
||||
export function getTokensByChain(chainId) {
|
||||
return TOKEN_LIST.filter((t) => t.chainId === chainId)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} chainId
|
||||
* @param {string} address - token contract address (checksummed or lowercase)
|
||||
* @returns {typeof TOKEN_LIST[0] | undefined}
|
||||
*/
|
||||
export function getToken(chainId, address) {
|
||||
const addr = address.toLowerCase()
|
||||
return TOKEN_LIST.find((t) => t.chainId === chainId && t.address.toLowerCase() === addr)
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the combined token list JSON (hosted by the explorer API).
|
||||
* Override in your app if you host the list elsewhere.
|
||||
*/
|
||||
export const TOKEN_LIST_URL = 'https://explorer.d-bis.org/api/config/token-list'
|
||||
30
provider/types.d.ts
vendored
Normal file
30
provider/types.d.ts
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Types for MetaMask dual-chain provider (Chain 138 + Ethereum Mainnet).
|
||||
*/
|
||||
|
||||
export interface ChainParams {
|
||||
chainId: string
|
||||
chainIdDecimal: number
|
||||
chainName: string
|
||||
nativeCurrency: { name: string; symbol: string; decimals: number }
|
||||
rpcUrls: string[]
|
||||
blockExplorerUrls: string[]
|
||||
iconUrls?: string[]
|
||||
}
|
||||
|
||||
export interface TokenInfo {
|
||||
chainId: number
|
||||
address: string
|
||||
name: string
|
||||
symbol: string
|
||||
decimals: number
|
||||
logoURI?: string
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
export interface OraclePriceResult {
|
||||
price: number
|
||||
updatedAt: Date
|
||||
decimals: number
|
||||
roundId?: number
|
||||
}
|
||||
104
provider/wallet.js
Normal file
104
provider/wallet.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Wallet helpers for MetaMask and EIP-1193 providers.
|
||||
* Add chains (138 + Mainnet), switch chain, add tokens.
|
||||
*/
|
||||
|
||||
import { CHAINS, getChainByHex, getChainById } from './chains.js'
|
||||
import { getTokensByChain, getToken } from './tokens.js'
|
||||
|
||||
/**
|
||||
* Add both Chain 138 and Ethereum Mainnet to the wallet (if not already added).
|
||||
* @param {import('ethers').Eip1193Provider} ethereum - window.ethereum or other EIP-1193 provider
|
||||
* @param {{ chains?: number[] }} options - optional: { chains: [138, 1] } to add only specific chains
|
||||
* @returns {Promise<{ added: number[], skipped: number[], errors: { chainId: number, error: Error }[] }>}
|
||||
*/
|
||||
export async function addChainsToWallet(ethereum, options = {}) {
|
||||
const chainIds = options.chains ?? [138, 1]
|
||||
const toAdd = CHAINS.filter((c) => chainIds.includes(c.chainIdDecimal))
|
||||
const result = { added: [], skipped: [], errors: [] }
|
||||
|
||||
for (const chain of toAdd) {
|
||||
try {
|
||||
await ethereum.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [{ ...chain }],
|
||||
})
|
||||
result.added.push(chain.chainIdDecimal)
|
||||
} catch (err) {
|
||||
if (err.code === 4902) {
|
||||
result.added.push(chain.chainIdDecimal)
|
||||
} else if (err.code === -32603 && /already exists/i.test(String(err.message))) {
|
||||
result.skipped.push(chain.chainIdDecimal)
|
||||
} else {
|
||||
result.errors.push({ chainId: chain.chainIdDecimal, error: err })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the wallet to the given chain. If chain is not added, adds it first (wallet_addEthereumChain).
|
||||
* @param {import('ethers').Eip1193Provider} ethereum - window.ethereum
|
||||
* @param {number} chainIdDecimal - 138 or 1
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function switchChain(ethereum, chainIdDecimal) {
|
||||
const chain = getChainById(chainIdDecimal)
|
||||
if (!chain) throw new Error(`Unknown chain: ${chainIdDecimal}`)
|
||||
|
||||
try {
|
||||
await ethereum.request({
|
||||
method: 'wallet_switchEthereumChain',
|
||||
params: [{ chainId: chain.chainId }],
|
||||
})
|
||||
} catch (err) {
|
||||
if (err.code === 4902) {
|
||||
await ethereum.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [chain],
|
||||
})
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a token to the wallet (wallet_watchAsset).
|
||||
* @param {import('ethers').Eip1193Provider} ethereum - window.ethereum
|
||||
* @param {{ chainId: number, address: string, symbol: string, decimals: number, name?: string }} token - token info (use getToken(chainId, address) for known tokens)
|
||||
* @returns {Promise<boolean>} - true if user approved
|
||||
*/
|
||||
export async function addTokenToWallet(ethereum, token) {
|
||||
const result = await ethereum.request({
|
||||
method: 'wallet_watchAsset',
|
||||
params: {
|
||||
type: 'ERC20',
|
||||
options: {
|
||||
address: token.address,
|
||||
symbol: token.symbol,
|
||||
decimals: token.decimals,
|
||||
...(token.name && { name: token.name }),
|
||||
},
|
||||
},
|
||||
})
|
||||
return !!result
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the wallet is on the given chain; if not, switch (and add if needed).
|
||||
* @param {import('ethers').Eip1193Provider} ethereum - window.ethereum
|
||||
* @param {number} chainIdDecimal - 138 or 1
|
||||
* @returns {Promise<boolean>} - true if already on chain or switched successfully
|
||||
*/
|
||||
export async function ensureChain(ethereum, chainIdDecimal) {
|
||||
const hex = await ethereum.request({ method: 'eth_chainId', params: [] })
|
||||
const current = parseInt(hex, 16)
|
||||
if (current === chainIdDecimal) return true
|
||||
await switchChain(ethereum, chainIdDecimal)
|
||||
return true
|
||||
}
|
||||
|
||||
export { getTokensByChain, getToken }
|
||||
Reference in New Issue
Block a user