mirror of
https://github.com/alexgo-io/stacks-blockchain-api.git
synced 2026-04-28 21:05:36 +08:00
feat: implement endpoint to get the latest account nonce based off mempool and unanchored or anchored tx data
This commit is contained in:
4
docs/entities/address/address-nonces.example.json
Normal file
4
docs/entities/address/address-nonces.example.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"last_mempool_tx_nonce": 5,
|
||||
"last_executed_tx_nonce": 4
|
||||
}
|
||||
23
docs/entities/address/address-nonces.schema.json
Normal file
23
docs/entities/address/address-nonces.schema.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "The latest nonce values used by an account by inspecting the mempool, microblock transactions, and anchored transactions",
|
||||
"title": "AddressNonces",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"last_mempool_tx_nonce",
|
||||
"last_executed_tx_nonce"
|
||||
],
|
||||
"properties": {
|
||||
"last_mempool_tx_nonce": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "The latest nonce found within mempool transactions sent by this address. Will be null if there are no current mempool transactions for this address."
|
||||
},
|
||||
"last_executed_tx_nonce": {
|
||||
"type": "integer",
|
||||
"nullable": true,
|
||||
"description": "The latest nonce found within transactions sent by this address, including unanchored microblock transactions. Will be null if there are no current transactions for this address."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -998,6 +998,29 @@ paths:
|
||||
example:
|
||||
$ref: ./api/address/get-address-transactions-with-transfers.example.json
|
||||
|
||||
/extended/v1/address/{principal}/nonces:
|
||||
get:
|
||||
summary: Get the latest nonce values used by an account by inspecting the mempool, microblock transactions, and anchored transactions.
|
||||
tags:
|
||||
- Accounts
|
||||
operationId: get_account_nonces
|
||||
parameters:
|
||||
- name: principal
|
||||
in: path
|
||||
description: Stacks address (e.g. `SP31DA6FTSJX2WGTZ69SFY11BH51NZMB0ZW97B5P0`)
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: Success
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: ./entities/address/address-nonces.schema.json
|
||||
example:
|
||||
$ref: ./entities/address/address-nonces.example.json
|
||||
|
||||
/extended/v1/address/{principal}/assets:
|
||||
get:
|
||||
summary: Get account assets
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
MempoolTransactionListResponse,
|
||||
AddressTransactionWithTransfers,
|
||||
AddressTransactionsWithTransfersListResponse,
|
||||
AddressNonces,
|
||||
} from '@stacks/stacks-blockchain-api-types';
|
||||
import { ChainID, cvToString, deserializeCV } from '@stacks/transactions';
|
||||
import { validate } from '../validate';
|
||||
@@ -383,5 +384,21 @@ export function createAddressRouter(db: DataStore, chainId: ChainID): RouterWith
|
||||
res.json(response);
|
||||
});
|
||||
|
||||
router.getAsync('/:stx_address/nonces', async (req, res) => {
|
||||
// get recent asset event associated with address
|
||||
const stxAddress = req.params['stx_address'];
|
||||
if (!isValidPrincipal(stxAddress)) {
|
||||
return res.status(400).json({ error: `invalid STX address "${stxAddress}"` });
|
||||
}
|
||||
const nonces = await db.getAddressNonces({
|
||||
stxAddress,
|
||||
});
|
||||
const results: AddressNonces = {
|
||||
last_executed_tx_nonce: nonces.lastExecutedTxNonce as number,
|
||||
last_mempool_tx_nonce: nonces.lastMempoolTxNonce as number,
|
||||
};
|
||||
res.json(results);
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -631,6 +631,10 @@ export interface DataStore extends DataStoreEventEmitter {
|
||||
offset: number;
|
||||
}): Promise<{ results: DbEvent[]; total: number }>;
|
||||
|
||||
getAddressNonces(args: {
|
||||
stxAddress: string;
|
||||
}): Promise<{ lastExecutedTxNonce: number | null; lastMempoolTxNonce: number | null }>;
|
||||
|
||||
getInboundTransfers(args: {
|
||||
stxAddress: string;
|
||||
limit: number;
|
||||
|
||||
@@ -516,6 +516,12 @@ export class MemoryDataStore
|
||||
throw new Error('not yet implemented');
|
||||
}
|
||||
|
||||
getAddressNonces(args: {
|
||||
stxAddress: string;
|
||||
}): Promise<{ lastExecutedTxNonce: number | null; lastMempoolTxNonce: number | null }> {
|
||||
throw new Error('not yet implemented');
|
||||
}
|
||||
|
||||
getInboundTransfers({
|
||||
stxAddress,
|
||||
}: {
|
||||
|
||||
@@ -1178,6 +1178,35 @@ export class PgDataStore
|
||||
this.emitAddressTxUpdates(data);
|
||||
}
|
||||
|
||||
async getAddressNonces(args: {
|
||||
stxAddress: string;
|
||||
}): Promise<{ lastExecutedTxNonce: number | null; lastMempoolTxNonce: number | null }> {
|
||||
return await this.queryTx(async client => {
|
||||
const executedTxNonce = await client.query<{ nonce: number }>(
|
||||
`
|
||||
SELECT MAX(nonce) nonce
|
||||
FROM txs
|
||||
WHERE sender_address = $1
|
||||
AND canonical = true AND microblock_canonical = true
|
||||
`,
|
||||
[args.stxAddress]
|
||||
);
|
||||
const mempoolTxNonce = await client.query<{ nonce: number }>(
|
||||
`
|
||||
SELECT MAX(nonce) nonce
|
||||
FROM mempool_txs
|
||||
WHERE sender_address = $1
|
||||
AND pruned = false
|
||||
`,
|
||||
[args.stxAddress]
|
||||
);
|
||||
return {
|
||||
lastExecutedTxNonce: executedTxNonce.rowCount === 1 ? executedTxNonce.rows[0].nonce : null,
|
||||
lastMempoolTxNonce: mempoolTxNonce.rowCount === 1 ? mempoolTxNonce.rows[0].nonce : null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getNameCanonical(txId: string, indexBlockHash: string): Promise<FoundOrNot<boolean>> {
|
||||
return this.query(async client => {
|
||||
const queryResult = await client.query(
|
||||
|
||||
Reference in New Issue
Block a user