diff --git a/docs/api/tokens/get-non-fungible-token-holdings-tx-metadata.example.schema.json b/docs/api/tokens/get-non-fungible-token-holdings-tx-metadata.example.schema.json index 501870a2..4d7721c6 100644 --- a/docs/api/tokens/get-non-fungible-token-holdings-tx-metadata.example.schema.json +++ b/docs/api/tokens/get-non-fungible-token-holdings-tx-metadata.example.schema.json @@ -9,6 +9,7 @@ "hex": "0x0100000000000000000000000000000803", "repr": "u2051" }, + "block_height": 36442, "tx": { "tx_id": "0x12e6f88724d0e630de26c376f172cf1781e25435e2b4ee54d36a949b244e429c", "nonce": 7, @@ -67,6 +68,7 @@ "hex": "0x01000000000000000000000000000004f3", "repr": "u1267" }, + "block_height": 37477, "tx": { "tx_id": "0xddc464c5e20a78dc5ac351913e0d9b9cce76fc955cc4599e0cccad960998f130", "nonce": 10, @@ -125,6 +127,7 @@ "hex": "0x0c00000002046e616d65020000000672616661656c096e616d6573706163650200000003627463", "repr": "(tuple (name 0x72616661656c) (namespace 0x627463))" }, + "block_height": 17250, "tx": { "tx_id": "0x0153a41ed24a0e1d32f66ea98338df09f942571ca66359e28bdca79ccd0305cf", "nonce": 4, diff --git a/docs/api/tokens/get-non-fungible-token-holdings.example.schema.json b/docs/api/tokens/get-non-fungible-token-holdings.example.schema.json index 2d2b51d2..d1cb2172 100644 --- a/docs/api/tokens/get-non-fungible-token-holdings.example.schema.json +++ b/docs/api/tokens/get-non-fungible-token-holdings.example.schema.json @@ -9,6 +9,7 @@ "hex": "0x0100000000000000000000000000000803", "repr": "u2051" }, + "block_height": 36442, "tx_id": "0x12e6f88724d0e630de26c376f172cf1781e25435e2b4ee54d36a949b244e429c" }, { @@ -17,6 +18,7 @@ "hex": "0x01000000000000000000000000000004f3", "repr": "u1267" }, + "block_height": 37477, "tx_id": "0xddc464c5e20a78dc5ac351913e0d9b9cce76fc955cc4599e0cccad960998f130" }, { @@ -25,6 +27,7 @@ "hex": "0x0c00000002046e616d65020000000672616661656c096e616d6573706163650200000003627463", "repr": "(tuple (name 0x72616661656c) (namespace 0x627463))" }, + "block_height": 17250, "tx_id": "0x0153a41ed24a0e1d32f66ea98338df09f942571ca66359e28bdca79ccd0305cf" } ] diff --git a/docs/entities/tokens/non-fungible-token-holding-0-tx-id.schema.json b/docs/entities/tokens/non-fungible-token-holding-0-tx-id.schema.json index 932307a7..90728bb7 100644 --- a/docs/entities/tokens/non-fungible-token-holding-0-tx-id.schema.json +++ b/docs/entities/tokens/non-fungible-token-holding-0-tx-id.schema.json @@ -3,7 +3,7 @@ "type": "object", "title": "NonFungibleTokenHoldingWithTxId", "description": "Ownership of a Non-Fungible Token", - "required": ["asset_identifier", "value", "tx_id"], + "required": ["asset_identifier", "value", "tx_id", "block_height"], "additionalProperties": false, "properties": { "asset_identifier": { @@ -25,6 +25,9 @@ } } }, + "block_height": { + "type": "number" + }, "tx_id": { "type": "string" } diff --git a/docs/entities/tokens/non-fungible-token-holding-1-tx-metadata.schema.json b/docs/entities/tokens/non-fungible-token-holding-1-tx-metadata.schema.json index b6ec9bc9..95880c3a 100644 --- a/docs/entities/tokens/non-fungible-token-holding-1-tx-metadata.schema.json +++ b/docs/entities/tokens/non-fungible-token-holding-1-tx-metadata.schema.json @@ -3,7 +3,7 @@ "type": "object", "title": "NonFungibleTokenHoldingWithTxMetadata", "description": "Ownership of a Non-Fungible Token with transaction metadata", - "required": ["asset_identifier", "value", "tx"], + "required": ["asset_identifier", "value", "tx", "block_height"], "additionalProperties": false, "properties": { "asset_identifier": { @@ -25,6 +25,9 @@ } } }, + "block_height": { + "type": "number" + }, "tx": { "$ref": "../transactions/transaction.schema.json" } diff --git a/docs/generated.d.ts b/docs/generated.d.ts index e2d24874..c912168a 100644 --- a/docs/generated.d.ts +++ b/docs/generated.d.ts @@ -3253,6 +3253,7 @@ export interface NonFungibleTokenHoldingWithTxId { */ repr: string; }; + block_height: number; tx_id: string; } /** @@ -3273,6 +3274,7 @@ export interface NonFungibleTokenHoldingWithTxMetadata { */ repr: string; }; + block_height: number; tx: Transaction; } /** diff --git a/src/api/routes/tokens/tokens.ts b/src/api/routes/tokens/tokens.ts index dbf51739..04a48dda 100644 --- a/src/api/routes/tokens/tokens.ts +++ b/src/api/routes/tokens/tokens.ts @@ -82,6 +82,7 @@ export function createTokenRouter(db: DataStore): express.Router { hex: bufferToHexPrefixString(result.nft_holding_info.value), repr: parsedClarityValue, }, + block_height: result.nft_holding_info.block_height, }; if (includeTxMetadata && result.tx) { return { ...parsedNftData, tx: parseDbTx(result.tx) }; diff --git a/src/datastore/common.ts b/src/datastore/common.ts index d6c880ae..43714d5e 100644 --- a/src/datastore/common.ts +++ b/src/datastore/common.ts @@ -363,6 +363,7 @@ export interface NftHoldingInfo { // TODO(perf): use hex string since that is what we already get from deserializing event payloads // and from the pg-node adapter (all the pg js libs use text mode rather than binary mode) value: Buffer; + block_height: number; recipient: string; tx_id: Buffer; } diff --git a/src/datastore/postgres-store.ts b/src/datastore/postgres-store.ts index 919ddc8b..2ed3baad 100644 --- a/src/datastore/postgres-store.ts +++ b/src/datastore/postgres-store.ts @@ -6544,6 +6544,7 @@ export class PgDataStore value: row.value, recipient: row.recipient, tx_id: row.tx_id, + block_height: row.block_height, }, tx: args.includeTxMetadata ? this.parseTxQueryResult(row) : undefined, })), diff --git a/src/tests/token-tests.ts b/src/tests/token-tests.ts index 77514617..c3414b62 100644 --- a/src/tests/token-tests.ts +++ b/src/tests/token-tests.ts @@ -51,6 +51,7 @@ describe('/extended/v1/tokens tests', () => { expect(result1.total).toEqual(1); expect(result1.results[0].asset_identifier).toEqual(assetId1); expect(result1.results[0].tx_id).toEqual('0x5454'); + expect(result1.results[0].block_height).toEqual(block1.block.block_height); // Request: with metadata const request2 = await supertest(api.server).get( @@ -62,6 +63,7 @@ describe('/extended/v1/tokens tests', () => { expect(result2.total).toEqual(1); expect(result2.results[0].asset_identifier).toEqual(assetId1); expect(result2.results[0].tx.tx_id).toEqual('0x5454'); + expect(result2.results[0].block_height).toEqual(block1.block.block_height); // Mint another NFT const block2 = new TestBlockBuilder({ @@ -89,6 +91,7 @@ describe('/extended/v1/tokens tests', () => { expect(result3.total).toEqual(2); expect(result3.results[0].asset_identifier).toEqual(assetId2); expect(result3.results[0].tx_id).toEqual('0x5464'); + expect(result3.results[0].block_height).toEqual(block2.block.block_height); // Request: filtered by asset id const request4 = await supertest(api.server).get( @@ -100,6 +103,7 @@ describe('/extended/v1/tokens tests', () => { expect(result4.total).toEqual(1); // 1 result only expect(result4.results[0].asset_identifier).toEqual(assetId2); expect(result4.results[0].tx_id).toEqual('0x5464'); + expect(result4.results[0].block_height).toEqual(block2.block.block_height); // Transfer one NFT from addr1 to addr2 const block3 = new TestBlockBuilder({ @@ -127,6 +131,7 @@ describe('/extended/v1/tokens tests', () => { expect(result5.total).toEqual(1); expect(result5.results[0].asset_identifier).toEqual(assetId1); expect(result5.results[0].tx_id).toEqual('0x5454'); + expect(result5.results[0].block_height).toEqual(block1.block.block_height); // Request: addr2 has the other const request6 = await supertest(api.server).get( @@ -138,6 +143,7 @@ describe('/extended/v1/tokens tests', () => { expect(result6.total).toEqual(1); expect(result6.results[0].asset_identifier).toEqual(assetId2); expect(result6.results[0].tx_id).toEqual('0x5484'); + expect(result6.results[0].block_height).toEqual(block3.block.block_height); // Transfer NFT from addr2 to addr3 in microblock const microblock1 = new TestMicroblockStreamBuilder()