Skip to content

Commit e04e69f

Browse files
authored
Merge pull request #261 from hack-a-chain-software/fixes
Bugfixes from Tests
2 parents b750592 + c2707f5 commit e04e69f

18 files changed

+446
-266
lines changed

indexer/src/kadena-server/repository/application/balance-repository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ export default interface BalanceRepository {
211211
getAccountInfo_NODE(
212212
accountName: string,
213213
fungibleName?: string | null,
214-
): Promise<FungibleAccountOutput>;
214+
): Promise<FungibleAccountOutput | null>;
215215

216216
/**
217217
* Retrieves fungible token information for an account across specified chains from blockchain nodes.

indexer/src/kadena-server/repository/infra/repository/balance-db-repository.ts

Lines changed: 56 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export default class BalanceDbRepository implements BalanceRepository {
6969
throw new Error('Account not found.');
7070
}
7171

72-
const accountInfo = fungibleAccountValidator.mapFromSequelize(account);
72+
const accountInfo = fungibleAccountValidator.validate(account);
7373
const totalBalance = fungibleAccountValidator.validateTotalBalance(rows[0]);
7474
return { ...accountInfo, totalBalance };
7575
}
@@ -352,7 +352,7 @@ export default class BalanceDbRepository implements BalanceRepository {
352352
): Promise<INonFungibleTokenBalance | null> {
353353
const queryParams = [accountName, tokenId, chainId];
354354
let query = `
355-
SELECT b.id, b."chainId", b.balance, b."tokenId", b.account, b."hasTokenId"
355+
SELECT b.id, b."chainId", b.balance, b."tokenId", b.account, b.module, b."hasTokenId"
356356
FROM "Balances" b
357357
WHERE b.account = $1
358358
AND b."tokenId" = $2
@@ -382,7 +382,7 @@ export default class BalanceDbRepository implements BalanceRepository {
382382
async getAccountInfo_NODE(
383383
accountName: string,
384384
fungibleName = 'coin',
385-
): Promise<FungibleAccountOutput> {
385+
): Promise<FungibleAccountOutput | null> {
386386
const query = `
387387
SELECT DISTINCT b."chainId"
388388
FROM "Balances" b
@@ -391,6 +391,8 @@ export default class BalanceDbRepository implements BalanceRepository {
391391
`;
392392
const { rows } = await rootPgPool.query(query, [accountName, fungibleName]);
393393

394+
if (rows.length === 0) return null;
395+
394396
const chainIds = rows.map(r => Number(r.chainId));
395397

396398
// Query each chain directly using Pact code
@@ -406,13 +408,12 @@ export default class BalanceDbRepository implements BalanceRepository {
406408
const balancesNumber = balances.map(b => formatBalance_NODE(b));
407409
const totalBalance = balancesNumber.reduce((acc, cur) => acc + cur, 0);
408410

409-
const accountInfo = fungibleAccountValidator.mapFromSequelize({
411+
const accountInfo = fungibleAccountValidator.validate({
412+
id: -1,
410413
account: accountName,
411414
module: fungibleName,
412-
chainId: -1,
413-
balance: BigInt(-1),
414-
hasTokenId: false,
415415
});
416+
416417
return { ...accountInfo, totalBalance };
417418
}
418419

@@ -502,86 +503,27 @@ export default class BalanceDbRepository implements BalanceRepository {
502503
// If no guard rows found, try with k: prefix for direct account lookup
503504
const params = [`k:${publicKey}`, fungibleName];
504505
const query = `
505-
SELECT b.account, b."chainId"
506+
SELECT b.id, b.account, b."chainId"
506507
FROM "Balances" b
507508
WHERE b.account = $1
508509
AND b.module = $2
509510
`;
510511
const { rows } = await rootPgPool.query(query, params);
511512

512513
if (!rows.length) return [];
513-
514-
// Query each chain directly using Pact code
515-
const balancePromises = rows.map(r => {
516-
const query = {
517-
chainId: r.chainId.toString(),
518-
code: `(${fungibleName}.details \"${r.account}\")`,
519-
};
520-
return handleSingleQuery(query);
521-
});
522-
523-
const balances = (await Promise.all(balancePromises)).filter(b => b.status === 'success');
524-
const balancesNumber = balances.map(q => formatBalance_NODE(q));
525-
const totalBalance = balancesNumber.reduce((acc, cur) => acc + cur, 0);
526-
527-
const accountInfo = fungibleAccountValidator.mapFromSequelize({
528-
account: `k:${publicKey}`,
529-
module: fungibleName,
530-
chainId: -1,
531-
balance: BigInt(-1),
532-
hasTokenId: false,
533-
});
534-
return [{ ...accountInfo, totalBalance }];
514+
return this.processAccounts(rows, fungibleName);
535515
} else {
536516
// Handle accounts associated with public key via guards
537517
const params = [publicKey, fungibleName];
538518
const query = `
539-
SELECT b.account, b."chainId"
519+
SELECT b.id, b.account, b."chainId"
540520
FROM "Balances" b
541521
JOIN "Guards" g ON b.id = g."balanceId"
542522
WHERE g."publicKey" = $1
543523
AND b.module = $2
544524
`;
545525
const { rows } = await rootPgPool.query(query, params);
546-
547-
// Group chains by account for efficient querying
548-
const groupedByAccount: Record<string, string[]> = rows.reduce(
549-
(acc, cur) => {
550-
if (!acc[cur.account]) {
551-
acc[cur.account] = [];
552-
}
553-
acc[cur.account].push(cur.chainId);
554-
return acc;
555-
},
556-
{} as Record<string, string[]>,
557-
);
558-
559-
// Process each account and get balances from node
560-
const accountsPromises = Object.entries(groupedByAccount).map(
561-
async ([account, chainIds], index) => {
562-
const balances = chainIds.map(async c => {
563-
const query = {
564-
chainId: c.toString(),
565-
code: `(${fungibleName}.details \"${account}\")`,
566-
};
567-
const result = await handleSingleQuery(query);
568-
return formatBalance_NODE(result);
569-
});
570-
571-
const balancesNumber = await Promise.all(balances);
572-
const totalBalance: Number = balancesNumber.reduce((acc, cur) => acc + cur, 0);
573-
574-
return {
575-
id: index.toString(),
576-
accountName: account,
577-
totalBalance,
578-
fungibleName,
579-
};
580-
},
581-
);
582-
583-
const accounts = await Promise.all(accountsPromises);
584-
return accounts;
526+
return this.processAccounts(rows, fungibleName);
585527
}
586528
}
587529

@@ -729,4 +671,48 @@ export default class BalanceDbRepository implements BalanceRepository {
729671
const pageInfo = getPageInfo({ edges, order, limit, after, before });
730672
return pageInfo;
731673
}
674+
675+
private async processAccounts(
676+
rows: { id: string; account: string; chainId: string }[],
677+
fungibleName: string,
678+
): Promise<FungibleAccountOutput[]> {
679+
const groupedByAccount: Record<string, string[]> = rows.reduce(
680+
(acc, cur) => {
681+
if (!acc[cur.account]) {
682+
acc[cur.account] = [];
683+
}
684+
acc[cur.account].push(cur.chainId);
685+
return acc;
686+
},
687+
{} as Record<string, string[]>,
688+
);
689+
690+
const accountsPromises = Object.entries(groupedByAccount).map(
691+
async ([account, chainIds], index) => {
692+
const balances = chainIds.map(async c => {
693+
const query = {
694+
chainId: c.toString(),
695+
code: `(${fungibleName}.details \"${account}\")`,
696+
};
697+
const result = await handleSingleQuery(query);
698+
return formatBalance_NODE(result);
699+
});
700+
701+
const balancesNumber = await Promise.all(balances);
702+
const totalBalance: Number = balancesNumber.reduce((acc, cur) => acc + cur, 0);
703+
704+
const accountInfo = fungibleAccountValidator.validate({
705+
id: index,
706+
account,
707+
module: fungibleName,
708+
});
709+
return {
710+
...accountInfo,
711+
totalBalance,
712+
};
713+
},
714+
);
715+
716+
return Promise.all(accountsPromises);
717+
}
732718
}

indexer/src/kadena-server/repository/infra/repository/block-db-repository.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ export default class BlockDbRepository implements BlockRepository {
160160
conditions += `\nAND b."chainId" = ANY($${queryParams.length})`;
161161
}
162162

163-
if (endHeight && endHeight - startHeight < 20) {
163+
if (endHeight) {
164164
queryParams.push(endHeight);
165165
conditions += `\nAND b."height" <= $${queryParams.length}`;
166166
}
@@ -182,7 +182,7 @@ export default class BlockDbRepository implements BlockRepository {
182182
FROM "Blocks" b
183183
WHERE b.height >= $2
184184
${conditions}
185-
ORDER BY b.height ${order}
185+
ORDER BY b.height, b.id ${order}
186186
LIMIT $1;
187187
`;
188188

indexer/src/kadena-server/repository/infra/repository/event-db-repository.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,6 @@ export default class EventDbRepository implements EventRepository {
227227
eventConditions += `\nAND e.id < $${queryParams.length}`;
228228
}
229229

230-
if (requestKey) {
231-
blockQueryParams.push(requestKey);
232-
const op = localOperator(blockQueryParams.length);
233-
conditions += `${op} t."requestkey" = $${blockQueryParams.length + queryParams.length}`;
234-
}
235-
236230
if (blockHash) {
237231
blockQueryParams.push(blockHash);
238232
const op = localOperator(blockQueryParams.length);
@@ -272,8 +266,10 @@ export default class EventDbRepository implements EventRepository {
272266
conditions += `${op} b."height" <= $${blockQueryParams.length + queryParams.length}`;
273267
}
274268

269+
const isHeightChainOrBlockHash = fromHeight || toHeight || blockHash || chainId;
270+
275271
let query = '';
276-
if (fromHeight || toHeight || blockHash || chainId) {
272+
if (isHeightChainOrBlockHash) {
277273
query = `
278274
WITH block_filtered AS (
279275
select *
@@ -296,6 +292,35 @@ export default class EventDbRepository implements EventRepository {
296292
WHERE e.module = $2
297293
AND e.name = $3
298294
${eventConditions}
295+
ORDER BY b.height ${order}
296+
LIMIT $1
297+
`;
298+
} else if (requestKey) {
299+
queryParams.push(requestKey);
300+
query = `
301+
WITH event_transaction_filtered AS (
302+
SELECT e.*, t."blockId"
303+
FROM "Transactions" t
304+
JOIN "Events" e ON t.id = e."transactionId"
305+
WHERE e.module = $2
306+
AND e.name = $3
307+
AND t.requestkey = $${blockQueryParams.length + queryParams.length}
308+
${eventConditions}
309+
ORDER BY e.id ${order}
310+
)
311+
SELECT
312+
et.id as id,
313+
et.requestkey as "requestKey",
314+
et."chainId" as "chainId",
315+
b.height as height,
316+
et."orderIndex" as "orderIndex",
317+
et.module as "moduleName",
318+
et.name as name,
319+
et.params as parameters,
320+
b.hash as "blockHash"
321+
FROM event_transaction_filtered et
322+
JOIN "Blocks" b ON b.id = et."blockId"
323+
${conditions}
299324
LIMIT $1
300325
`;
301326
} else {
@@ -306,7 +331,7 @@ export default class EventDbRepository implements EventRepository {
306331
WHERE e.module = $2
307332
AND e.name = $3
308333
${eventConditions}
309-
ORDER BY e.module, e.name ${order}
334+
ORDER BY e.id ${order}
310335
)
311336
SELECT
312337
e.id as id,
@@ -329,7 +354,7 @@ export default class EventDbRepository implements EventRepository {
329354
const { rows } = await rootPgPool.query(query, [...queryParams, ...blockQueryParams]);
330355

331356
const edges = rows.map(row => ({
332-
cursor: row.id.toString(),
357+
cursor: isHeightChainOrBlockHash ? row.height.toString() : row.id.toString(),
333358
node: eventValidator.validate(row),
334359
}));
335360

indexer/src/kadena-server/repository/infra/repository/transfer-db-repository.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,11 @@ export default class TransferDbRepository implements TransferRepository {
122122

123123
if (blockHash) {
124124
queryParams.push(blockHash);
125-
const op = operator(queryParams.length);
126125
query = `
127126
WITH filtered_block AS (
128127
SELECT id, height, hash
129128
FROM "Blocks" b
130-
${op} b.hash = $${queryParams.length}
129+
WHERE b.hash = $${queryParams.length}
131130
)
132131
select transfers.id as id,
133132
transfers.amount as "transferAmount",

indexer/src/kadena-server/repository/infra/schema-validator/fungible-account-validator.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -91,32 +91,11 @@ const validateTotalBalance = (row: any): string => {
9191
return res.totalBalance;
9292
};
9393

94-
/**
95-
* Transforms a Sequelize balance model instance into the standardized output format
96-
*
97-
* This function works specifically with Sequelize ORM model instances,
98-
* converting them to the standardized partial FungibleAccountOutput format.
99-
* Like validate(), it doesn't include the total balance in its output.
100-
*
101-
* @param balanceModel - A Sequelize Balance model instance
102-
* @returns A transformed partial FungibleAccountOutput object
103-
*/
104-
const mapFromSequelize = (
105-
balanceModel: BalanceAttributes,
106-
): Omit<FungibleAccountOutput, 'totalBalance'> => {
107-
return {
108-
id: getBase64ID(balanceModel.module, balanceModel.account),
109-
accountName: balanceModel.account,
110-
fungibleName: balanceModel.module,
111-
};
112-
};
113-
11494
/**
11595
* Exported validator object providing validation and transformation functionality
11696
* This pattern allows for easy mocking in tests and consistent usage across the codebase
11797
*/
11898
export const fungibleAccountValidator = {
11999
validate,
120100
validateTotalBalance,
121-
mapFromSequelize,
122101
};

