Skip to content

Commit 639276c

Browse files
committed
Merge branch 'fix/tokens-balance-issues'
2 parents 9efd3d4 + 9524c57 commit 639276c

File tree

5 files changed

+147
-77
lines changed

5 files changed

+147
-77
lines changed

dextools-api/package-lock.json

Lines changed: 19 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dextools-api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"compression": "^1.7.5",
2121
"cors": "^2.8.5",
2222
"dotenv": "^16.0.3",
23-
"express": "^4.21.1",
23+
"express": "^4.21.2",
2424
"helmet": "^8.0.0",
2525
"jsonwebtoken": "^9.0.2",
2626
"luxon": "^3.2.1",

dextools-cron/helpers/pact.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const makePactCallWithFallback = async (chainId, pactCode, gasLimit, urlIndex =
3131
.local(makeCMD(chainId, pactCode, gasLimit), chainwebUrl)
3232
.then((data) => {
3333
if (data.result && data.result.status) {
34-
console.log(`Success: ${chainwebUrl}`);
34+
// console.log(`Success: ${chainwebUrl}`);
3535
return data;
3636
}
3737
throw new Error(`failed to fetch from ${chainwebUrl}`);

dextools-cron/src/accountsBalanceUpdate.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ const updateAccountsBalance = async () => {
7676
}, {});
7777

7878
const chainPromises = Array.from({ length: KADENA_CHAINS_COUNT }, async (_, chainId) => {
79-
const tokens = await getStoredKadenaTokensByChain(chainId);
80-
79+
let tokens = await getStoredKadenaTokensByChain(chainId);
80+
const isValidPactString = (str) => /^[a-zA-Z0-9._-]+$/.test(str);
81+
tokens = tokens.filter((token) => isValidPactString(token));
8182
const getTokenAlias = (tokenName) => tokenName.replace(/\./g, '');
8283

8384
const pactCode = `
@@ -135,6 +136,7 @@ const updateAccountsBalance = async () => {
135136
} catch (err) {
136137
console.error(`ERROR on chain ${chainId}`);
137138
console.log(err);
139+
console.log(err.stack);
138140
}
139141
});
140142

dextools-cron/src/allKadenaTokenUpdate.js

Lines changed: 122 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,85 +8,72 @@ const {
88
} = require('../helpers');
99

1010
const COLLECTION_NAME = 'kadenaTokens';
11-
1211
const dbName = process.env.MONGO_DB || 'dextools';
1312

1413
let mongoClient = null;
1514
let db = null;
1615

17-
const updateKadenaTokens = async (chainId) => {
16+
const getTokensBatch = async (chainId) => {
1817
const pactCode = `(let
1918
((all-tokens
2019
(lambda (contract:object)
2120
(let*
2221
((module-name (at 'name contract))
23-
(interfaces (if (contains 'interfaces contract) (at 'interfaces contract) (if (contains 'interface contract) (at 'interface contract) [])))
22+
(interfaces (if (contains 'interfaces contract)
23+
(at 'interfaces contract)
24+
(if (contains 'interface contract)
25+
(at 'interface contract)
26+
[])))
2427
(is-implementing-fungible-v2 (contains "fungible-v2" interfaces))
2528
)
26-
(if is-implementing-fungible-v2 module-name "")
29+
(if is-implementing-fungible-v2 module-name "")
2730
)
2831
)
2932
)
3033
)
3134
(filter (!= "") (map (all-tokens) (map (describe-module) (list-modules))))
3235
)`;
33-
const invalidChainTokens = [];
3436

35-
try {
36-
const { db } = await mongoConnect();
37-
const collection = db.collection(COLLECTION_NAME);
37+
const res = await makePactCall(chainId.toString(), pactCode);
3838

39-
const res = await makePactCall(chainId.toString(), pactCode);
40-
if (res?.result?.data?.length > 0) {
41-
console.log(`[CHAIN ${chainId}] FOUND ${res?.result?.data?.length} tokens`);
42-
const storedTokens = await getStoredKadenaTokensByChain(chainId);
43-
console.log(`[CHAIN ${chainId}] ${storedTokens?.length} tokens already saved`);
44-
const difference = res?.result?.data?.filter((t) => !storedTokens.includes(t));
45-
46-
if (difference.length) {
47-
console.log(`[CHAIN ${chainId}] FOUNDED ${difference.length} new tokens: ${difference.join(', ')}`);
48-
const validChainTokens = storedTokens;
49-
let tokenCount = 1;
50-
51-
for (const token of difference) {
52-
try {
53-
const isTokenWorking = await makePactCall(chainId.toString(), `(${token}.get-balance "k:alice")`);
54-
await sleep(500);
55-
if (isTokenWorking?.result?.status === 'success' || isTokenWorking?.result?.error?.message?.includes('row not found')) {
56-
validChainTokens.push(token);
57-
} else {
58-
console.error(`[CHAIN ${chainId}] TOKEN ${token} IS NOT VALID`);
59-
invalidChainTokens.push(token);
60-
}
61-
} catch (err) {
62-
console.error(`FETCH ERROR ${token}:`, err);
63-
}
64-
65-
tokenCount += 1;
66-
}
39+
if (res?.result?.status === 'success' && Array.isArray(res?.result?.data)) {
40+
return res.result.data;
41+
} else {
42+
throw new Error(`Batch approach failed on chain ${chainId}: ${res?.result?.error?.message}`);
43+
}
44+
};
6745

68-
console.log(`[CHAIN ${chainId}] invalid TOKENS: ${invalidChainTokens.length}/${res?.result?.data?.length}`);
69-
70-
// Upsert in MongoDB
71-
await collection.updateOne(
72-
{ chainId: chainId.toString() },
73-
{
74-
$set: {
75-
tokens: stringify(validChainTokens),
76-
lastUpdate: new Date().toISOString(),
77-
},
78-
},
79-
{ upsert: true }
80-
);
81-
console.log('UPLOADING TOKENS ON CHAIN ' + chainId);
46+
const getTokensFallback = async (chainId) => {
47+
console.log(`[CHAIN ${chainId}] Fallback: describing each module...`);
48+
const listRes = await makePactCall(chainId.toString(), '(list-modules)');
49+
if (listRes?.result?.status !== 'success' || !Array.isArray(listRes.result?.data)) {
50+
console.error(`[CHAIN ${chainId}] Fallback list-modules failed`);
51+
return [];
52+
}
53+
54+
const modules = listRes.result.data;
55+
console.log(`[CHAIN ${chainId}] Fallback found ${modules.length} modules`);
56+
const fungibleTokens = [];
57+
let i = 1;
58+
for (const mod of modules) {
59+
try {
60+
const descRes = await makePactCall(chainId.toString(), `(describe-module "${mod}")`);
61+
if (descRes?.result?.status === 'success') {
62+
const data = descRes.result?.data;
63+
const interfaces = data?.interfaces || data?.interface || [];
64+
if (interfaces.includes('fungible-v2')) {
65+
console.log(`[CHAIN ${chainId}] ${i}/${modules.length} Found fungible token: ${data.name}`);
66+
fungibleTokens.push(data.name);
67+
} else {
68+
console.log(`[CHAIN ${chainId}] ${i}/${modules.length} Skipping non-fungible token: ${data.name}`);
69+
}
8270
}
83-
} else {
84-
console.error(`NO TOKENS FOUNDED ON CHAIN ${chainId} `, res);
71+
} catch (err) {
72+
console.error(`[CHAIN ${chainId}] Fallback error describing ${mod}`, err);
8573
}
86-
} catch (err) {
87-
console.error(`ERROR FETCHING TOKENS ON CHAIN ${chainId}`);
88-
console.log(err);
74+
i++;
8975
}
76+
return fungibleTokens;
9077
};
9178

9279
const getStoredKadenaTokensByChain = async (chainId) => {
@@ -102,6 +89,82 @@ const getStoredKadenaTokensByChain = async (chainId) => {
10289
}
10390
};
10491

92+
const updateKadenaTokens = async (chainId) => {
93+
const invalidChainTokens = [];
94+
let tokens = [];
95+
96+
try {
97+
tokens = await getTokensBatch(chainId);
98+
console.log(`[CHAIN ${chainId}] BATCH FOUND ${tokens?.length} tokens`);
99+
} catch (err) {
100+
console.error(`[CHAIN ${chainId}] Batch approach failed or returned no data:`, err.message);
101+
tokens = await getTokensFallback(chainId);
102+
console.log(`[CHAIN ${chainId}] Fallback found ${tokens.length} tokens`);
103+
}
104+
105+
if (!tokens.length) {
106+
console.error(`NO TOKENS FOUNDED ON CHAIN ${chainId}`);
107+
return;
108+
}
109+
110+
const storedTokens = await getStoredKadenaTokensByChain(chainId);
111+
console.log(`[CHAIN ${chainId}] ${storedTokens?.length} tokens already saved`);
112+
const difference = tokens.filter((t) => !storedTokens.includes(t));
113+
114+
if (!difference.length) {
115+
console.log(`[CHAIN ${chainId}] No new tokens to add.`);
116+
return;
117+
}
118+
119+
console.log(`[CHAIN ${chainId}] FOUND ${difference.length} new tokens: ${difference.join(', ')}`);
120+
121+
const validChainTokens = [...storedTokens];
122+
let tokenCount = 1;
123+
124+
for (const token of difference) {
125+
try {
126+
const isTokenWorking = await makePactCall(chainId.toString(), `(${token}.get-balance "k:alice")`);
127+
// await sleep(500);
128+
if (
129+
isTokenWorking?.result?.status === 'success' ||
130+
isTokenWorking?.result?.error?.message?.includes('row not found') ||
131+
isTokenWorking?.result?.error?.message?.includes('No value found in table')
132+
) {
133+
console.log(`[CHAIN ${chainId}] TOKEN ${tokenCount}/${tokens.length}: ${token} IS VALID`);
134+
validChainTokens.push(token);
135+
} else {
136+
console.error(`[CHAIN ${chainId}] TOKEN ${tokenCount}/${tokens.length}: ${token} IS NOT VALID`);
137+
invalidChainTokens.push(token);
138+
}
139+
} catch (err) {
140+
console.error(`FETCH ERROR ${token}:`, err);
141+
}
142+
143+
tokenCount += 1;
144+
}
145+
146+
console.log(`[CHAIN ${chainId}] invalid TOKENS: ${invalidChainTokens.length}/${tokens.length}`);
147+
148+
try {
149+
const { db } = await mongoConnect();
150+
const collection = db.collection(COLLECTION_NAME);
151+
152+
await collection.updateOne(
153+
{ chainId: chainId.toString() },
154+
{
155+
$set: {
156+
tokens: stringify(validChainTokens),
157+
lastUpdate: new Date().toISOString(),
158+
},
159+
},
160+
{ upsert: true }
161+
);
162+
console.log(`[CHAIN ${chainId}] SAVED TOKENS => ${validChainTokens.length} total`);
163+
} catch (dbErr) {
164+
console.error(`[CHAIN ${chainId}] Error saving to Mongo:`, dbErr);
165+
}
166+
};
167+
105168
const allKadenaTokenUpdate = async () => {
106169
try {
107170
for (let chainId = 0; chainId < KADENA_CHAINS_COUNT; chainId++) {
@@ -116,4 +179,7 @@ const allKadenaTokenUpdate = async () => {
116179
}
117180
};
118181

119-
module.exports = { allKadenaTokenUpdate, getStoredKadenaTokensByChain };
182+
module.exports = {
183+
allKadenaTokenUpdate,
184+
getStoredKadenaTokensByChain,
185+
};

0 commit comments

Comments
 (0)