Skip to content

Commit

Permalink
feat: add /extended/v2/mempool/fees endpoint (#1795)
Browse files Browse the repository at this point in the history
* docs: endpoints

* feat: fees endpoint

* test: strict eq
  • Loading branch information
rafaelcr authored Jan 3, 2024
1 parent 2700642 commit ea9c378
Show file tree
Hide file tree
Showing 10 changed files with 403 additions and 3 deletions.
26 changes: 26 additions & 0 deletions docs/api/mempool/get-fee-priorities.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"all": {
"no_priority": 3000,
"low_priority": 3000,
"medium_priority": 6000,
"high_priority": 401199
},
"token_transfer": {
"no_priority": 3000,
"low_priority": 3000,
"medium_priority": 6000,
"high_priority": 401199
},
"smart_contract": {
"no_priority": 837500,
"low_priority": 925000,
"medium_priority": 1012500,
"high_priority": 1082500
},
"contract_call": {
"no_priority": 3000,
"low_priority": 10368,
"medium_priority": 100000,
"high_priority": 1000000
}
}
107 changes: 107 additions & 0 deletions docs/api/mempool/get-fee-priorities.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{
"description": "GET request that returns fee priorities from mempool transactions",
"title": "MempoolFeePriorities",
"type": "object",
"additionalProperties": false,
"required": [
"all"
],
"properties": {
"all": {
"type": "object",
"additionalProperties": false,
"required": [
"no_priority",
"low_priority",
"medium_priority",
"high_priority"
],
"properties": {
"no_priority": {
"type": "integer"
},
"low_priority": {
"type": "integer"
},
"medium_priority": {
"type": "integer"
},
"high_priority": {
"type": "integer"
}
}
},
"token_transfer": {
"type": "object",
"additionalProperties": false,
"required": [
"no_priority",
"low_priority",
"medium_priority",
"high_priority"
],
"properties": {
"no_priority": {
"type": "integer"
},
"low_priority": {
"type": "integer"
},
"medium_priority": {
"type": "integer"
},
"high_priority": {
"type": "integer"
}
}
},
"smart_contract": {
"type": "object",
"additionalProperties": false,
"required": [
"no_priority",
"low_priority",
"medium_priority",
"high_priority"
],
"properties": {
"no_priority": {
"type": "integer"
},
"low_priority": {
"type": "integer"
},
"medium_priority": {
"type": "integer"
},
"high_priority": {
"type": "integer"
}
}
},
"contract_call": {
"type": "object",
"additionalProperties": false,
"required": [
"no_priority",
"low_priority",
"medium_priority",
"high_priority"
],
"properties": {
"no_priority": {
"type": "integer"
},
"low_priority": {
"type": "integer"
},
"medium_priority": {
"type": "integer"
},
"high_priority": {
"type": "integer"
}
}
}
}
}
30 changes: 30 additions & 0 deletions docs/generated.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export type SchemaMergeRootStub =
| GetStxSupplyLegacyFormatResponse
| GetStxTotalSupplyPlainResponse
| GetStxSupplyResponse
| MempoolFeePriorities
| MicroblockListResponse
| UnanchoredTransactionListResponse
| RosettaAccountBalanceRequest
Expand Down Expand Up @@ -1694,6 +1695,35 @@ export interface GetStxSupplyResponse {
*/
block_height: number;
}
/**
* GET request that returns fee priorities from mempool transactions
*/
export interface MempoolFeePriorities {
all: {
no_priority: number;
low_priority: number;
medium_priority: number;
high_priority: number;
};
token_transfer?: {
no_priority: number;
low_priority: number;
medium_priority: number;
high_priority: number;
};
smart_contract?: {
no_priority: number;
low_priority: number;
medium_priority: number;
high_priority: number;
};
contract_call?: {
no_priority: number;
low_priority: number;
medium_priority: number;
high_priority: number;
};
}
/**
* GET request that returns microblocks
*/
Expand Down
20 changes: 20 additions & 0 deletions docs/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ tags:
externalDocs:
description: Hiro Documentation - Transactions
url: https://docs.hiro.so/get-started/transactions
- name: Mempool
description: Endpoints to obtain Mempool information

paths:
/extended/v1/faucets/stx:
Expand Down Expand Up @@ -296,6 +298,24 @@ paths:
example:
$ref: ./api/transaction/get-mempool-transactions.example.json

/extended/v2/mempool/fees:
get:
summary: Get mempool transaction fee priorities
tags:
- Mempool
operationId: get_mempool_fee_priorities
description: |
Returns estimated fee priorities (in micro-STX) for all transactions that are currently in the mempool. Also returns priorities separated by transaction type.
responses:
200:
description: Mempool fee priorities
content:
application/json:
schema:
$ref: ./api/transaction/get-mempool-transactions.schema.json
example:
$ref: ./api/transaction/get-mempool-transactions.example.json

/extended/v1/tx/mempool/dropped:
get:
summary: Get dropped mempool transactions
Expand Down
16 changes: 16 additions & 0 deletions src/api/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { createPox3EventsRouter } from './routes/pox3';
import { isPgConnectionError } from '../datastore/helpers';
import { createStackingRouter } from './routes/stacking';
import { logger, loggerMiddleware } from '../logger';
import { createMempoolRouter } from './v2/mempool';

export interface ApiServer {
expressApp: express.Express;
Expand Down Expand Up @@ -220,6 +221,21 @@ export async function startApiServer(opts: {
})()
);

