diff --git a/docs/api/address/get-address-nft-events.example.json b/docs/api/address/get-address-nft-events.example.json deleted file mode 100644 index 079a2060..00000000 --- a/docs/api/address/get-address-nft-events.example.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "limit": 20, - "offset": 0, - "total": 1, - "nft_events": [ - { - "sender": "none", - "recipient": "ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4", - "asset_identifier": "some-asset", - "value": { "hex": "0x00", "repr": "0" } - } - ] -} diff --git a/docs/api/address/get-address-nft-events.schema.json b/docs/api/address/get-address-nft-events.schema.json deleted file mode 100644 index ea1a083e..00000000 --- a/docs/api/address/get-address-nft-events.schema.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "type": "object", - "title": "AddressNftListResponse", - "additionalProperties": false, - "required": [ - "limit", - "offset", - "total", - "nft_events" - ], - "properties": { - "limit": { - "type": "integer" - }, - "offset": { - "type": "integer" - }, - "total": { - "type": "integer" - }, - "nft_events": { - "type": "array", - "items": { - "$ref": "../../entities/nft-events/nft-event.schema.json" - } - } - } -} diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 45d8921a..c1743f74 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1643,63 +1643,6 @@ paths: example: $ref: ./api/address/get-address-stx-inbound.example.json - /extended/v1/address/{principal}/nft_events: - get: - summary: Get nft events - deprecated: true - description: | - **NOTE:** This endpoint is deprecated in favor of [Non-Fungible Token holdings](#operation/get_nft_holdings). - - Retrieves a list of all nfts owned by an address, contains the clarity value of the identifier of the nft. - tags: - - Accounts - operationId: get_account_nft - parameters: - - name: principal - in: path - description: Stacks address or a Contract identifier - required: true - schema: - type: string - example: "SP31DA6FTSJX2WGTZ69SFY11BH51NZMB0ZW97B5P0" - - name: limit - in: query - description: number of items to return - required: false - schema: - type: integer - - name: offset - in: query - description: number of items to skip - required: false - schema: - type: integer - example: 42000 - - name: unanchored - in: query - description: Include transaction data from unanchored (i.e. unconfirmed) microblocks - required: false - schema: - type: boolean - example: true - default: false - - name: until_block - in: query - description: returned data representing the state up until that point in time, rather than the current block. Note - Use either of the query parameters but not both at a time. - required: false - schema: - type: string - example: 60000 - responses: - 200: - description: Success - content: - application/json: - schema: - $ref: ./api/address/get-address-nft-events.schema.json - example: - $ref: ./api/address/get-address-nft-events.example.json - /v2/accounts/{principal}: get: summary: Get account info diff --git a/src/api/routes/address.ts b/src/api/routes/address.ts index 6ef49885..09159574 100644 --- a/src/api/routes/address.ts +++ b/src/api/routes/address.ts @@ -476,59 +476,6 @@ export function createAddressRouter(db: PgStore, chainId: ChainID): express.Rout }) ); - /** - * @deprecated Use `/extended/v1/tokens/nft/holdings` instead. - */ - router.get( - '/:stx_address/nft_events', - cacheHandler, - asyncHandler(async (req, res, next) => { - // get recent asset event associated with address - const stxAddress = req.params['stx_address']; - validatePrincipal(stxAddress); - - const untilBlock = parseUntilBlockQuery(req, res, next); - const blockHeight = await getBlockHeight(untilBlock, req, res, next, db); - const limit = parseAssetsQueryLimit(req.query.limit ?? 20); - const offset = parsePagingQueryInput(req.query.offset ?? 0); - const includeUnanchored = isUnanchoredRequest(req, res, next); - - const response = await db.getAddressNFTEvent({ - stxAddress, - limit, - offset, - blockHeight, - includeUnanchored, - }); - const nft_events = response.results.map(row => { - const parsedClarityValue = decodeClarityValueToRepr(row.value); - const r: NftEvent = { - sender: row.sender, - recipient: row.recipient, - asset_identifier: row.asset_identifier, - value: { - hex: row.value, - repr: parsedClarityValue, - }, - tx_id: row.tx_id, - block_height: row.block_height, - event_index: row.event_index, - asset_event_type: getAssetEventTypeString(row.asset_event_type_id), - tx_index: row.tx_index, - }; - return r; - }); - const nftListResponse: AddressNftListResponse = { - nft_events: nft_events, - total: response.total, - limit: limit, - offset: offset, - }; - setETagCacheHeaders(res); - res.json(nftListResponse); - }) - ); - router.get( '/:address/mempool', mempoolCacheHandler, diff --git a/src/datastore/pg-store.ts b/src/datastore/pg-store.ts index 40f61ce4..e76fbcc0 100644 --- a/src/datastore/pg-store.ts +++ b/src/datastore/pg-store.ts @@ -3066,56 +3066,6 @@ export class PgStore { return { found: true, result: result[0] } as const; } - /** - * @deprecated Use `getNftHoldings` instead. - */ - async getAddressNFTEvent(args: { - stxAddress: string; - limit: number; - offset: number; - blockHeight: number; - includeUnanchored: boolean; - }): Promise<{ results: AddressNftEventIdentifier[]; total: number }> { - // Join against `nft_custody` materialized view only if we're looking for canonical results. - const result = await this.sql<(AddressNftEventIdentifier & { count: number })[]>` - WITH address_transfers AS ( - SELECT asset_identifier, value, sender, recipient, block_height, microblock_sequence, tx_index, event_index, tx_id, asset_event_type_id - FROM nft_events - WHERE canonical = true AND microblock_canonical = true - AND recipient = ${args.stxAddress} AND block_height <= ${args.blockHeight} - ), - last_nft_transfers AS ( - SELECT DISTINCT ON(asset_identifier, value) asset_identifier, value, recipient - FROM nft_events - WHERE canonical = true AND microblock_canonical = true - AND block_height <= ${args.blockHeight} - ORDER BY asset_identifier, value, block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC - ) - SELECT sender, recipient, asset_identifier, value, event_index, asset_event_type_id, address_transfers.block_height, address_transfers.tx_id, (COUNT(*) OVER())::INTEGER AS count - FROM address_transfers - INNER JOIN ${args.includeUnanchored ? this.sql`last_nft_transfers` : this.sql`nft_custody`} - USING (asset_identifier, value, recipient) - ORDER BY block_height DESC, microblock_sequence DESC, tx_index DESC, event_index DESC - LIMIT ${args.limit} OFFSET ${args.offset} - `; - - const count = result.length > 0 ? result[0].count : 0; - - const nftEvents = result.map(row => ({ - sender: row.sender, - recipient: row.recipient, - asset_identifier: row.asset_identifier, - value: row.value, - block_height: row.block_height, - tx_id: row.tx_id, - event_index: row.event_index, - asset_event_type_id: row.asset_event_type_id, - tx_index: row.tx_index, - })); - - return { results: nftEvents, total: count }; - } - async getTxListDetails({ txIds, includeUnanchored, diff --git a/src/tests/address-tests.ts b/src/tests/address-tests.ts index d0d45e00..57763e87 100644 --- a/src/tests/address-tests.ts +++ b/src/tests/address-tests.ts @@ -2235,7 +2235,6 @@ describe('address tests', () => { '/transactions_with_transfers', '/assets', '/stx_inbound', - '/nft_events', ]; //check for mutually exclusive unachored and and until_block @@ -2257,233 +2256,6 @@ describe('address tests', () => { } }); - test('Success: nft events for address', async () => { - const addr1 = 'ST3J8EVYHVKH6XXPD61EE8XEHW4Y2K83861225AB1'; - const addr2 = 'ST1HB64MAJ1MBV4CQ80GF01DZS4T1DSMX20ADCRA4'; - - const dbBlock: DbBlock = { - block_hash: '0xff', - index_block_hash: '0x1234', - parent_index_block_hash: '0x5678', - parent_block_hash: '0x5678', - parent_microblock_hash: '', - parent_microblock_sequence: 0, - block_height: 1, - burn_block_time: 1594647995, - burn_block_hash: '0x1234', - burn_block_height: 123, - miner_txid: '0x4321', - canonical: true, - execution_cost_read_count: 0, - execution_cost_read_length: 0, - execution_cost_runtime: 0, - execution_cost_write_count: 0, - execution_cost_write_length: 0, - }; - const stxTx: DbTx = { - tx_id: '0x1111000000000000000000000000000000000000000000000000000000000000', - tx_index: 0, - anchor_mode: 3, - nonce: 0, - raw_tx: '0x', - index_block_hash: dbBlock.index_block_hash, - block_hash: dbBlock.block_hash, - block_height: dbBlock.block_height, - burn_block_time: dbBlock.burn_block_time, - parent_burn_block_time: 1626122935, - type_id: DbTxTypeId.TokenTransfer, - token_transfer_amount: 1n, - token_transfer_memo: bufferToHexPrefixString(Buffer.from('hi')), - token_transfer_recipient_address: 'none', - status: 1, - raw_result: '0x0100000000000000000000000000000001', // u1 - canonical: true, - microblock_canonical: true, - microblock_sequence: I32_MAX, - microblock_hash: '', - parent_index_block_hash: dbBlock.parent_index_block_hash, - parent_block_hash: dbBlock.parent_block_hash, - post_conditions: '0x01f5', - fee_rate: 1234n, - sponsored: false, - sponsor_address: undefined, - sender_address: addr1, - origin_hash_mode: 1, - event_count: 10, - execution_cost_read_count: 0, - execution_cost_read_length: 0, - execution_cost_runtime: 0, - execution_cost_write_count: 0, - execution_cost_write_length: 0, - }; - const nftEvents: DbNftEvent[] = []; - for (let i = 0; i < 10; i++) { - nftEvents.push({ - canonical: true, - event_type: DbEventTypeId.NonFungibleTokenAsset, - asset_event_type_id: DbAssetEventTypeId.Transfer, - event_index: 0, - tx_id: stxTx.tx_id, - tx_index: 1, - block_height: dbBlock.block_height, - asset_identifier: 'some-asset', - value: '0x0000000000000000000000000000000000', - recipient: addr1, - sender: 'none', - }); - } - - await db.update({ - block: dbBlock, - microblocks: [], - minerRewards: [], - txs: [ - { - tx: stxTx, - stxLockEvents: [], - stxEvents: [], - ftEvents: [], - nftEvents: nftEvents, - contractLogEvents: [], - smartContracts: [], - names: [], - namespaces: [], - }, - ], - }); - - const limit = 2; - const offset = 0; - // test nft for given addresses - const result = await supertest(api.server).get( - `/extended/v1/address/${addr1}/nft_events?limit=${limit}&offset=${offset}` - ); - expect(result.status).toBe(200); - expect(result.type).toBe('application/json'); - expect(result.body.total).toEqual(10); - expect(result.body.nft_events.length).toEqual(2); - expect(result.body.nft_events[0].recipient).toBe(addr1); - expect(result.body.nft_events[0].tx_id).toBe( - '0x1111000000000000000000000000000000000000000000000000000000000000' - ); - expect(result.body.nft_events[0].block_height).toBe(1); - expect(result.body.nft_events[0].value.repr).toBe('0'); - - const dbBlock2: DbBlock = { - block_hash: '0xffff', - index_block_hash: '0x123466', - parent_index_block_hash: '0x1234', - parent_block_hash: '0xff', - parent_microblock_hash: '', - parent_microblock_sequence: 0, - block_height: 2, - burn_block_time: 1594649995, - burn_block_hash: '0x123456', - burn_block_height: 124, - miner_txid: '0x4321', - canonical: true, - execution_cost_read_count: 0, - execution_cost_read_length: 0, - execution_cost_runtime: 0, - execution_cost_write_count: 0, - execution_cost_write_length: 0, - }; - const stxTx1: DbTx = { - tx_id: '0x1111100000000000000000000000000000000000000000000000000000000001', - tx_index: 0, - anchor_mode: 3, - nonce: 0, - raw_tx: '0x', - index_block_hash: dbBlock2.index_block_hash, - block_hash: dbBlock2.block_hash, - block_height: dbBlock2.block_height, - burn_block_time: dbBlock2.burn_block_time, - parent_burn_block_time: 1626124935, - type_id: DbTxTypeId.TokenTransfer, - token_transfer_amount: 1n, - token_transfer_memo: bufferToHexPrefixString(Buffer.from('hi')), - token_transfer_recipient_address: 'none', - status: 1, - raw_result: '0x0100000000000000000000000000000001', // u1 - canonical: true, - microblock_canonical: true, - microblock_sequence: I32_MAX, - microblock_hash: '', - parent_index_block_hash: dbBlock2.parent_index_block_hash, - parent_block_hash: dbBlock2.parent_block_hash, - post_conditions: '0x01f5', - fee_rate: 1234n, - sponsored: false, - sponsor_address: undefined, - sender_address: addr2, - origin_hash_mode: 1, - event_count: 1, - execution_cost_read_count: 0, - execution_cost_read_length: 0, - execution_cost_runtime: 0, - execution_cost_write_count: 0, - execution_cost_write_length: 0, - }; - const nftEvent2: DbNftEvent = { - canonical: true, - event_type: DbEventTypeId.NonFungibleTokenAsset, - asset_event_type_id: DbAssetEventTypeId.Transfer, - event_index: 1, - tx_id: stxTx1.tx_id, - tx_index: 2, - block_height: dbBlock2.block_height, - asset_identifier: 'some-asset', - value: '0x0000000000000000000000000000000000', - recipient: addr2, - sender: 'none', - }; - await db.update({ - block: dbBlock2, - microblocks: [], - minerRewards: [], - txs: [ - { - tx: stxTx1, - stxLockEvents: [], - stxEvents: [], - ftEvents: [], - nftEvents: [nftEvent2], - contractLogEvents: [], - smartContracts: [], - names: [], - namespaces: [], - }, - ], - }); - - const result1 = await supertest(api.server).get(`/extended/v1/address/${addr2}/nft_events`); - expect(result1.status).toBe(200); - expect(result1.type).toBe('application/json'); - expect(result1.body.total).toEqual(1); - expect(result1.body.nft_events.length).toEqual(1); - expect(result1.body.nft_events[0].recipient).toBe(addr2); - expect(result1.body.nft_events[0].tx_id).toBe( - '0x1111100000000000000000000000000000000000000000000000000000000001' - ); - expect(result1.body.nft_events[0].block_height).toBe(2); - expect(result.body.nft_events[0].value.repr).toBe('0'); - - //check ownership for addr - const result2 = await supertest(api.server).get(`/extended/v1/address/${addr1}/nft_events`); - expect(result2.status).toBe(200); - expect(result2.type).toBe('application/json'); - expect(result2.body.nft_events.length).toEqual(0); - expect(result2.body.total).toEqual(0); - }); - - test('nft invalid address', async () => { - const result = await supertest(api.server).get( - `/extended/v1/address/invalid-address/nft_events` - ); - expect(result.status).toBe(400); - expect(result.type).toBe('application/json'); - }); - test('/transactions materialized view separates anchored and unanchored counts correctly', async () => { const contractId = 'SP3D6PV2ACBPEKYJTCMH7HEN02KP87QSP8KTEH335.megapont-ape-club-nft';