indexer/src/kadena-server/resolvers/output/build-fungible-account-output.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import { FungibleAccountOutput } from '../../repository/application/balance-repo
2929
* @param account - The fungible account entity from the database repository
3030
* @returns A GraphQL-compatible fungible account object with placeholders for related entities
3131
*/
32-
export const buildFungibleAccount = (account: FungibleAccountOutput) => {
32+
export const buildFungibleAccount = (account: FungibleAccountOutput | null) => {
33+
if (!account) return null;
3334
return {
3435
...account,
3536
// resolvers

indexer/src/kadena-server/resolvers/query/fungible-account-query-resolver.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ export const fungibleAccountQueryResolver: QueryResolvers<ResolverContext>['fung
3434
args.accountName,
3535
args.fungibleName,
3636
);
37+
3738
return buildFungibleAccount(account);
3839
};

indexer/src/kadena-server/resolvers/query/fungible-accounts-by-public-key-query-resolver.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ export const fungibleAccountsByPublicKeyQueryResolver: QueryResolvers<ResolverCo
1010
fungibleName,
1111
);
1212

13-
const output = accounts.map(acc => buildFungibleAccount(acc));
13+
const output = accounts
14+
.map(acc => buildFungibleAccount(acc))
15+
.filter((acc): acc is NonNullable<typeof acc> => acc !== null);
1416

1517
return output;
1618
};

indexer/tests/integration/builders/events.builder.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ export const getEventsQuery = (params: any): string => {
1212
const queryGql = gql`
1313
query {
1414
events(${query}) {
15+
pageInfo {
16+
hasNextPage
17+
hasPreviousPage
18+
startCursor
19+
endCursor
20+
}
1521
edges {
1622
cursor
1723
node {

0 commit comments

Comments
 (0)