Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions defi/src/cli/check_gecko_overlap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// HELPER FUNCTION LOOKS FOR DUPLIATE GECKO_ID BETWEEN DATA1/2/3/4 & parentProtocols
// OUPUTS TO JSON OBJECT- DOESNT MODIFY FILES

// node src/cli/check_gecko_overlap.js

const fs = require('fs');
const path = require('path');

function stripComments(ts) {
// Remove block comments /* ... */ and line comments // ...
return ts
.replace(/\/\*[\s\S]*?\*\//g, '')
.replace(/(^|[^:])\/\/.*$/gm, '$1');
}

function extractNameGeckoPairs(filePath) {
const raw = fs.readFileSync(filePath, 'utf8');
const content = stripComments(raw);
const pairs = [];
// Find all gecko_id occurrences and capture nearby name
const geckoRegex = /gecko_id\s*:\s*["']([^"']+)["']/g;
let match;
while ((match = geckoRegex.exec(content)) !== null) {
const geckoId = match[1];
// Extract the object block that encloses this gecko_id occurrence
const block = getEnclosingObjectBlock(content, match.index);
let name = '(unknown)';
if (block) {
const obj = content.slice(block.start, block.end);
const nameMatch = obj.match(/name\s*:\s*["']([^"']+)["']/);
if (nameMatch) name = nameMatch[1];
}
// compute line number
const before = content.slice(0, match.index);
const line = before.split('\n').length;
pairs.push({ name, gecko_id: geckoId, file: filePath, line });
}
return pairs;
}

// Attempts to find the start and end indices of the object literal enclosing a given index
function getEnclosingObjectBlock(text, idx) {
// Find '{' going backwards, tracking brace balance to avoid landing inside nested blocks
let start = -1;
let i = idx;
let braceDepth = 0;
// Move backward to find the matching '{'
for (i = idx; i >= 0; i--) {
const ch = text[i];
if (ch === '}') braceDepth++;
else if (ch === '{') {
if (braceDepth === 0) { start = i; break; }
braceDepth--;
}
}
if (start === -1) return null;
// From start, find the matching '}' going forward
let end = -1;
braceDepth = 0;
let inString = false;
let stringQuote = '';
for (i = start; i < text.length; i++) {
const ch = text[i];
const prev = i > 0 ? text[i - 1] : '';
if (inString) {
if (ch === stringQuote && prev !== '\\') inString = false;
continue;
}
if (ch === '"' || ch === '\'') {
inString = true; stringQuote = ch; continue;
}
if (ch === '{') braceDepth++;
else if (ch === '}') {
braceDepth--;
if (braceDepth === 0) { end = i + 1; break; }
}
}
if (end === -1) return null;
return { start, end };
}

const protocolsFiles = ['data1.ts', 'data2.ts', 'data3.ts', 'data4.ts'].map(f => path.join(__dirname, '../protocols', f));
const parentFile = path.join(__dirname, '../protocols/parentProtocols.ts');

const parentPairs = extractNameGeckoPairs(parentFile);
const protocolPairs = protocolsFiles.flatMap(extractNameGeckoPairs);

const parentByGecko = new Map();
for (const p of parentPairs) {
if (typeof p.gecko_id === 'string') parentByGecko.set(p.gecko_id, p.name);
}

// Build parent vs data overlaps
const parentDataOverlaps = [];
for (const p of protocolPairs) {
const parentName = parentByGecko.get(p.gecko_id);
if (parentName) {
const parentEntry = parentPairs.find(x => x.gecko_id === p.gecko_id);
parentDataOverlaps.push({
gecko_id: p.gecko_id,
protocol: { name: p.name, file: p.file, line: p.line },
parent: parentEntry ? { name: parentEntry.name, file: parentEntry.file, line: parentEntry.line } : { name: parentName }
});
}
}

// Build data vs data overlaps (duplicate gecko_ids across data files)
const byGecko = new Map();
for (const p of protocolPairs) {
if (!byGecko.has(p.gecko_id)) byGecko.set(p.gecko_id, []);
byGecko.get(p.gecko_id).push(p);
}
const dataDataOverlaps = [];
for (const [gid, entries] of byGecko.entries()) {
if (entries.length > 1) {
dataDataOverlaps.push({
gecko_id: gid,
protocols: entries.map(e => ({ name: e.name, file: e.file, line: e.line }))
});
}
}

// Optional: auto-fix by removing gecko_id from child protocol files (data1..4)
if (process.env.FIX_CHILD_GECKO === '1' && overlaps.length) {
const byFile = new Map();
for (const o of overlaps) {
const file = o.protocol.file;
if (!byFile.has(file)) byFile.set(file, new Set());
byFile.get(file).add(o.gecko_id);
}
for (const [file, idSet] of byFile.entries()) {
let content = fs.readFileSync(file, 'utf8');
for (const id of idSet) {
// remove patterns like: gecko_id: "id",
const patternWithComma = new RegExp(`\\s*gecko_id\\s*:\\s*["']${id}["']\\s*,`, 'g');
const patternLast = new RegExp(`,?\\s*gecko_id\\s*:\\s*["']${id}["']`, 'g');
content = content.replace(patternWithComma, '');
content = content.replace(patternLast, '');
}
fs.writeFileSync(file, content);
}
}

// Prepare compact output
function shortFile(filePath) {
const base = path.basename(filePath);
return base; // e.g., data1.ts
}

const compact = {
parent_data_overlaps: parentDataOverlaps.map(o => ({
gecko_id: o.gecko_id,
protocol: { name: o.protocol.name, file: shortFile(o.protocol.file) },
parent: { name: o.parent.name }
})),
data_data_overlaps: dataDataOverlaps.map(item => ({
gecko_id: item.gecko_id,
protocols: item.protocols.map(p => ({ name: p.name, file: shortFile(p.file) }))
}))
};

const outPath = path.join(__dirname, 'temp/gecko_id_overlaps.json');
fs.writeFileSync(outPath, JSON.stringify(compact, null, 2));
console.log(`Written overlaps to ${outPath}. parent_data_overlaps=${compact.parent_data_overlaps.length}, data_data_overlaps=${compact.data_data_overlaps.length}`);


74 changes: 38 additions & 36 deletions defi/src/cli/coingeckoUpdater.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import fs from 'fs';
import path from 'path';

const DATA_FILE = 'data';
const DATA_FILE = 'data4';

const COINGECKO_API_KEY = undefined;

interface Protocol {
id: string;
Expand Down Expand Up @@ -44,7 +46,7 @@ async function searchCoingecko(tokenSymbol: string): Promise<any[]> {
method: 'GET',
headers: {
accept: 'application/json',
'x-cg-pro-api-key': process.env.CG_API_KEY!
'x-cg-pro-api-key': COINGECKO_API_KEY || ''
}
});
const json = await response.json();
Expand All @@ -68,7 +70,7 @@ async function fetchCoinDetails(
method: 'GET',
headers: {
accept: 'application/json',
'x-cg-pro-api-key': process.env.CG_API_KEY!
'x-cg-pro-api-key': COINGECKO_API_KEY || ''
}
});
const data = await response.json();
Expand Down Expand Up @@ -119,37 +121,25 @@ async function fetchCoinDetails(
}

