Skip to content

Commit 123160e

Browse files
authored
Merge pull request #431 from hack-a-chain-software/transaction-code
feat: added code filter to transactions query
2 parents 40ce2d7 + 4832e49 commit 123160e

File tree

9 files changed

+642
-2
lines changed

9 files changed

+642
-2
lines changed

indexer/src/kadena-server/config/graphql-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,7 @@ export type QueryTransactionsArgs = {
959959
before?: InputMaybe<Scalars['String']['input']>;
960960
blockHash?: InputMaybe<Scalars['String']['input']>;
961961
chainId?: InputMaybe<Scalars['String']['input']>;
962+
code?: InputMaybe<Scalars['String']['input']>;
962963
first?: InputMaybe<Scalars['Int']['input']>;
963964
fungibleName?: InputMaybe<Scalars['String']['input']>;
964965
isCoinbase?: InputMaybe<Scalars['Boolean']['input']>;

indexer/src/kadena-server/config/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ type Query {
311311
minimumDepth: Int
312312
requestKey: String
313313
isCoinbase: Boolean
314+
code: String
314315
): QueryTransactionsConnection! @complexity(value: 1, multipliers: ["first", "last"])
315316

316317
"""

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ export interface GetTransactionsCountParams {
7373
hasTokenId?: boolean | null;
7474
/** Filter by coinbase */
7575
isCoinbase?: boolean | null;
76+
/** Filter by transaction code */
77+
transactionCode?: string | null;
7678
}
7779

7880
/**

indexer/src/kadena-server/repository/infra/query-builders/transaction-query-builder.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,4 +413,69 @@ export default class TransactionQueryBuilder {
413413

414414
return { query, queryParams };
415415
}
416+
417+
buildTransactionByCodeQuery(params: {
418+
after?: string | null;
419+
before?: string | null;
420+
order: string;
421+
limit: number;
422+
transactionCode: string;
423+
}) {
424+
let whereCondition = `\nWHERE td.code::text LIKE '%' || $2::text || '%' AND b.canonical = $3`;
425+
let queryParams: (string | number | boolean)[] = [params.limit, params.transactionCode, true];
426+
427+
if (!params.after && !params.before && params.order === 'DESC') {
428+
const currentTime = Date.now() - 10000000;
429+
queryParams.push(currentTime, 0);
430+
whereCondition += `\nAND t.creationtime > $${queryParams.length - 1} AND t.id > $${queryParams.length}`;
431+
}
432+
433+
if (params.after) {
434+
const [creationTime, id] = params.after.split(':');
435+
queryParams.push(creationTime, id);
436+
whereCondition += `\nAND (t.creationtime, t.id) < ($${queryParams.length - 1}, $${queryParams.length})`;
437+
}
438+
if (params.before) {
439+
const [creationTime, id] = params.before.split(':');
440+
queryParams.push(creationTime, id);
441+
whereCondition += `\nAND (t.creationtime, t.id) > ($${queryParams.length - 1}, $${queryParams.length})`;
442+
}
443+
444+
const query = `
445+
WITH filtered_transactions AS (
446+
SELECT t.*, td.code, td.nonce, td.sigs, td.continuation, td.pactid, td.proof, td.rollback, td.gas, td.step, td.data, b.height, b.hash as "blockHash"
447+
FROM "Transactions" t
448+
JOIN "TransactionDetails" td ON t.id = td."transactionId"
449+
JOIN "Blocks" b ON b.id = t."blockId"
450+
${whereCondition}
451+
ORDER BY t.creationtime ${params.order}, t.id ${params.order}
452+
)
453+
SELECT
454+
t.id AS id,
455+
t.creationtime AS "creationTime",
456+
t.hash AS "hashTransaction",
457+
t.nonce AS "nonceTransaction",
458+
t.sigs AS sigs,
459+
t.continuation AS continuation,
460+
t.num_events AS "eventCount",
461+
t.pactid AS "pactId",
462+
t.proof AS proof,
463+
t.rollback AS rollback,
464+
t.txid AS txid,
465+
t.height AS "height",
466+
t."blockHash" AS "blockHash",
467+
t."chainId" AS "chainId",
468+
t.gas AS "gas",
469+
t.step AS step,
470+
t.data AS data,
471+
t.code AS code,
472+
t.logs AS "logs",
473+
t.result AS "result",
474+
t.requestkey AS "requestKey"
475+
FROM filtered_transactions t
476+
LIMIT $1
477+
`;
478+
479+
return { query, queryParams };
480+
}
416481
}

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,30 @@ export default class TransactionDbRepository implements TransactionRepository {
126126
return pageInfo;
127127
}
128128

129-
const maxHeightFromDb = (await rootPgPool.query(`SELECT max(height) FROM "Blocks"`)).rows[0]
130-
.max;
129+
if (rest.transactionCode) {
130+
const { query, queryParams } = this.queryBuilder.buildTransactionByCodeQuery({
131+
after,
132+
before,
133+
order,
134+
limit,
135+
transactionCode: rest.transactionCode,
136+
});
137+
138+
// Execute the query with the constructed parameters
139+
const { rows } = await rootPgPool.query(query, queryParams);
140+
141+
// Transform database rows into GraphQL-compatible edges with cursors
142+
const edges = rows.map(row => ({
143+
cursor: `${row.creationTime.toString()}:${row.id.toString()}`,
144+
node: transactionValidator.validate(row),
145+
}));
146+
147+
const pageInfo = getPageInfo({ edges, order, limit, after, before });
148+
return pageInfo;
149+
}
131150

151+
const maxHeightQuery = `SELECT max(height) FROM "Blocks"`;
152+
const maxHeightFromDb = (await rootPgPool.query(maxHeightQuery)).rows[0].max;
132153
// If no minimumDepth is specified, we can use the normal query approach
133154
if (!rest.minimumDepth) {
134155
// Build and execute the query using the query builder

indexer/src/kadena-server/resolvers/fields/query-transactions-connection/total-count-query-transactions-connection-resolver.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const schema = zod.object({
2020
minimumDepth: zod.number().nullable().optional(),
2121
requestKey: zod.string().nullable().optional(),
2222
isCoinbase: zod.boolean().nullable().optional(),
23+
transactionCode: zod.string().nullable().optional(),
2324
});
2425

2526
/**
@@ -43,7 +44,13 @@ export const totalCountQueryTransactionsConnectionResolver: QueryTransactionsCon
4344
fungibleName,
4445
requestKey,
4546
isCoinbase,
47+
transactionCode,
4648
} = schema.parse(parent);
49+
50+
if (transactionCode) {
51+
throw new Error('Total count is not supported for transactions with code.');
52+
}
53+
4754
const output = await context.transactionRepository.getTransactionsCount({
4855
accountName,
4956
blockHash,

indexer/src/kadena-server/resolvers/query/transactions-query-resolver.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* a clean interface for transaction data access.
1717
*/
1818

19+
import { isNullOrUndefined } from '@/utils/helpers';
1920
import { ResolverContext } from '../../config/apollo-server-config';
2021
import { QueryResolvers } from '../../config/graphql-types';
2122
import { buildTransactionOutput } from '../output/build-transaction-output';
@@ -59,6 +60,21 @@ export const transactionsQueryResolver: QueryResolvers<ResolverContext>['transac
5960
isCoinbase,
6061
} = args;
6162

63+
const hasNoParamsSet = Object.values({
64+
accountName,
65+
blockHash,
66+
chainId,
67+
fungibleName,
68+
requestKey,
69+
maxHeight,
70+
minHeight,
71+
minimumDepth,
72+
isCoinbase,
73+
}).every(v => isNullOrUndefined(v));
74+
if (args.code && !hasNoParamsSet) {
75+
throw new Error('Code parameter cannot be composed with other filters');
76+
}
77+
6278
// Call the repository layer to retrieve the filtered and paginated transactions
6379
// Pass all parameters through to maintain complete filtering flexibility
6480
const output = await context.transactionRepository.getTransactions({
@@ -71,6 +87,7 @@ export const transactionsQueryResolver: QueryResolvers<ResolverContext>['transac
7187
minHeight,
7288
minimumDepth,
7389
isCoinbase,
90+
transactionCode: args.code,
7491
first,
7592
last,
7693
before,
@@ -105,5 +122,6 @@ export const transactionsQueryResolver: QueryResolvers<ResolverContext>['transac
105122
isCoinbase,
106123
fungibleName,
107124
requestKey,
125+
transactionCode: args.code,
108126
};
109127
};

0 commit comments

Comments
 (0)