app.use(
'/extended/v2',
(() => {
const router = express.Router();
router.use(cors());
router.use((req, res, next) => {
// Set caching on all routes to be disabled by default, individual routes can override
res.set('Cache-Control', 'no-store');
next();
});
router.use('/mempool', createMempoolRouter(datastore));
return router;
})()
);

app.use(
'/extended/beta',
(() => {
Expand Down
55 changes: 55 additions & 0 deletions src/api/v2/mempool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as express from 'express';
import { asyncHandler } from '../async-handler';
import {
ETagType,
getETagCacheHandler,
setETagCacheHeaders,
} from '../controllers/cache-controller';
import { PgStore } from '../../datastore/pg-store';
import { DbMempoolFeePriority, DbTxTypeId } from '../../datastore/common';
import { MempoolFeePriorities } from '../../../docs/generated';

function parseMempoolFeePriority(fees: DbMempoolFeePriority[]): MempoolFeePriorities {
const out: MempoolFeePriorities = {
all: { no_priority: 0, low_priority: 0, medium_priority: 0, high_priority: 0 },
};
for (const fee of fees) {
const value = {
no_priority: fee.no_priority,
low_priority: fee.low_priority,
medium_priority: fee.medium_priority,
high_priority: fee.high_priority,
};
if (fee.type_id == null) out.all = value;
else
switch (fee.type_id) {
case DbTxTypeId.TokenTransfer:
out.token_transfer = value;
break;
case DbTxTypeId.ContractCall:
out.contract_call = value;
break;
case DbTxTypeId.SmartContract:
case DbTxTypeId.VersionedSmartContract:
out.smart_contract = value;
break;
}
}
return out;
}

export function createMempoolRouter(db: PgStore): express.Router {
const router = express.Router();
const mempoolCacheHandler = getETagCacheHandler(db, ETagType.mempool);

router.get(
'/fees',
mempoolCacheHandler,
asyncHandler(async (req, res, next) => {
setETagCacheHeaders(res);
res.status(200).json(parseMempoolFeePriority(await db.getMempoolFeePriority()));
})
);

return router;
}
8 changes: 8 additions & 0 deletions src/datastore/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,14 @@ export interface DbMempoolStats {
>;
}

export interface DbMempoolFeePriority {
type_id: DbTxTypeId | null;
high_priority: number;
medium_priority: number;
low_priority: number;
no_priority: number;
}

export interface DbMempoolTx extends BaseTx {
pruned: boolean;

Expand Down
39 changes: 39 additions & 0 deletions src/datastore/pg-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
DbGetBlockWithMetadataOpts,
DbGetBlockWithMetadataResponse,
DbInboundStxTransfer,
DbMempoolFeePriority,
DbMempoolStats,
DbMempoolTx,
DbMicroblock,
Expand Down Expand Up @@ -1286,6 +1287,44 @@ export class PgStore {
};
}

async getMempoolFeePriority(): Promise<DbMempoolFeePriority[]> {
const txFeesQuery = await this.sql<DbMempoolFeePriority[]>`
WITH fees AS (
(
SELECT
NULL AS type_id,
ROUND(PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY fee_rate ASC)) AS high_priority,
ROUND(PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY fee_rate ASC)) AS medium_priority,
ROUND(PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY fee_rate ASC)) AS low_priority,
ROUND(PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY fee_rate ASC)) AS no_priority
FROM mempool_txs
WHERE pruned = FALSE
)
UNION
(
WITH txs_grouped AS (
SELECT
(CASE type_id WHEN 6 THEN 1 ELSE type_id END) AS type_id,
fee_rate
FROM mempool_txs
WHERE pruned = FALSE
AND type_id NOT IN (4, 5)
)
SELECT
type_id,
ROUND(PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY fee_rate ASC)) AS high_priority,
ROUND(PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY fee_rate ASC)) AS medium_priority,
ROUND(PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY fee_rate ASC)) AS low_priority,
ROUND(PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY fee_rate ASC)) AS no_priority
FROM txs_grouped
GROUP BY type_id
)
)
SELECT * FROM fees ORDER BY type_id ASC NULLS FIRST
`;
return txFeesQuery;
}

async getMempoolTxList({
limit,
offset,
Expand Down
7 changes: 4 additions & 3 deletions src/test-utils/test-builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
DbBnsNamespace,
DbEventTypeId,
DbFtEvent,
DbMempoolTx,
DbMempoolTxRaw,
DbMicroblockPartial,
DbMinerReward,
Expand Down Expand Up @@ -259,6 +258,8 @@ interface TestMempoolTxArgs {
smart_contract_contract_id?: string;
status?: DbTxStatus;
token_transfer_recipient_address?: string;
token_transfer_amount?: bigint;
token_transfer_memo?: string;
tx_id?: string;
type_id?: DbTxTypeId;
nonce?: number;
Expand Down Expand Up @@ -287,8 +288,8 @@ export function testMempoolTx(args?: TestMempoolTxArgs): DbMempoolTxRaw {
sponsor_address: undefined,
origin_hash_mode: 1,
sender_address: args?.sender_address ?? SENDER_ADDRESS,
token_transfer_amount: 1234n,
token_transfer_memo: '',
token_transfer_amount: args?.token_transfer_amount ?? 1234n,
token_transfer_memo: args?.token_transfer_memo ?? '',
token_transfer_recipient_address: args?.token_transfer_recipient_address ?? RECIPIENT_ADDRESS,
smart_contract_clarity_version: args?.smart_contract_clarity_version,
smart_contract_contract_id: args?.smart_contract_contract_id ?? CONTRACT_ID,
Expand Down
Loading

0 comments on commit ea9c378

Please sign in to comment.