chore: pnpm lockfile, info-defi-oracle-138 app, token-lists, OMNL discovery output

- Refresh pnpm-lock.yaml / workspace after prior merge
- Add Chain 138 info hub SPA (info-defi-oracle-138)
- Token list and validation script tweaks; path_b report; Hyperledger proxmox install notes
- HYBX implementation roadmap and routing graph data model

Note: transaction-composer is a nested git repo — convert to submodule before tracking.
Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-31 22:32:15 -07:00
parent 7ac74f432b
commit 6c5fdcfd62
45 changed files with 3601 additions and 408 deletions

View File

@@ -4,16 +4,57 @@
* Validates that all logoURI URLs are accessible and return image content
*/
import { readFileSync } from 'fs';
import { readFileSync, existsSync, statSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const PROJECT_ROOT = resolve(__dirname, '../..');
const MAX_LOGO_SIZE = 500 * 1024; // 500KB
const IMAGE_MIME_TYPES = ['image/png', 'image/jpeg', 'image/jpg', 'image/svg+xml', 'image/webp', 'image/gif'];
function inferContentTypeFromPath(filePath) {
if (filePath.endsWith('.svg')) return 'image/svg+xml';
if (filePath.endsWith('.png')) return 'image/png';
if (filePath.endsWith('.jpg') || filePath.endsWith('.jpeg')) return 'image/jpeg';
if (filePath.endsWith('.webp')) return 'image/webp';
if (filePath.endsWith('.gif')) return 'image/gif';
return null;
}
function resolveLocalRepoAsset(logoURI) {
try {
const url = new URL(logoURI);
if (url.protocol !== 'https:') return null;
if (url.hostname === 'raw.githubusercontent.com') {
const segments = url.pathname.split('/').filter(Boolean);
if (segments.length < 4) return null;
const relativePath = segments.slice(3).join('/');
const candidate = resolve(PROJECT_ROOT, relativePath);
return existsSync(candidate) ? candidate : null;
}
if (url.hostname === 'gitea.d-bis.org') {
const marker = '/raw/branch/';
const markerIndex = url.pathname.indexOf(marker);
if (markerIndex === -1) return null;
const afterMarker = url.pathname.slice(markerIndex + marker.length);
const pathSegments = afterMarker.split('/').filter(Boolean);
if (pathSegments.length < 2) return null;
const relativePath = pathSegments.slice(1).join('/');
const candidate = resolve(PROJECT_ROOT, relativePath);
return existsSync(candidate) ? candidate : null;
}
} catch {
return null;
}
return null;
}
async function validateLogo(logoURI, tokenInfo) {
const issues = [];
@@ -24,6 +65,24 @@ async function validateLogo(logoURI, tokenInfo) {
// For HTTPS URLs, validate accessibility
if (logoURI.startsWith('https://')) {
const localAsset = resolveLocalRepoAsset(logoURI);
if (localAsset) {
try {
const size = statSync(localAsset).size;
const contentType = inferContentTypeFromPath(localAsset);
if (!contentType || !IMAGE_MIME_TYPES.includes(contentType)) {
issues.push(`Local repo asset has unsupported extension: ${localAsset}`);
}
if (size > MAX_LOGO_SIZE) {
issues.push(`Local repo asset too large: ${(size / 1024).toFixed(2)}KB (max ${MAX_LOGO_SIZE / 1024}KB)`);
}
return issues;
} catch (error) {
issues.push(`Failed to stat local repo asset: ${error.message}`);
return issues;
}
}
try {
const response = await fetch(logoURI, { method: 'HEAD' });
@@ -132,4 +191,3 @@ validateLogos(filePath).then(exitCode => {
console.error('Unexpected error:', error);
process.exit(1);
});

View File

@@ -38,7 +38,7 @@ async function getSchema() {
return tokenLists.schema;
}
} catch (error) {
console.log(' @uniswap/token-lists package not available, fetching schema from URL...\n');
console.log(' Using fallback token list schema source\n');
}
// Fallback: fetch schema from Uniswap
@@ -69,10 +69,38 @@ function isChecksummed(address) {
function enhancedValidation(tokenList) {
const errors = [];
const warnings = [];
const infos = [];
const seenAddresses = new Set();
const seenSymbols = new Map(); // chainId -> Set of symbols
const seenSymbols = new Map(); // chainId -> Map<symbol, token[]>
let detectedChainId = null;
function isAllowedGruVersionDuplicate(existingToken, nextToken) {
if (!existingToken?.extensions || !nextToken?.extensions) return false;
const existingVersion = existingToken.extensions.gruVersion;
const nextVersion = nextToken.extensions.gruVersion;
const existingCurrencyCode = existingToken.extensions.currencyCode;
const nextCurrencyCode = nextToken.extensions.currencyCode;
if (typeof existingVersion !== 'string' || typeof nextVersion !== 'string') {
return false;
}
if (existingVersion === nextVersion) {
return false;
}
if (typeof existingCurrencyCode !== 'string' || typeof nextCurrencyCode !== 'string') {
return false;
}
if (existingCurrencyCode !== nextCurrencyCode) {
return false;
}
return true;
}
// Required fields
if (!tokenList.name || typeof tokenList.name !== 'string') {
errors.push('Missing or invalid "name" field');
@@ -154,13 +182,27 @@ function enhancedValidation(tokenList) {
// Symbol uniqueness per chainId
const chainId = token.chainId || 0;
if (!seenSymbols.has(chainId)) {
seenSymbols.set(chainId, new Set());
seenSymbols.set(chainId, new Map());
}
const symbolSet = seenSymbols.get(chainId);
if (symbolSet.has(token.symbol.toUpperCase())) {
warnings.push(`${prefix}: Duplicate symbol "${token.symbol}" on chainId ${chainId}`);
const symbolMap = seenSymbols.get(chainId);
const symbolKey = token.symbol.toUpperCase();
const existingTokens = symbolMap.get(symbolKey) || [];
if (existingTokens.length > 0) {
const duplicateAllowed = existingTokens.every(existingToken =>
isAllowedGruVersionDuplicate(existingToken, token)
);
if (duplicateAllowed) {
infos.push(
`${prefix}: Allowed staged GRU duplicate symbol "${token.symbol}" on chainId ${chainId} ` +
`(${existingTokens.map(existingToken => existingToken.extensions?.gruVersion).join(', ')} -> ${token.extensions?.gruVersion})`
);
} else {
warnings.push(`${prefix}: Duplicate symbol "${token.symbol}" on chainId ${chainId}`);
}
}
symbolSet.add(token.symbol.toUpperCase());
existingTokens.push(token);
symbolMap.set(symbolKey, existingTokens);
}
if (typeof token.decimals !== 'number' || token.decimals < 0 || token.decimals > 255) {
@@ -181,7 +223,7 @@ function enhancedValidation(tokenList) {
}
});
return { errors, warnings, valid: errors.length === 0 };
return { errors, warnings, infos, valid: errors.length === 0 };
}
async function validateTokenList(filePath) {
@@ -226,6 +268,7 @@ async function validateTokenList(filePath) {
validationResult = {
errors: [...schemaErrors, ...enhanced.errors],
warnings: enhanced.warnings,
infos: enhanced.infos,
valid: false
};
}
@@ -275,6 +318,14 @@ async function validateTokenList(filePath) {
});
console.log('');
}
if (validationResult.infos.length > 0) {
console.log(' Notes:');
validationResult.infos.forEach(info => {
console.log(` - ${info}`);
});
console.log('');
}
process.exit(0);
} else {
@@ -295,6 +346,14 @@ async function validateTokenList(filePath) {
});
console.log('');
}
if (validationResult.infos.length > 0) {
console.log('Notes:');
validationResult.infos.forEach(info => {
console.log(` ${info}`);
});
console.log('');
}
process.exit(1);
}
@@ -318,4 +377,3 @@ validateTokenList(filePath).catch(error => {
console.error('Unexpected error:', error);
process.exit(1);
});