chore: update submodule references and documentation
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled
- Marked submodules ai-mcp-pmm-controller, explorer-monorepo, and smom-dbis-138 as dirty to reflect recent changes. - Updated documentation to clarify operator script usage, including dotenv loading and task execution instructions. - Enhanced the README and various index files to provide clearer navigation and task completion guidance. Made-with: Cursor
This commit is contained in:
@@ -1 +1 @@
|
||||
{"name":"DBIS Chain 138 Token List","version":{"major":1,"minor":5,"patch":0},"timestamp":"2026-02-28T00:00:00.000Z","keywords":["dbis","chain138","defi oracle meta"],"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","pricefeed"]},{"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":138,"address":"0x8085961F9cF02b4d800A3c6d386D31da4B34266a","name":"Euro Coin (Compliant)","symbol":"cEURC","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]}],"tags":{"defi":{"name":"DeFi","description":"Decentralized Finance tokens"},"wrapped":{"name":"Wrapped","description":"Wrapped tokens representing native assets"},"oracle":{"name":"Oracle","description":"Oracle price feed tokens"},"pricefeed":{"name":"Price Feed","description":"Price feed oracle contracts"},"stablecoin":{"name":"Stablecoin","description":"Stable value tokens pegged to fiat currencies"},"compliant":{"name":"Compliant","description":"Regulatory compliant tokens with compliance features"},"ccip":{"name":"CCIP","description":"Cross Chain Interoperability Protocol tokens"}}}
|
||||
{"name":"DBIS Chain 138 Token List","version":{"major":1,"minor":6,"patch":0},"timestamp":"2026-02-28T00:00:00.000Z","keywords":["dbis","chain138","defi oracle meta"],"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","pricefeed"]},{"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":138,"address":"0x8085961F9cF02b4d800A3c6d386D31da4B34266a","name":"Euro Coin (Compliant)","symbol":"cEURC","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]},{"chainId":138,"address":"0xdf4b71c61E5912712C1Bdd451416B9aC26949d72","name":"Tether EUR (Compliant)","symbol":"cEURT","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]},{"chainId":138,"address":"0x003960f16D9d34F2e98d62723B6721Fb92074aD2","name":"Pound Sterling (Compliant)","symbol":"cGBPC","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]},{"chainId":138,"address":"0x350f54e4D23795f86A9c03988c7135357CCaD97c","name":"Tether GBP (Compliant)","symbol":"cGBPT","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]},{"chainId":138,"address":"0xD51482e567c03899eecE3CAe8a058161FD56069D","name":"Australian Dollar (Compliant)","symbol":"cAUDC","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]},{"chainId":138,"address":"0xEe269e1226a334182aace90056EE4ee5Cc8A6770","name":"Japanese Yen (Compliant)","symbol":"cJPYC","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]},{"chainId":138,"address":"0x873990849DDa5117d7C644f0aF24370797C03885","name":"Swiss Franc (Compliant)","symbol":"cCHFC","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]},{"chainId":138,"address":"0x54dBd40cF05e15906A2C21f600937e96787f5679","name":"Canadian Dollar (Compliant)","symbol":"cCADC","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["stablecoin","defi","compliant"]},{"chainId":138,"address":"0x290E52a8819A4fbD0714E517225429aA2B70EC6b","name":"Gold (Compliant)","symbol":"cXAUC","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["defi","compliant"]},{"chainId":138,"address":"0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E","name":"Tether XAU (Compliant)","symbol":"cXAUT","decimals":6,"logoURI":"https://ipfs.io/ipfs/Qma3FKtLce9MjgJgWbtyCxBiPjJ6xi8jGWUSKNS5Jc2ong","tags":["defi","compliant"]}],"tags":{"defi":{"name":"DeFi","description":"Decentralized Finance tokens"},"wrapped":{"name":"Wrapped","description":"Wrapped tokens representing native assets"},"oracle":{"name":"Oracle","description":"Oracle price feed tokens"},"pricefeed":{"name":"Price Feed","description":"Price feed oracle contracts"},"stablecoin":{"name":"Stablecoin","description":"Stable value tokens pegged to fiat currencies"},"compliant":{"name":"Compliant","description":"Regulatory compliant tokens with compliance features"},"ccip":{"name":"CCIP","description":"Cross Chain Interoperability Protocol tokens"}}}
|
||||
170
token-lists/scripts/diff-blockscout-vs-tokenlist.js
Normal file
170
token-lists/scripts/diff-blockscout-vs-tokenlist.js
Normal file
@@ -0,0 +1,170 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Full diff: Blockscout /api/v2/tokens vs curated token list (Chain 138).
|
||||
*
|
||||
* Outputs:
|
||||
* - missing_in_blockscout: in tokenlist but not in Blockscout response
|
||||
* - missing_in_tokenlist: in Blockscout but not in tokenlist
|
||||
* - metadata_mismatches: same address, different name/symbol/decimals (or null/0)
|
||||
* - source-of-truth recommendation per field
|
||||
*
|
||||
* Usage:
|
||||
* node diff-blockscout-vs-tokenlist.js
|
||||
* node diff-blockscout-vs-tokenlist.js --url "https://explorer.d-bis.org/api/v2/tokens"
|
||||
* node diff-blockscout-vs-tokenlist.js --file /path/to/blockscout-tokens.json
|
||||
*
|
||||
* Curated list: token-lists/lists/dbis-138.tokenlist.json (Chain 138 tokens).
|
||||
* ETH-USD (oracle) is not an ERC-20 supply token; it is expected to be missing from Blockscout.
|
||||
*/
|
||||
|
||||
import { readFileSync } from 'fs';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const CHAIN_ID = 138;
|
||||
const TOKENLIST_PATH = resolve(__dirname, '../lists/dbis-138.tokenlist.json');
|
||||
|
||||
function normAddr(addr) {
|
||||
return (addr || '').toLowerCase();
|
||||
}
|
||||
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
let url = null;
|
||||
let file = null;
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--url' && args[i + 1]) {
|
||||
url = args[i + 1];
|
||||
i++;
|
||||
} else if (args[i] === '--file' && args[i + 1]) {
|
||||
file = args[i + 1];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return { url, file };
|
||||
}
|
||||
|
||||
async function fetchAllBlockscoutTokens(baseUrl) {
|
||||
const items = [];
|
||||
let next = null;
|
||||
const base = baseUrl.replace(/\?.*$/, '');
|
||||
while (true) {
|
||||
const qs = next ? new URLSearchParams({ page_size: 100, ...next }) : new URLSearchParams({ page: 1, page_size: 100 });
|
||||
const res = await fetch(`${base}?${qs}`);
|
||||
if (!res.ok) throw new Error(`Blockscout ${res.status}`);
|
||||
const data = await res.json();
|
||||
const list = data.items ?? data.data ?? (Array.isArray(data) ? data : []);
|
||||
items.push(...list);
|
||||
next = data.next_page_params ?? null;
|
||||
if (!next) break;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
function loadTokenList() {
|
||||
const raw = readFileSync(TOKENLIST_PATH, 'utf8');
|
||||
const data = JSON.parse(raw);
|
||||
const tokens = (data.tokens || []).filter((t) => t.chainId === CHAIN_ID);
|
||||
return tokens.map((t) => ({
|
||||
address: normAddr(t.address),
|
||||
name: t.name ?? null,
|
||||
symbol: t.symbol ?? null,
|
||||
decimals: t.decimals != null ? Number(t.decimals) : null,
|
||||
logoURI: t.logoURI ?? null,
|
||||
}));
|
||||
}
|
||||
|
||||
function loadBlockscoutFromFile(path) {
|
||||
const raw = readFileSync(path, 'utf8');
|
||||
const data = JSON.parse(raw);
|
||||
const list = data.items ?? data.data ?? (Array.isArray(data) ? data : []);
|
||||
return list.map((t) => ({
|
||||
address: normAddr(t.address ?? t.hash),
|
||||
name: t.name ?? null,
|
||||
symbol: t.symbol ?? null,
|
||||
decimals: t.decimals != null && t.decimals !== '' ? Number(t.decimals) : null,
|
||||
}));
|
||||
}
|
||||
|
||||
function runDiff(tokenlist, blockscout) {
|
||||
const byAddr = (arr) => Object.fromEntries(arr.map((t) => [t.address, t]));
|
||||
const listMap = byAddr(tokenlist);
|
||||
const scoutMap = byAddr(blockscout);
|
||||
|
||||
const missing_in_blockscout = tokenlist
|
||||
.filter((t) => !scoutMap[t.address])
|
||||
.map((t) => ({ address: t.address, symbol: t.symbol, name: t.name, note: t.symbol === 'ETH-USD' ? 'Oracle; not ERC-20 supply token' : null }));
|
||||
|
||||
const missing_in_tokenlist = blockscout
|
||||
.filter((t) => !listMap[t.address])
|
||||
.map((t) => ({ address: t.address, symbol: t.symbol, name: t.name, decimals: t.decimals }));
|
||||
|
||||
const metadata_mismatches = [];
|
||||
for (const addr of Object.keys(listMap)) {
|
||||
const list = listMap[addr];
|
||||
const scout = scoutMap[addr];
|
||||
if (!scout) continue;
|
||||
const mismatches = [];
|
||||
if (list.name !== scout.name && (scout.name != null || list.name != null)) mismatches.push({ field: 'name', tokenlist: list.name, blockscout: scout.name });
|
||||
if (list.symbol !== scout.symbol && (scout.symbol != null || list.symbol != null)) mismatches.push({ field: 'symbol', tokenlist: list.symbol, blockscout: scout.symbol });
|
||||
if (list.decimals !== scout.decimals && (scout.decimals != null || list.decimals != null)) mismatches.push({ field: 'decimals', tokenlist: list.decimals, blockscout: scout.decimals });
|
||||
if (mismatches.length) metadata_mismatches.push({ address: addr, symbol: list.symbol ?? scout.symbol, mismatches });
|
||||
}
|
||||
|
||||
return { missing_in_blockscout, missing_in_tokenlist, metadata_mismatches };
|
||||
}
|
||||
|
||||
function sourceOfTruthRecommendation(diff) {
|
||||
return {
|
||||
address: 'Token list (dbis-138.tokenlist.json) and CONTRACT_ADDRESSES_REFERENCE; Blockscout is on-chain index.',
|
||||
symbol: 'Token list; use Explorer UI override only when Blockscout returns null (e.g. WETH9).',
|
||||
name: 'Token list; same as symbol.',
|
||||
decimals: 'Token list; use override when Blockscout returns 0 or null.',
|
||||
logo: 'Token list logoURI.',
|
||||
};
|
||||
}
|
||||
|
||||
function main() {
|
||||
const { url, file } = parseArgs();
|
||||
const baseUrl = url || 'https://explorer.d-bis.org/api/v2/tokens';
|
||||
|
||||
(async () => {
|
||||
let blockscout;
|
||||
if (file) {
|
||||
blockscout = loadBlockscoutFromFile(file);
|
||||
console.error(`Loaded ${blockscout.length} tokens from file: ${file}`);
|
||||
} else {
|
||||
try {
|
||||
blockscout = await fetchAllBlockscoutTokens(baseUrl);
|
||||
console.error(`Fetched ${blockscout.length} tokens from ${baseUrl}`);
|
||||
} catch (e) {
|
||||
console.error('Fetch failed:', e.message);
|
||||
console.error('Use --file path/to/blockscout-tokens.json with a saved snapshot.');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const tokenlist = loadTokenList();
|
||||
console.error(`Loaded ${tokenlist.length} Chain ${CHAIN_ID} tokens from ${TOKENLIST_PATH}`);
|
||||
|
||||
const diff = runDiff(tokenlist, blockscout);
|
||||
const recommendation = sourceOfTruthRecommendation(diff);
|
||||
|
||||
const out = {
|
||||
chainId: CHAIN_ID,
|
||||
tokenlist_path: TOKENLIST_PATH,
|
||||
blockscout_source: file || baseUrl,
|
||||
missing_in_blockscout: diff.missing_in_blockscout,
|
||||
missing_in_tokenlist: diff.missing_in_tokenlist,
|
||||
metadata_mismatches: diff.metadata_mismatches,
|
||||
source_of_truth: recommendation,
|
||||
};
|
||||
console.log(JSON.stringify(out, null, 2));
|
||||
})().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user