Skip to content

Commit 88fc5ce

Browse files
authored
feat: tx list nonce filter option (#2023)
* feat: tx list nonce filter option * docs: tx nonce filter param
1 parent 9c2fd78 commit 88fc5ce

File tree

5 files changed

+119
-1
lines changed

5 files changed

+119
-1
lines changed

docs/openapi.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,13 @@ paths:
268268
schema:
269269
type: string
270270
example: "delegate-stx"
271+
- name: nonce
272+
in: query
273+
description: Filter by transactions with this nonce
274+
required: false
275+
schema:
276+
type: integer
277+
example: 123
271278
- name: order
272279
in: query
273280
description: Option to sort results in ascending or descending order
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/** @param { import("node-pg-migrate").MigrationBuilder } pgm */
2+
exports.up = pgm => {
3+
pgm.createIndex('txs', 'nonce');
4+
};
5+
6+
/** @param { import("node-pg-migrate").MigrationBuilder } pgm */
7+
exports.down = pgm => {
8+
pgm.dropIndex('txs', 'nonce');
9+
};

src/api/routes/tx.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,17 @@ export function createTxRouter(db: PgStore): express.Router {
140140
functionName = req.query.function_name;
141141
}
142142

143+
let nonce: number | undefined;
144+
if (typeof req.query.nonce === 'string') {
145+
if (!/^\d{1,10}$/.test(req.query.nonce)) {
146+
throw new InvalidRequestError(
147+
`Invalid query parameter for "nonce": "${req.query.nonce}" is not a valid nonce`,
148+
InvalidRequestErrorType.invalid_param
149+
);
150+
}
151+
nonce = parseInt(req.query.nonce);
152+
}
153+
143154
let sortBy: 'block_height' | 'burn_block_time' | 'fee' | undefined;
144155
if (req.query.sort_by) {
145156
if (
@@ -166,6 +177,7 @@ export function createTxRouter(db: PgStore): express.Router {
166177
endTime,
167178
contractId,
168179
functionName,
180+
nonce,
169181
order,
170182
sortBy,
171183
});

src/datastore/pg-store.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,7 @@ export class PgStore extends BasePgStore {
14221422
endTime,
14231423
contractId,
14241424
functionName,
1425+
nonce,
14251426
order,
14261427
sortBy,
14271428
}: {
@@ -1435,6 +1436,7 @@ export class PgStore extends BasePgStore {
14351436
endTime?: number;
14361437
contractId?: string;
14371438
functionName?: string;
1439+
nonce?: number;
14381440
order?: 'desc' | 'asc';
14391441
sortBy?: 'block_height' | 'burn_block_time' | 'fee';
14401442
}): Promise<{ results: DbTx[]; total: number }> {
@@ -1474,8 +1476,16 @@ export class PgStore extends BasePgStore {
14741476
const contractFuncFilterSql = functionName
14751477
? sql`AND contract_call_function_name = ${functionName}`
14761478
: sql``;
1479+
const nonceFilterSql = nonce ? sql`AND nonce = ${nonce}` : sql``;
14771480
const noFilters =
1478-
txTypeFilter.length === 0 && !fromAddress && !toAddress && !startTime && !endTime;
1481+
txTypeFilter.length === 0 &&
1482+
!fromAddress &&
1483+
!toAddress &&
1484+
!startTime &&
1485+
!endTime &&
1486+
!contractId &&
1487+
!functionName &&
1488+
!nonce;
14791489

14801490
const totalQuery: { count: number }[] = noFilters
14811491
? await sql<{ count: number }[]>`
@@ -1493,6 +1503,7 @@ export class PgStore extends BasePgStore {
14931503
${endTimeFilterSql}
14941504
${contractIdFilterSql}
14951505
${contractFuncFilterSql}
1506+
${nonceFilterSql}
14961507
`;
14971508

14981509
const resultQuery: ContractTxQueryResult[] = await sql<ContractTxQueryResult[]>`
@@ -1506,6 +1517,7 @@ export class PgStore extends BasePgStore {
15061517
${endTimeFilterSql}
15071518
${contractIdFilterSql}
15081519
${contractFuncFilterSql}
1520+
${nonceFilterSql}
15091521
${orderBySql}
15101522
LIMIT ${limit}
15111523
OFFSET ${offset}

src/tests/tx-tests.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,6 +2469,84 @@ describe('tx tests', () => {
24692469
);
24702470
});
24712471

2472+
test('tx list - filter by nonce', async () => {
2473+
const testSendertAddr = 'ST27W5M8BRKA7C5MZE2R1S1F4XTPHFWFRNHA9M04Y';
2474+
const block1 = new TestBlockBuilder({
2475+
block_height: 1,
2476+
index_block_hash: '0x01',
2477+
burn_block_time: 1710000000,
2478+
})
2479+
.addTx({
2480+
tx_id: '0x1234',
2481+
fee_rate: 1n,
2482+
sender_address: testSendertAddr,
2483+
nonce: 1,
2484+
})
2485+
.build();
2486+
2487+
await db.update(block1);
2488+
2489+
const block2 = new TestBlockBuilder({
2490+
block_height: 2,
2491+
index_block_hash: '0x02',
2492+
parent_block_hash: block1.block.block_hash,
2493+
parent_index_block_hash: block1.block.index_block_hash,
2494+
burn_block_time: 1720000000,
2495+
})
2496+
.addTx({
2497+
tx_id: '0x2234',
2498+
fee_rate: 3n,
2499+
sender_address: testSendertAddr,
2500+
nonce: 2,
2501+
})
2502+
.build();
2503+
await db.update(block2);
2504+
2505+
const block3 = new TestBlockBuilder({
2506+
block_height: 3,
2507+
index_block_hash: '0x03',
2508+
parent_block_hash: block2.block.block_hash,
2509+
parent_index_block_hash: block2.block.index_block_hash,
2510+
burn_block_time: 1730000000,
2511+
})
2512+
.addTx({
2513+
tx_id: '0x3234',
2514+
fee_rate: 2n,
2515+
sender_address: testSendertAddr,
2516+
nonce: 3,
2517+
})
2518+
.build();
2519+
await db.update(block3);
2520+
2521+
const txsReq1 = await supertest(api.server).get(
2522+
`/extended/v1/tx?from_address=${testSendertAddr}&nonce=${1}`
2523+
);
2524+
expect(txsReq1.status).toBe(200);
2525+
expect(txsReq1.body).toEqual(
2526+
expect.objectContaining({
2527+
results: [
2528+
expect.objectContaining({
2529+
tx_id: block1.txs[0].tx.tx_id,
2530+
}),
2531+
],
2532+
})
2533+
);
2534+
2535+
const txsReq2 = await supertest(api.server).get(
2536+
`/extended/v1/tx?from_address=${testSendertAddr}&nonce=${2}`
2537+
);
2538+
expect(txsReq2.status).toBe(200);
2539+
expect(txsReq2.body).toEqual(
2540+
expect.objectContaining({
2541+
results: [
2542+
expect.objectContaining({
2543+
tx_id: block2.txs[0].tx.tx_id,
2544+
}),
2545+
],
2546+
})
2547+
);
2548+
});
2549+
24722550
test('fetch raw tx', async () => {
24732551
const block: DbBlock = {
24742552
block_hash: '0x1234',

0 commit comments

Comments
 (0)