feat: start returning tx metadata

This commit is contained in:
Rafael Cárdenas
2021-12-21 11:42:54 -06:00
parent f2feefa7b8
commit 3117fe158f
4 changed files with 35 additions and 28 deletions

View File

@@ -470,7 +470,7 @@ export function createAddressRouter(db: DataStore, chainId: ChainID): express.Ro
);
/**
* DEPRECATED: Use `/extended/v1/tokens/nft/holdings`.
* @deprecated Use `/extended/v1/tokens/nft/holdings` instead.
*/
router.get(
'/:stx_address/nft_events',

View File

@@ -13,8 +13,9 @@ import {
isNftMetadataEnabled,
} from '../../../event-stream/tokens-contract-handler';
import { bufferToHexPrefixString, isValidPrincipal } from '../../../helpers';
import { isUnanchoredRequest } from '../../../api/query-helpers';
import { booleanValueForParam, isUnanchoredRequest } from '../../../api/query-helpers';
import { cvToString, deserializeCV } from '@stacks/transactions';
import { getTxFromDataStore } from 'src/api/controllers/db-controller';
const MAX_TOKENS_PER_REQUEST = 200;
const parseTokenQueryLimit = parseLimitQuery({
@@ -29,7 +30,7 @@ export function createTokenRouter(db: DataStore): express.Router {
router.get(
'/nft/holdings',
asyncHandler(async (req, res, next) => {
const principal = req.query.principal ?? '';
const principal = req.query.principal;
if (typeof principal !== 'string' || !isValidPrincipal(principal)) {
res.status(400).json({ error: `Invalid or missing principal` });
return;
@@ -37,6 +38,7 @@ export function createTokenRouter(db: DataStore): express.Router {
const limit = parseTokenQueryLimit(req.query.limit ?? 50);
const offset = parsePagingQueryInput(req.query.offset ?? 0);
const includeUnanchored = isUnanchoredRequest(req, res, next);
const includeTxMetadata = booleanValueForParam(req, res, next, 'tx_metadata');
const { results, total } = await db.getNftHoldings({
principal: principal,
@@ -44,14 +46,23 @@ export function createTokenRouter(db: DataStore): express.Router {
limit: limit,
includeUnanchored: includeUnanchored,
});
const parsedResults = results.map(result => ({
asset_identifier: result.asset_identifier,
value: {
hex: bufferToHexPrefixString(result.value),
repr: cvToString(deserializeCV(result.value)),
},
tx_id: bufferToHexPrefixString(result.tx_id),
}));
const parsedResults = await Promise.all(
results.map(async result => {
const txId = bufferToHexPrefixString(result.tx_id);
return {
asset_identifier: result.asset_identifier,
value: {
hex: bufferToHexPrefixString(result.value),
repr: cvToString(deserializeCV(result.value)),
},
tx_id: txId,
tx: includeTxMetadata
? (await getTxFromDataStore(db, { txId: txId, includeUnanchored: includeUnanchored }))
.result
: undefined,
};
})
);
const response = {
limit: limit,

View File

@@ -810,6 +810,11 @@ export interface DataStore extends DataStoreEventEmitter {
getRawTx(txId: string): Promise<FoundOrNot<RawTxQueryResult>>;
/**
* Returns a list of NFTs owned by the given principal with the optional transaction
* that gave them the ownership of said token.
* @param args - Query arguments
*/
getNftHoldings(args: {
principal: string;
limit: number;
@@ -817,6 +822,9 @@ export interface DataStore extends DataStoreEventEmitter {
includeUnanchored: boolean;
}): Promise<{ results: NftHoldingInfo[]; total: number }>;
/**
* @deprecated Use `getNftHoldings` instead.
*/
getAddressNFTEvent(args: {
stxAddress: string;
blockHeight: number;

View File

@@ -3681,22 +3681,13 @@ export class PgDataStore
const maxBlockHeight = await this.getMaxBlockHeight(client, { includeUnanchored });
const result = await client.query<ContractTxQueryResult>(
`
SELECT ${TX_COLUMNS},
CASE
WHEN txs.type_id = $3 THEN (
SELECT abi
FROM smart_contracts
WHERE smart_contracts.contract_id = txs.contract_call_contract_id
ORDER BY abi != 'null' DESC, canonical DESC, microblock_canonical DESC, block_height DESC
LIMIT 1
)
END as abi
SELECT ${TX_COLUMNS}, ${abiColumn()}
FROM txs
WHERE tx_id = $1 AND block_height <= $2
ORDER BY canonical DESC, microblock_canonical DESC, block_height DESC
LIMIT 1
`,
[hexToBuffer(txId), maxBlockHeight, DbTxTypeId.ContractCall]
[hexToBuffer(txId), maxBlockHeight]
);
if (result.rowCount === 0) {
return { found: false } as const;
@@ -5933,7 +5924,7 @@ export class PgDataStore
includeUnanchored: boolean;
}): Promise<{ results: NftHoldingInfo[]; total: number }> {
return this.queryTx(async client => {
const dbResults = await client.query<NftHoldingInfo & { count: string }>(
const nftResults = await client.query<NftHoldingInfo & { count: number }>(
`
SELECT *, (COUNT(*) OVER())::integer
FROM ${args.includeUnanchored ? 'nft_custody_unanchored' : 'nft_custody'}
@@ -5943,20 +5934,17 @@ export class PgDataStore
`,
[args.principal, args.limit, args.offset]
);
const count: number = dbResults.rows.length > 0 ? parseInt(dbResults.rows[0].count) : 0;
const holdings: NftHoldingInfo[] = dbResults.rows.map(row => ({
const holdings: NftHoldingInfo[] = nftResults.rows.map(row => ({
asset_identifier: row.asset_identifier,
value: row.value,
recipient: row.recipient,
tx_id: row.tx_id,
}));
const count: number = nftResults.rows.length > 0 ? nftResults.rows[0].count : 0;
return { results: holdings, total: count };
});
}
/**
* DEPRECATED: Use `getNftHoldings`.
*/
async getAddressNFTEvent(args: {
stxAddress: string;
limit: number;