async function updateProtocolsData() {
const dataPath = path.join(__dirname, '../protocols', `${DATA_FILE}.ts`);
console.log('Working with file:', dataPath);

const fileContent = fs.readFileSync(dataPath, 'utf8');

// Import baseIconsUrl from the constants file
const { baseIconsUrl } = require('../constants');

const regex = new RegExp(`const ${DATA_FILE}: Protocol\\[\\] = (\\[[\\s\\S]*?\\]);`, 'm');
const match = fileContent.match(regex);
if (!match) {
console.error('Could not find protocols data in file');
if (!COINGECKO_API_KEY) {
console.error('Error: COINGECKO_API_KEY variable is required');
return;
}

console.log('Loading protocols data...');

let protocols: Protocol[];
try {
const code = `
const baseIconsUrl = "${baseIconsUrl}";
return ${match[1]};
`;
const fn = new Function(code);
protocols = fn();
console.log('Successfully parsed protocols:', protocols.length);
// Import the data directly
protocols = require(`../protocols/${DATA_FILE}`).default;
console.log('Successfully loaded protocols:', protocols.length);
} catch (error) {
console.error('Failed to parse protocols data:', error);
console.error('Failed to load protocols data:', error);
return;
}

// Track updates for final report
const updatedProtocols: { name: string; symbol: string; gecko_id: string }[] = [];
const updatedProtocols: { name: string; twitter: string; gecko_id: string }[] = [];

// Collect existing gecko_ids
protocols.forEach((protocol) => {
Expand Down Expand Up @@ -195,14 +185,13 @@ async function updateProtocolsData() {
protocol.url
);
if (coingeckoId) {
protocol.gecko_id = coingeckoId;
usedGeckoIds.add(coingeckoId);
updatedProtocols.push({
name: protocol.name,
symbol: protocol.symbol,
twitter: protocol.twitter,
gecko_id: coingeckoId
});
console.log(`Added gecko_id ${coingeckoId} to ${protocol.name}\n`);
console.log(`Found gecko_id ${coingeckoId} for ${protocol.name}\n`);
foundMatch = true;
break;
}
Expand All @@ -216,16 +205,29 @@ async function updateProtocolsData() {
await new Promise((resolve) => setTimeout(resolve, 250));
}

console.log('\n=== Update Summary ===');
if (updatedProtocols.length === 0) {
console.log('No protocols were updated with new gecko_ids');
} else {
console.log('The following protocols were updated with gecko_ids:');
updatedProtocols.forEach((p) => {
console.log(`- ${p.name} (${p.symbol}): ${p.gecko_id}`);
});
// Write results to a new file
const outputPath = path.join(__dirname, 'temp', `${DATA_FILE}_new_gecko_id.json`);
const outputData = {
timestamp: new Date().toISOString(),
totalFound: updatedProtocols.length,
protocols: updatedProtocols
};

try {
fs.writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
console.log(`\n=== Results Summary ===`);
console.log(`Found ${updatedProtocols.length} new gecko_ids`);
console.log(`Results written to: ${outputPath}`);
if (updatedProtocols.length > 0) {
console.log('\nFound gecko_ids:');
updatedProtocols.forEach((p) => {
console.log(`- ${p.name} (@${p.twitter}): ${p.gecko_id}`);
});
}
console.log('=====================\n');
} catch (error) {
console.error('Error writing output file:', error);
}
console.log('===================\n');
}

updateProtocolsData().catch(console.error);
Loading