diff --git a/package.json b/package.json index 1990ea648..41ecc6e97 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@reduxjs/toolkit": "1.9.7", "@sentry/nextjs": "8.26.0", "@stacks/auth": "6.15.0", - "@stacks/blockchain-api-client": "7.12.0", + "@stacks/blockchain-api-client": "8.2.2", "@stacks/common": "6.15.1-pr.0bcf867e.0+0bcf867e", "@stacks/connect": "7.7.1", "@stacks/connect-react": "22.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c40c3e910..efa63ca93 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,8 +85,8 @@ dependencies: specifier: 6.15.0 version: 6.15.0 '@stacks/blockchain-api-client': - specifier: 7.12.0 - version: 7.12.0 + specifier: 8.2.2 + version: 8.2.2 '@stacks/common': specifier: 6.15.1-pr.0bcf867e.0+0bcf867e version: 6.15.1-pr.0bcf867e.0 @@ -499,7 +499,7 @@ packages: '@babel/traverse': 7.23.4 '@babel/types': 7.23.4 convert-source-map: 2.0.0 - debug: 4.3.4 + debug: 4.3.6 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2185,7 +2185,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.4 '@babel/types': 7.23.4 - debug: 4.3.4 + debug: 4.3.6 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -6896,19 +6896,16 @@ packages: - encoding dev: false - /@stacks/blockchain-api-client@7.12.0: - resolution: {integrity: sha512-N0Pxc8ZK0FQe4CtabDvs2oj2+9E0nrcCt3A3bx8QgZUWS7DPtIaqX3gK6aDSu0k8kD/v5lQQyVrISbNDAeygqQ==} + /@stacks/blockchain-api-client@8.2.2: + resolution: {integrity: sha512-bT5w4778hEkPaz+j6tXdwccBl5YTANol5A2Jdr4J5SMLclG8G4kHG8vaXZx6lufewUkvnK92woHd/q44vYVVPQ==} dependencies: - '@stacks/stacks-blockchain-api-types': 7.12.0 - '@types/ws': 7.4.7 - cross-fetch: 3.1.5 + '@types/node': 20.14.14 eventemitter3: 4.0.7 jsonrpc-lite: 2.2.0 - socket.io-client: 4.7.3 - ws: 8.16.0 + openapi-fetch: 0.10.6 + socket.io-client: 4.8.1 transitivePeerDependencies: - bufferutil - - encoding - supports-color - utf-8-validate dev: false @@ -6927,15 +6924,8 @@ packages: '@types/node': 18.18.12 dev: false - /@stacks/common@6.15.1-pr.e36e2015.5: - resolution: {integrity: sha512-TMpyBvtYU/Irc9u9PDb8mqKSQHVgGQKIZvzZF58Rg60xQ038bJ8JEqmxrGnP5sEPbyUEBH3BX5fcvkRCokww6g==} - dependencies: - '@types/bn.js': 5.1.5 - '@types/node': 18.18.12 - dev: false - - /@stacks/common@6.8.1: - resolution: {integrity: sha512-ewL9GLZNQYa5a/3K4xSHlHIgHkD4rwWW/QEaPId8zQIaL+1O9qCaF4LX9orNQeOmEk8kvG0x2xGV54fXKCZeWQ==} + /@stacks/common@6.16.0: + resolution: {integrity: sha512-PnzvhrdGRMVZvxTulitlYafSK4l02gPCBBoI9QEoTqgSnv62oaOXhYAUUkTMFKxdHW1seVEwZsrahuXiZPIAwg==} dependencies: '@types/bn.js': 5.1.5 '@types/node': 18.18.12 @@ -6968,7 +6958,7 @@ packages: '@stacks/connect-ui': 6.4.1 '@stacks/network': 6.13.0 '@stacks/profile': 6.9.0 - '@stacks/transactions': 6.9.0 + '@stacks/transactions': 6.15.0 jsontokens: 4.0.1 transitivePeerDependencies: - encoding @@ -7021,10 +7011,10 @@ packages: - encoding dev: false - /@stacks/network@6.15.1-pr.e36e2015.5: - resolution: {integrity: sha512-lk7YnXi6B/ycTvLlZdoPUv1nStTxhNfZ46IEu/eTYBdXYbw0c8JdNT62GEyaAYyXnxg/MFKNqGxRgJp2oYArsg==} + /@stacks/network@6.17.0: + resolution: {integrity: sha512-numHbfKjwco/rbkGPOEz8+FcJ2nBnS/tdJ8R422Q70h3SiA9eqk9RjSzB8p4JP8yW1SZvW+eihADHfMpBuZyfw==} dependencies: - '@stacks/common': 6.15.1-pr.e36e2015.5 + '@stacks/common': 6.16.0 cross-fetch: 3.1.8 transitivePeerDependencies: - encoding @@ -7052,9 +7042,9 @@ packages: /@stacks/profile@6.9.0: resolution: {integrity: sha512-sIR60DsAHi8C6zGqKqSe1r2hXTMHgwrJkX3fAaP3de40KeplZ2bkE+0B83yismEeU2baNc+AukyVvWJv0PfP0A==} dependencies: - '@stacks/common': 6.8.1 + '@stacks/common': 6.13.0 '@stacks/network': 6.13.0 - '@stacks/transactions': 6.9.0 + '@stacks/transactions': 6.15.0 jsontokens: 4.0.1 schema-inspector: 2.0.0 zone-file: 2.0.0-beta.3 @@ -7064,6 +7054,7 @@ packages: /@stacks/stacks-blockchain-api-types@7.12.0: resolution: {integrity: sha512-y76wTzp472m66qHEVvWErxopUqj8ZHYds+odyIjDciTG/u4YG1DwohaCPzgCP3X1fiB6qZ82wMt1VlivYwiuMQ==} + dev: true /@stacks/transactions@6.15.0: resolution: {integrity: sha512-P6XKDcqqycPy+KBJBw8+5N+u57D8moJN7msYdde1gYXERmvOo9ht/MNREWWQ7SAM7Nlhau5mpezCdYCzXOCilQ==} @@ -7084,20 +7075,7 @@ packages: '@noble/hashes': 1.1.5 '@noble/secp256k1': 1.7.1 '@stacks/common': 6.15.1-pr.0bcf867e.0 - '@stacks/network': 6.15.1-pr.e36e2015.5 - c32check: 2.0.0 - lodash.clonedeep: 4.5.0 - transitivePeerDependencies: - - encoding - dev: false - - /@stacks/transactions@6.9.0: - resolution: {integrity: sha512-hSs9+0Ew++GwMZMgPObOx0iVCQRxkiCqI+DHdPEikAmg2utpyLh2/txHOjfSIkQHvcBfJJ6O5KphmxDP4gUqiA==} - dependencies: - '@noble/hashes': 1.1.5 - '@noble/secp256k1': 1.7.1 - '@stacks/common': 6.8.1 - '@stacks/network': 6.13.0 + '@stacks/network': 6.17.0 c32check: 2.0.0 lodash.clonedeep: 4.5.0 transitivePeerDependencies: @@ -8186,6 +8164,12 @@ packages: undici-types: 5.26.5 dev: false + /@types/node@20.14.14: + resolution: {integrity: sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==} + dependencies: + undici-types: 5.26.5 + dev: false + /@types/node@20.9.4: resolution: {integrity: sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==} dependencies: @@ -8346,12 +8330,6 @@ packages: resolution: {integrity: sha512-+33x29mg+ecU88ODdWpqaie2upIuRkhujVLA7TuJjM823cNMbeggfI6NhxewaRaRF8dy+g33e4uIg/m5Mb3xDQ==} dev: true - /@types/ws@7.4.7: - resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} - dependencies: - '@types/node': 20.9.4 - dev: false - /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} dev: true @@ -8818,7 +8796,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.4 + debug: 4.3.6 transitivePeerDependencies: - supports-color @@ -10013,14 +9991,6 @@ packages: sha.js: 2.4.11 dev: true - /cross-fetch@3.1.5: - resolution: {integrity: sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==} - dependencies: - node-fetch: 2.6.7 - transitivePeerDependencies: - - encoding - dev: false - /cross-fetch@3.1.8: resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} dependencies: @@ -10613,14 +10583,14 @@ packages: objectorarray: 1.0.5 dev: true - /engine.io-client@6.5.3: - resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==} + /engine.io-client@6.6.2: + resolution: {integrity: sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==} dependencies: '@socket.io/component-emitter': 3.1.0 - debug: 4.3.4 + debug: 4.3.6 engine.io-parser: 5.2.1 - ws: 8.11.0 - xmlhttprequest-ssl: 2.0.0 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil - supports-color @@ -12620,7 +12590,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.4 + debug: 4.3.6 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -13880,18 +13850,6 @@ packages: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} dev: true - /node-fetch@2.6.7: - resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - whatwg-url: 5.0.0 - dev: false - /node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -14144,6 +14102,16 @@ packages: is-wsl: 2.2.0 dev: true + /openapi-fetch@0.10.6: + resolution: {integrity: sha512-6xXfvIEL/POtLGOaFPsp3O+pDe+J3DZYxbD9BrsQHXOTeNK8z/gsWHT6adUy1KcpQOhmkerMzlQrJM6DbN55dQ==} + dependencies: + openapi-typescript-helpers: 0.0.11 + dev: false + + /openapi-typescript-helpers@0.0.11: + resolution: {integrity: sha512-xofUHlVFq+BMquf3nh9I8N2guHckW6mrDO/F3kaFgrL7MGbjldDnQ9TIT+rkH/+H0LiuO+RuZLnNmsJwsjwUKg==} + dev: false + /opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true @@ -15922,13 +15890,13 @@ packages: is-fullwidth-code-point: 3.0.0 dev: true - /socket.io-client@4.7.3: - resolution: {integrity: sha512-nU+ywttCyBitXIl9Xe0RSEfek4LneYkJxCeNnKCuhwoH4jGXO1ipIUw/VA/+Vvv2G1MTym11fzFC0SxkrcfXDw==} + /socket.io-client@4.8.1: + resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} engines: {node: '>=10.0.0'} dependencies: '@socket.io/component-emitter': 3.1.0 - debug: 4.3.4 - engine.io-client: 6.5.3 + debug: 4.3.6 + engine.io-client: 6.6.2 socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil @@ -15941,7 +15909,7 @@ packages: engines: {node: '>=10.0.0'} dependencies: '@socket.io/component-emitter': 3.1.0 - debug: 4.3.4 + debug: 4.3.6 transitivePeerDependencies: - supports-color dev: false @@ -17271,21 +17239,21 @@ packages: optional: true dev: false - /ws@8.11.0: - resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==} + /ws@8.14.2: + resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 + utf-8-validate: '>=5.0.2' peerDependenciesMeta: bufferutil: optional: true utf-8-validate: optional: true - dev: false + dev: true - /ws@8.14.2: - resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -17297,8 +17265,8 @@ packages: optional: true dev: true - /ws@8.16.0: - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} + /ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -17308,6 +17276,7 @@ packages: optional: true utf-8-validate: optional: true + dev: false /xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} @@ -17318,8 +17287,8 @@ packages: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} dev: true - /xmlhttprequest-ssl@2.0.0: - resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==} + /xmlhttprequest-ssl@2.1.2: + resolution: {integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==} engines: {node: '>=0.4.0'} dev: false diff --git a/src/api/getApiClient.ts b/src/api/getApiClient.ts new file mode 100644 index 000000000..b7f2baac3 --- /dev/null +++ b/src/api/getApiClient.ts @@ -0,0 +1,20 @@ +import { createClient } from '@stacks/blockchain-api-client'; + +import packageJson from '../../package.json'; +import { RELEASE_TAG_NAME } from '../common/constants/env'; + +export const getApiClient = (baseUrl: string) => { + const apiClient = createClient({ + baseUrl, + }); + + apiClient.use({ + onRequest({ request }) { + request.headers.set('x-hiro-product', 'explorer'); + request.headers.set('x-hiro-version', RELEASE_TAG_NAME || packageJson.version); + return request; + }, + }); + + return apiClient; +}; diff --git a/src/api/getErrorMessage.ts b/src/api/getErrorMessage.ts new file mode 100644 index 000000000..ef6df2cc4 --- /dev/null +++ b/src/api/getErrorMessage.ts @@ -0,0 +1,3 @@ +export function getErrorMessage(error: any) { + return error?.message || 'Something went wrong! Please try again later.'; +} diff --git a/src/api/useApiClient.ts b/src/api/useApiClient.ts new file mode 100644 index 000000000..821bc2727 --- /dev/null +++ b/src/api/useApiClient.ts @@ -0,0 +1,7 @@ +import { useGlobalContext } from '../common/context/useGlobalContext'; +import { getApiClient } from './getApiClient'; + +export function useApiClient() { + const baseUrl = useGlobalContext().activeNetworkKey; + return getApiClient(baseUrl); +} diff --git a/src/api/useGetQueryFn.ts b/src/api/useGetQueryFn.ts new file mode 100644 index 000000000..6b8b7fe29 --- /dev/null +++ b/src/api/useGetQueryFn.ts @@ -0,0 +1,20 @@ +import { useApiClient } from './useApiClient'; + +export function useGetQueryFn(path: string) { + const apiClient = useApiClient(); + return apiClient.GET('/extended/v2/blocks/{height_or_hash}/transactions', { + params: { + path: { height_or_hash: 2000 }, + query: { limit: 20, offset: 100 }, + }, + }); +} + +export function useGetTransactionsQueryFn() { + return useApiClient().GET('/extended/v2/blocks/{height_or_hash}/transactions', { + params: { + path: { height_or_hash: 2000 }, + query: { limit: 20, offset: 100 }, + }, + }); +} diff --git a/src/api/useQuery.ts b/src/api/useQuery.ts new file mode 100644 index 000000000..6fcdff9d3 --- /dev/null +++ b/src/api/useQuery.ts @@ -0,0 +1,12 @@ +import { + QueryKey, + UseQueryOptions, + UseQueryResult, + useQuery as tanstackUseQuery, +} from '@tanstack/react-query'; + +export function useQuery( + options: UseQueryOptions +): UseQueryResult { + return tanstackUseQuery(options); +} diff --git a/src/app/_components/BlockList/data/useAverageBlockTimes.ts b/src/app/_components/BlockList/data/useAverageBlockTimes.ts index 36c0e6401..d70973e2b 100644 --- a/src/app/_components/BlockList/data/useAverageBlockTimes.ts +++ b/src/app/_components/BlockList/data/useAverageBlockTimes.ts @@ -1,6 +1,7 @@ import { UseSuspenseQueryResult, useSuspenseQuery } from '@tanstack/react-query'; -import { useApi } from '../../../../common/api/useApi'; +import { getErrorMessage } from '../../../../api/getErrorMessage'; +import { useApiClient } from '../../../../api/useApiClient'; const AVERAGE_BLOCK_TIMES_QUERY_KEY = '/extended/v2/blocks/average-times'; @@ -12,10 +13,16 @@ interface AverageBlockTimesResponse { } export function useSuspenseAverageBlockTimes(): UseSuspenseQueryResult { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseQuery({ queryKey: [AVERAGE_BLOCK_TIMES_QUERY_KEY], - queryFn: () => api.blocksApi.getAverageBlockTimes(), + queryFn: async () => { + const { data, error } = await apiClient.GET('/extended/v2/blocks/average-times'); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: 30 * 60 * 1000, }); } diff --git a/src/app/address/[principal]/PageClient.tsx b/src/app/address/[principal]/PageClient.tsx index ddb78aa73..8fc7bc1ee 100644 --- a/src/app/address/[principal]/PageClient.tsx +++ b/src/app/address/[principal]/PageClient.tsx @@ -25,7 +25,7 @@ export function AddressPageLayout(props: GridProps) { } export default function AddressPage({ params: { principal } }: any) { - const { data: balance } = useSuspenseAccountBalance(principal, { refetchOnWindowFocus: true }); + const { data: balance } = useSuspenseAccountBalance(principal); const { data: nonces } = useAddressNonces({ address: principal }); const hasTokenBalances = hasTokenBalance(balance); diff --git a/src/app/block/[hash]/PageClient.tsx b/src/app/block/[hash]/PageClient.tsx index bdccd01e4..fde2ac2d2 100644 --- a/src/app/block/[hash]/PageClient.tsx +++ b/src/app/block/[hash]/PageClient.tsx @@ -35,10 +35,9 @@ const BlockTxsList = dynamic( ); export default function BlockPage({ params: { hash } }: any) { - const { data: block } = useSuspenseBlockByHeightOrHash(hash, { refetchOnWindowFocus: true }); + const { data: block } = useSuspenseBlockByHeightOrHash(hash); const title = (block && `STX Block #${block.height.toLocaleString()}`) || ''; const { isOpen, onToggle, onClose } = useDisclosure(); - // const { data: signerMetricsBlock } = useSignerMetricsBlock(hash); return ( <> {title} @@ -65,46 +64,6 @@ export default function BlockPage({ params: { hash } }: any) { value={{block.tenure_height}} /> )} - {/* - {signerMetricsBlock?.signer_data - ? `${signerMetricsBlock?.signer_data?.average_response_time_ms / 1_000}s` - : '-'} - - } - /> - - {signerMetricsBlock?.signer_data - ? `${signerMetricsBlock?.signer_data?.accepted_weight}%` - : '-'} - - } - /> - - {signerMetricsBlock?.signer_data - ? `${signerMetricsBlock?.signer_data?.rejected_weight}%` - : '-'} - - } - /> - - {signerMetricsBlock?.signer_data - ? `${signerMetricsBlock?.signer_data?.missing_weight}%` - : '-'} - - } - /> */} {!block.canonical ? ( ; totalTxCount: number; }) { const pieData = Object.entries(filteredTxTypeCounts) diff --git a/src/app/transactions/MempoolFeeStats.tsx b/src/app/transactions/MempoolFeeStats.tsx index d2b538ccc..cc4b566c2 100644 --- a/src/app/transactions/MempoolFeeStats.tsx +++ b/src/app/transactions/MempoolFeeStats.tsx @@ -7,8 +7,7 @@ import { } from '@phosphor-icons/react'; import { useMemo, useState } from 'react'; -import { MempoolFeePriorities } from '@stacks/blockchain-api-client/src/generated/models'; -import { MempoolFeePrioritiesAll } from '@stacks/blockchain-api-client/src/generated/models/MempoolFeePrioritiesAll'; +import { MempoolFeePriorities } from '@stacks/stacks-blockchain-api-types/generated'; import { Card } from '../../common/components/Card'; import { getTxTypeIcon } from '../../common/components/TxIcon'; @@ -30,7 +29,7 @@ import { mapTransactionTypeToFilterValue, } from './TransactionTypeFilterMenu'; -export const getFeePriorityIcon = (priority: keyof MempoolFeePrioritiesAll) => { +export const getFeePriorityIcon = (priority: keyof MempoolFeePriorities['all']) => { switch (priority) { case 'no_priority': return ; @@ -52,14 +51,16 @@ function MempoolFeePriorityCard({ txTypeFilter, }: { mempoolFeeResponse: MempoolFeePriorities; - priority: keyof MempoolFeePrioritiesAll; + priority: keyof MempoolFeePriorities['all']; stxPrice: number; txTypeFilter: TransactionTypeFilterTypes; } & FlexProps) { const borderColor = useColorModeValue('slate.200', 'slate.800'); const isTxTypeFiltered = txTypeFilter !== TransactionTypeFilterTypes.AverageForAllTransactions; const mempoolFeeAll = isTxTypeFiltered - ? mempoolFeeResponse?.[mapTransactionTypeToFilterValue(txTypeFilter)]?.[priority] || 0 + ? mempoolFeeResponse?.[ + mapTransactionTypeToFilterValue(txTypeFilter) as keyof MempoolFeePriorities + ]?.[priority] || 0 : mempoolFeeResponse?.all?.[priority] || 0; const mempoolFeeTokenTransfer = mempoolFeeResponse?.token_transfer?.[priority] || 0; const mempoolFeeContractCall = mempoolFeeResponse?.contract_call?.[priority] || 0; @@ -239,7 +240,7 @@ export function MempoolFeeStats({ tokenPrice }: { tokenPrice: TokenPrice }) { {Object.entries(filteredTxTypeCounts).map(([key, value]) => { - const icon = getTxTypeIcon(key as keyof typeof filteredTxTypeCounts); + const icon = getTxTypeIcon(key as any); const text = key === 'token_transfer' ? 'Token transfer' diff --git a/src/app/transactions/TransactionTypeFilterMenu.tsx b/src/app/transactions/TransactionTypeFilterMenu.tsx index 6ff93f2ab..7c68a856a 100644 --- a/src/app/transactions/TransactionTypeFilterMenu.tsx +++ b/src/app/transactions/TransactionTypeFilterMenu.tsx @@ -1,6 +1,6 @@ import { useCallback, useMemo } from 'react'; -import { MempoolFeePriorities } from '@stacks/blockchain-api-client'; +import { MempoolFeePriorities } from '@stacks/stacks-blockchain-api-types/generated'; import { FilterMenu } from '../../common/components/FilterMenu'; diff --git a/src/common/api/client.ts b/src/common/api/client.ts deleted file mode 100644 index 786fa3c92..000000000 --- a/src/common/api/client.ts +++ /dev/null @@ -1,87 +0,0 @@ -'use client'; - -import { TokensApi } from '@hirosystems/token-metadata-api-client'; -import { Configuration as TokenMetadataApiConfiguration } from '@hirosystems/token-metadata-api-client/dist/configuration'; - -import { - AccountsApi, - BlocksApi, - BurnBlocksApi, - Configuration, - FaucetsApi, - FeesApi, - InfoApi, - MempoolApi, - MicroblocksApi, - Middleware, - NonFungibleTokensApi, - RequestContext, - RosettaApi, - SearchApi, - SmartContractsApi, - TransactionsApi, -} from '@stacks/blockchain-api-client'; - -import { MICROBLOCKS_ENABLED } from '../constants/constants'; -import { fetcher as fetchApi } from './fetch'; - -export function apiClients( - config: Configuration, - tokenMetadataApiConfig?: TokenMetadataApiConfiguration -) { - const smartContractsApi = new SmartContractsApi(config); - const accountsApi = new AccountsApi(config); - const infoApi = new InfoApi(config); - const transactionsApi = new TransactionsApi(config); - const microblocksApi = new MicroblocksApi(config); - const blocksApi = new BlocksApi(config); - const burnBlocksApi = new BurnBlocksApi(config); - const faucetsApi = new FaucetsApi(config); - const feesApi = new FeesApi(config); - const searchApi = new SearchApi(config); - const rosettaApi = new RosettaApi(config); - const nonFungibleTokensApi = new NonFungibleTokensApi(config); - const mempoolApi = new MempoolApi(config); - const tokenMetadataApi = tokenMetadataApiConfig - ? new TokensApi(tokenMetadataApiConfig) - : undefined; - - return { - smartContractsApi, - accountsApi, - infoApi, - transactionsApi, - microblocksApi, - blocksApi, - burnBlocksApi, - faucetsApi, - feesApi, - searchApi, - rosettaApi, - nonFungibleTokensApi, - mempoolApi, - tokenMetadataApi, - config, - }; -} - -// this is used to enable automatic passing of `unanchored=true` to all requests -const unanchoredMiddleware: Middleware = { - pre: (context: RequestContext) => { - const url = new URL(context.url); - if (!url.toString().includes('/v2')) url.searchParams.set('unanchored', 'true'); - return Promise.resolve({ - init: context.init, - url: url.toString(), - }); - }, -}; -export function createConfig(basePath?: string) { - const middleware: Middleware[] = []; - if (MICROBLOCKS_ENABLED) middleware.push(unanchoredMiddleware); - return new Configuration({ - basePath, - fetchApi, - middleware, - }); -} diff --git a/src/common/api/useApi.ts b/src/common/api/useApi.ts index 66156f453..80ea2ba60 100644 --- a/src/common/api/useApi.ts +++ b/src/common/api/useApi.ts @@ -1,15 +1,17 @@ 'use client'; -import { Configuration as TokenMetadataApiConfiguration } from '@hirosystems/token-metadata-api-client'; +import { + Configuration as TokenMetadataApiConfiguration, + TokensApi, +} from '@hirosystems/token-metadata-api-client'; import { useGlobalContext } from '../context/useGlobalContext'; -import { apiClients, createConfig } from './client'; -export const useApi = () => { +export const useMetadataApi = () => { const basePath = useGlobalContext().activeNetworkKey; - const apiConfig = createConfig(basePath); - const tokenMetadataApiConfig = new TokenMetadataApiConfiguration({ - basePath, - }); - return apiClients(apiConfig, tokenMetadataApiConfig); + return new TokensApi( + new TokenMetadataApiConfiguration({ + basePath, + }) + ); }; diff --git a/src/common/api/utils.ts b/src/common/api/utils.ts deleted file mode 100644 index 36dd6772b..000000000 --- a/src/common/api/utils.ts +++ /dev/null @@ -1,16 +0,0 @@ -'use client'; - -import { ChainID } from '@stacks/transactions'; - -import { NetworkModes } from '../types/network'; - -export const getNetworkModeFromNetworkId = (networkId: ChainID) => { - switch (networkId) { - case ChainID.Mainnet: - return NetworkModes.Mainnet; - case ChainID.Testnet: - return NetworkModes.Testnet; - default: - return undefined; - } -}; diff --git a/src/common/components/FilterMenu.tsx b/src/common/components/FilterMenu.tsx index 69cd8fa9e..ac47ba41d 100644 --- a/src/common/components/FilterMenu.tsx +++ b/src/common/components/FilterMenu.tsx @@ -13,11 +13,11 @@ import { MenuList } from '../../ui/MenuList'; interface MenuItem { onClick: () => void; - label: string; + label: string | undefined; } interface FilterMenuProps { - filterLabel: string | (() => string); + filterLabel: string | (() => string | undefined); menuItems: MenuItem[] | ReactNode[]; leftIcon?: IconType; } diff --git a/src/common/constants/env.ts b/src/common/constants/env.ts index a98674ec6..bf15a58e4 100644 --- a/src/common/constants/env.ts +++ b/src/common/constants/env.ts @@ -32,7 +32,7 @@ export const NODE_ENV = process.env.NODE_ENV || ''; export const RELEASE_TAG_NAME = process.env.NEXT_PUBLIC_RELEASE_TAG_NAME ?? ''; export const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN || ''; -export const HIRO_HEADERS: HeadersInit = { +export const HIRO_HEADERS = { 'x-hiro-product': 'explorer', 'x-hiro-version': RELEASE_TAG_NAME || packageJson.version, }; diff --git a/src/common/queries/useAccountBalance.ts b/src/common/queries/useAccountBalance.ts index cacf2f5f6..b71c71e98 100644 --- a/src/common/queries/useAccountBalance.ts +++ b/src/common/queries/useAccountBalance.ts @@ -1,56 +1,45 @@ -import { - UseQueryOptions, - UseSuspenseQueryOptions, - useQuery, - useSuspenseQuery, -} from '@tanstack/react-query'; +import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; -import { AddressBalanceResponse } from '@stacks/stacks-blockchain-api-types'; - -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { ONE_MINUTE } from './query-stale-time'; const ACCOUNT_BALANCE_QUERY_KEY = 'accountBalance'; -export function useAccountBalance( - address?: string, - options: Omit, 'queryKey' | 'queryFn'> = {} -) { - const api = useApi(); +export function useAccountBalance(address?: string) { + const apiClient = useApiClient(); return useQuery({ queryKey: [ACCOUNT_BALANCE_QUERY_KEY, address], - queryFn: () => { + queryFn: async () => { if (!address) return undefined; - const response = api.accountsApi.getAccountBalance({ - principal: address, + const { data, error } = await apiClient.GET('/extended/v1/address/{principal}/balances', { + params: { path: { principal: address } }, }); - return response; + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, staleTime: ONE_MINUTE, enabled: !!address, - ...options, }); } -export function useSuspenseAccountBalance( - address?: string, - options: Omit< - UseSuspenseQueryOptions, - 'queryKey' | 'queryFn' - > = {} -) { - const api = useApi(); +export function useSuspenseAccountBalance(address?: string) { + const apiClient = useApiClient(); if (!address) throw new Error('Address is required'); return useSuspenseQuery({ queryKey: [ACCOUNT_BALANCE_QUERY_KEY, address], - queryFn: () => { - const response = api.accountsApi.getAccountBalance({ - principal: address, + queryFn: async () => { + const { data, error } = await apiClient.GET('/extended/v1/address/{principal}/balances', { + params: { path: { principal: address } }, }); - - return response; + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, staleTime: ONE_MINUTE, - ...options, + refetchOnWindowFocus: true, }); } diff --git a/src/common/queries/useAccountStxBalance.ts b/src/common/queries/useAccountStxBalance.ts index d222f17a3..da75cbd6c 100644 --- a/src/common/queries/useAccountStxBalance.ts +++ b/src/common/queries/useAccountStxBalance.ts @@ -1,16 +1,22 @@ import { useQuery } from '@tanstack/react-query'; -import { AddressStxBalanceResponse } from '@stacks/stacks-blockchain-api-types'; - -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { THREE_MINUTES } from './query-stale-time'; export function useAccountStxBalance(principal: string) { - const api = useApi(); + const apiClient = useApiClient(); return useQuery({ queryKey: ['stx-balance', principal], - queryFn: () => - api.accountsApi.getAccountStxBalance({ principal }) as Promise, + queryFn: async () => { + const { data, error } = await apiClient.GET('/extended/v1/address/{principal}/stx', { + params: { path: { principal } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: THREE_MINUTES, }); } diff --git a/src/common/queries/useAddressConfirmedTxsWithTransfersInfinite.ts b/src/common/queries/useAddressConfirmedTxsWithTransfersInfinite.ts index ebd53b56c..77fe548ae 100644 --- a/src/common/queries/useAddressConfirmedTxsWithTransfersInfinite.ts +++ b/src/common/queries/useAddressConfirmedTxsWithTransfersInfinite.ts @@ -12,7 +12,8 @@ import { AddressTransactionWithTransfers, } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { DEFAULT_LIST_LIMIT } from '../constants/constants'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; @@ -22,22 +23,32 @@ const ADDRESS_CONFIRMED_TXS_WITH_TRANSFERS_INFINITE_QUERY_KEY = 'addressConfirmedTxsWithTransfersInfinite'; export function useAddressConfirmedTxsWithTransfersInfinite( - address?: string, + principal?: string, options: any = {} ): UseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useInfiniteQuery({ - queryKey: [ADDRESS_CONFIRMED_TXS_WITH_TRANSFERS_INFINITE_QUERY_KEY, address], - queryFn: ({ pageParam }: { pageParam: number }) => - api.accountsApi.getAccountTransactionsWithTransfers({ - principal: address!, - limit: DEFAULT_LIST_LIMIT, - offset: pageParam || 0, - }), + queryKey: [ADDRESS_CONFIRMED_TXS_WITH_TRANSFERS_INFINITE_QUERY_KEY, principal], + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!principal) return undefined; + const { data, error } = await apiClient.GET( + '/extended/v1/address/{principal}/transactions_with_transfers', + { + params: { + path: { principal }, + query: { limit: DEFAULT_LIST_LIMIT, offset: pageParam || 0 }, + }, + } + ); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: 0, staleTime: TWO_MINUTES, - enabled: !!address, + enabled: !!principal, ...options, }); } @@ -47,16 +58,25 @@ export function useAddressTransactionEventsInfinite( txId?: string, options: any = {} ): UseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useInfiniteQuery({ queryKey: [ADDRESS_CONFIRMED_TXS_WITH_TRANSFERS_INFINITE_QUERY_KEY, address, txId], - queryFn: ({ pageParam }: { pageParam: number }) => - api.transactionsApi.getAddressTransactionEvents({ - address: address!, - txId: txId!, - limit: 5, - offset: pageParam || 0, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!address || !txId) return undefined; + const { data, error } = await apiClient.GET( + '/extended/v2/addresses/{address}/transactions/{tx_id}/events', + { + params: { + path: { address, tx_id: txId }, + query: { limit: DEFAULT_LIST_LIMIT, offset: pageParam || 0 }, + }, + } + ); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: 0, staleTime: TWO_MINUTES, @@ -69,29 +89,19 @@ export function useSuspenseAddressTransactionInfinite( address?: string, options: any = {} ): UseSuspenseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); if (!address) throw new Error('Address is required'); return useSuspenseInfiniteQuery({ queryKey: [ADDRESS_CONFIRMED_TXS_WITH_TRANSFERS_INFINITE_QUERY_KEY, address], - queryFn: ({ pageParam }: { pageParam: number }) => { - const response = api.transactionsApi - .getAddressTransactions({ - address: address, - limit: DEFAULT_LIST_LIMIT, - offset: pageParam || 0, - }) - .catch(error => { - if (error.status === 404) { - return { - limit: DEFAULT_LIST_LIMIT, - offset: pageParam || 0, - total: 0, - results: [], - } as GenericResponseType; - } - throw new Error('Failed to fetch transactions.'); - }); - return response; + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!address) return undefined; + const { data, error } = await apiClient.GET('/extended/v2/addresses/{address}/transactions', { + params: { path: { address }, query: { limit: DEFAULT_LIST_LIMIT, offset: pageParam || 0 } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, getNextPageParam, initialPageParam: 0, diff --git a/src/common/queries/useAddressMempoolTxsInfinite.ts b/src/common/queries/useAddressMempoolTxsInfinite.ts index 6ece76e9c..8d4f24e59 100644 --- a/src/common/queries/useAddressMempoolTxsInfinite.ts +++ b/src/common/queries/useAddressMempoolTxsInfinite.ts @@ -1,13 +1,15 @@ import { + InfiniteData, UseInfiniteQueryResult, + UseSuspenseInfiniteQueryResult, useInfiniteQuery, useSuspenseInfiniteQuery, } from '@tanstack/react-query'; -import { InfiniteData, UseSuspenseInfiniteQueryResult } from '@tanstack/react-query'; import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { DEFAULT_LIST_LIMIT } from '../constants/constants'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; @@ -17,15 +19,19 @@ export function useAddressMempoolTxsInfinite( address?: string, options: any = {} ): UseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useInfiniteQuery({ queryKey: ['addressMempoolTxsInfinite', address], - queryFn: ({ pageParam }: { pageParam: number }) => - api.transactionsApi.getMempoolTransactionList({ - address, - limit: DEFAULT_LIST_LIMIT, - offset: pageParam, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!address) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/tx/mempool', { + params: { query: { limit: DEFAULT_LIST_LIMIT, offset: pageParam } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: 0, staleTime: TWO_MINUTES, @@ -37,15 +43,19 @@ export function useSuspenseAddressMempoolTxsInfinite( address?: string, options: any = {} ): UseSuspenseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseInfiniteQuery({ queryKey: ['addressMempoolTxsInfinite', address], - queryFn: ({ pageParam }: { pageParam: number }) => - api.transactionsApi.getMempoolTransactionList({ - address, - limit: DEFAULT_LIST_LIMIT, - offset: pageParam, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!address) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/tx/mempool', { + params: { query: { limit: DEFAULT_LIST_LIMIT, offset: pageParam } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: 0, staleTime: TWO_MINUTES, diff --git a/src/common/queries/useAddressNonces.ts b/src/common/queries/useAddressNonces.ts index ce2809468..10a3e9474 100644 --- a/src/common/queries/useAddressNonces.ts +++ b/src/common/queries/useAddressNonces.ts @@ -1,14 +1,21 @@ import { useQuery } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; export const useAddressNonces = ({ address }: { address: string }) => { - const api = useApi(); + const apiClient = useApiClient(); return useQuery({ queryKey: ['addressNonces', address], - queryFn: () => - api.accountsApi.getAccountNonces({ - principal: address, - }), + queryFn: async () => { + if (!address) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/address/{principal}/nonces', { + params: { path: { principal: address } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, }); }; diff --git a/src/common/queries/useBlockByHash.ts b/src/common/queries/useBlockByHash.ts index b65747d5f..ae2d18a60 100644 --- a/src/common/queries/useBlockByHash.ts +++ b/src/common/queries/useBlockByHash.ts @@ -2,8 +2,8 @@ import { UseSuspenseQueryOptions, useQuery, useSuspenseQuery } from '@tanstack/r import { Block } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; -import { useGlobalContext } from '../context/useGlobalContext'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; const BLOCK_QUERY_KEY = 'block'; @@ -11,38 +11,41 @@ export function useBlockByHash( hash?: string, options: Partial, 'queryKey'>> = {} ) { - const api = useApi(); + const apiClient = useApiClient(); return useQuery({ queryKey: ['blockByHash', hash], - queryFn: () => - api.blocksApi.getBlockByHash({ - hash: hash!, - }), + queryFn: async () => { + if (!hash) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/block/{hash}', { + params: { path: { hash } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: Infinity, enabled: !!hash, ...options, }); } -// TODO: Use this until we update @stacks/stacks-blockchain-client -interface BlockWithTenureHeight extends Block { - tenure_height: number | null; - tx_count: number; -} - -export function useSuspenseBlockByHeightOrHash( - heightOrHash: string, - options: Partial< - Omit, 'queryKey'> - > = {} -) { - const { url: activeNetworkUrl } = useGlobalContext().activeNetwork; +export function useSuspenseBlockByHeightOrHash(heightOrHash: string) { + const apiClient = useApiClient(); if (!heightOrHash) throw new Error('Height or hash is required'); return useSuspenseQuery({ queryKey: [BLOCK_QUERY_KEY, heightOrHash], - queryFn: () => - fetch(`${activeNetworkUrl}/extended/v2/blocks/${heightOrHash}`).then(res => res.json()), + queryFn: async () => { + if (!heightOrHash) return undefined; + const { data, error } = await apiClient.GET('/extended/v2/blocks/{height_or_hash}', { + params: { path: { height_or_hash: heightOrHash } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: Infinity, - ...options, + refetchOnWindowFocus: true, }); } diff --git a/src/common/queries/useBlockByHeight.ts b/src/common/queries/useBlockByHeight.ts deleted file mode 100644 index 552e245c3..000000000 --- a/src/common/queries/useBlockByHeight.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { UseQueryResult, useQuery } from '@tanstack/react-query'; - -import { Block } from '@stacks/stacks-blockchain-api-types'; - -import { useApi } from '../api/useApi'; -import { ONE_MINUTE } from './query-stale-time'; - -export function useBlockByHeight(height: number, options: any = {}): UseQueryResult { - const api = useApi(); - return useQuery({ - queryKey: ['block-by-height', height], - queryFn: () => api.blocksApi.getBlockByHeight({ height }), - staleTime: ONE_MINUTE, - ...options, - }); -} diff --git a/src/common/queries/useBlockListInfinite.ts b/src/common/queries/useBlockListInfinite.ts index bba33cdaf..0f55ff5a2 100644 --- a/src/common/queries/useBlockListInfinite.ts +++ b/src/common/queries/useBlockListInfinite.ts @@ -1,66 +1,26 @@ -import { useInfiniteQuery, useSuspenseInfiniteQuery } from '@tanstack/react-query'; +import { useSuspenseInfiniteQuery } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { DEFAULT_LIST_LIMIT } from '../constants/constants'; import { getNextPageParam } from '../utils/utils'; import { TWO_MINUTES } from './query-stale-time'; export const BLOCK_LIST_QUERY_KEY = 'blockListInfinite'; -export const useBlockListInfinite = () => { - const api = useApi(); - return useInfiniteQuery({ - queryKey: [BLOCK_LIST_QUERY_KEY], - queryFn: ({ pageParam }: { pageParam: number }) => - api.blocksApi.getBlockList({ - limit: DEFAULT_LIST_LIMIT, - offset: pageParam || 0, - }), - staleTime: TWO_MINUTES, - getNextPageParam, - initialPageParam: 0, - }); -}; - export const useSuspenseBlockListInfinite = (limit = DEFAULT_LIST_LIMIT) => { - const api = useApi(); - return useSuspenseInfiniteQuery({ - queryKey: [BLOCK_LIST_QUERY_KEY, limit], - queryFn: ({ pageParam }: { pageParam: number }) => - api.blocksApi.getBlockList({ - limit, - offset: pageParam || 0, - }), - staleTime: TWO_MINUTES, - getNextPageParam, - initialPageParam: 0, - }); -}; - -export const useBlocksInfiniteNew = () => { - const api = useApi(); - return useInfiniteQuery({ - queryKey: [BLOCK_LIST_QUERY_KEY], - queryFn: ({ pageParam }: { pageParam: number }) => - api.blocksApi.getBlocks({ - limit: DEFAULT_LIST_LIMIT, - offset: pageParam || 0, - }), - staleTime: TWO_MINUTES, - getNextPageParam, - initialPageParam: 0, - }); -}; - -export const useSuspenseBlocksInfiniteNew = (limit = DEFAULT_LIST_LIMIT) => { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseInfiniteQuery({ queryKey: [BLOCK_LIST_QUERY_KEY, limit], - queryFn: ({ pageParam }: { pageParam: number }) => - api.blocksApi.getBlocks({ - limit, - offset: pageParam || 0, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + const { data, error } = await apiClient.GET('/extended/v2/blocks/', { + params: { query: { limit, offset: pageParam || 0 } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: TWO_MINUTES, getNextPageParam, initialPageParam: 0, diff --git a/src/common/queries/useBlockTxsInfinite.ts b/src/common/queries/useBlockTxsInfinite.ts index 7bed18bd0..773784227 100644 --- a/src/common/queries/useBlockTxsInfinite.ts +++ b/src/common/queries/useBlockTxsInfinite.ts @@ -1,15 +1,15 @@ import { + InfiniteData, UseInfiniteQueryResult, - UseSuspenseInfiniteQueryOptions, UseSuspenseInfiniteQueryResult, useInfiniteQuery, useSuspenseInfiniteQuery, } from '@tanstack/react-query'; -import { InfiniteData } from '@tanstack/react-query'; import { Transaction } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { MAX_BLOCK_TRANSACTIONS_PER_CALL } from '../constants/constants'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; @@ -19,15 +19,22 @@ export function useBlockTxsInfinite( blockHash?: string, options: any = {} ): UseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useInfiniteQuery({ queryKey: ['blockTxsInfinite', blockHash], - queryFn: ({ pageParam }: { pageParam: number }) => - api.transactionsApi.getTransactionsByBlockHash({ - blockHash: blockHash!, - limit: MAX_BLOCK_TRANSACTIONS_PER_CALL, - offset: pageParam || 0, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!blockHash) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/tx/block/{block_hash}', { + params: { + path: { block_hash: blockHash }, + query: { limit: MAX_BLOCK_TRANSACTIONS_PER_CALL, offset: pageParam || 0 }, + }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: 0, staleTime: TWO_MINUTES, @@ -40,15 +47,22 @@ export function useSuspenseBlockTxsInfinite( blockHash: string, options: any = {} ): UseSuspenseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseInfiniteQuery({ queryKey: ['blockTxsInfinite', blockHash], - queryFn: ({ pageParam }: { pageParam: number }) => - api.transactionsApi.getTransactionsByBlockHash({ - blockHash, - limit: MAX_BLOCK_TRANSACTIONS_PER_CALL, - offset: pageParam || 0, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!blockHash) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/tx/block/{block_hash}', { + params: { + path: { block_hash: blockHash }, + query: { limit: MAX_BLOCK_TRANSACTIONS_PER_CALL, offset: pageParam || 0 }, + }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: 0, staleTime: TWO_MINUTES, diff --git a/src/common/queries/useBlocksByBurnBlock.ts b/src/common/queries/useBlocksByBurnBlock.ts index 9c6f45329..42380a3d1 100644 --- a/src/common/queries/useBlocksByBurnBlock.ts +++ b/src/common/queries/useBlocksByBurnBlock.ts @@ -6,9 +6,10 @@ import { useSuspenseInfiniteQuery, } from '@tanstack/react-query'; -import { NakamotoBlock } from '@stacks/blockchain-api-client/src/generated/models'; +import { NakamotoBlock } from '@stacks/blockchain-api-client'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; import { ONE_SECOND, TWO_MINUTES } from './query-stale-time'; @@ -18,18 +19,29 @@ export const GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY = 'getBlocksByBurnBlock'; export const MAX_STX_BLOCKS_PER_BURN_BLOCK_LIMIT = 30; export function useGetStxBlocksByBurnBlockQuery() { - const api = useApi(); + const apiClient = useApiClient(); return ( heightOrHash: string | number, - numStxBlocksperBtcBlock: number = MAX_STX_BLOCKS_PER_BURN_BLOCK_LIMIT + numStxBlocksPerBtcBlock: number = MAX_STX_BLOCKS_PER_BURN_BLOCK_LIMIT ) => ({ queryKey: [GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY, heightOrHash, 'special'], - queryFn: () => - api.blocksApi.getBlocksByBurnBlock({ - heightOrHash, - limit: numStxBlocksperBtcBlock, - }), + queryFn: async () => { + if (!heightOrHash) return undefined; + const { data, error } = await apiClient.GET( + '/extended/v2/burn-blocks/{height_or_hash}/blocks', + { + params: { + path: { height_or_hash: heightOrHash }, + query: { limit: numStxBlocksPerBtcBlock }, + }, + } + ); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: TWO_MINUTES, cacheTime: 15 * 60 * 1000, refetchOnWindowFocus: false, @@ -43,16 +55,23 @@ export function useBlocksByBurnBlock( options?: object, queryKeyExtension?: string ): UseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); const rangeQueryKey = offset ? `${offset}-${offset + limit}` : ''; return useInfiniteQuery({ queryKey: [GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY, heightOrHash, rangeQueryKey, queryKeyExtension], - queryFn: ({ pageParam }: { pageParam: number }) => - api.blocksApi.getBlocksByBurnBlock({ - heightOrHash, - limit, - offset: pageParam, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!heightOrHash) return undefined; + const { data, error } = await apiClient.GET( + '/extended/v2/burn-blocks/{height_or_hash}/blocks', + { + params: { path: { height_or_hash: heightOrHash }, query: { limit, offset: pageParam } }, + } + ); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: offset ?? 0, staleTime: heightOrHash === 'latest' ? ONE_SECOND * 5 : TWO_MINUTES, @@ -66,15 +85,22 @@ export function useSuspenseBlocksByBurnBlock( options: any = {}, queryKeyExtension?: string ): UseSuspenseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseInfiniteQuery({ queryKey: [GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY, heightOrHash, queryKeyExtension], - queryFn: ({ pageParam }: { pageParam: number }) => - api.blocksApi.getBlocksByBurnBlock({ - heightOrHash, - limit, - offset: pageParam, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + if (!heightOrHash) return undefined; + const { data, error } = await apiClient.GET( + '/extended/v2/burn-blocks/{height_or_hash}/blocks', + { + params: { path: { height_or_hash: heightOrHash }, query: { limit, offset: pageParam } }, + } + ); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: options.offset ?? 0, staleTime: heightOrHash === 'latest' ? ONE_SECOND * 5 : TWO_MINUTES, diff --git a/src/common/queries/useBurnBlock.ts b/src/common/queries/useBurnBlock.ts index ed333249d..b9f55cc06 100644 --- a/src/common/queries/useBurnBlock.ts +++ b/src/common/queries/useBurnBlock.ts @@ -8,14 +8,15 @@ import { import { BurnBlock } from '@stacks/blockchain-api-client'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; export const BURN_BLOCKS_QUERY_KEY = 'burnBlocks'; export function useFetchBurnBlock(): ( heightOrHash: string | number ) => Promise { - const api = useApi(); + const apiClient = useApiClient(); const queryClient = useQueryClient(); return async (heightOrHash: string | number) => { @@ -27,8 +28,15 @@ export function useFetchBurnBlock(): ( } // Fetch the data and update the cache - const fetchBurnBlock = async (): Promise => { - return api.burnBlocksApi.getBurnBlock({ heightOrHash }).catch(() => undefined); + const fetchBurnBlock = async () => { + if (!heightOrHash) return undefined; + const { data, error } = await apiClient.GET('/extended/v2/burn-blocks/{height_or_hash}', { + params: { path: { height_or_hash: heightOrHash } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }; return queryClient.fetchQuery({ @@ -53,31 +61,23 @@ export function useFetchMultipleBurnBlocks(): ( }; } -export function useGetBurnBlockQuery() { - const api = useApi(); - return (heightOrHash: string | number) => ({ - queryKey: [BURN_BLOCKS_QUERY_KEY, heightOrHash], - queryFn: () => - api.burnBlocksApi.getBurnBlock({ - heightOrHash, - }), - cacheTime: 15 * 60 * 1000, - }); -} - export function useBurnBlock( heightOrHash: number | string, options: any = {} ): UseQueryResult { - const api = useApi(); + const apiClient = useApiClient(); return useQuery({ queryKey: ['burn-block', heightOrHash], - queryFn: () => - api.burnBlocksApi - .getBurnBlock({ - heightOrHash, - }) - .catch(() => undefined), + queryFn: async () => { + if (!heightOrHash) return undefined; + const { data, error } = await apiClient.GET('/extended/v2/burn-blocks/{height_or_hash}', { + params: { path: { height_or_hash: heightOrHash } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: Infinity, ...options, }); @@ -87,10 +87,19 @@ export function useSuspenseBurnBlock( heightOrHash: number | string, options: any = {} ): UseSuspenseQueryResult { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseQuery({ queryKey: ['burn-block', heightOrHash], - queryFn: () => api.burnBlocksApi.getBurnBlock({ heightOrHash }).catch(() => undefined), + queryFn: async () => { + if (!heightOrHash) return undefined; + const { data, error } = await apiClient.GET('/extended/v2/burn-blocks/{height_or_hash}', { + params: { path: { height_or_hash: heightOrHash } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: Infinity, ...options, }); diff --git a/src/common/queries/useBurnBlocksInfinite.ts b/src/common/queries/useBurnBlocksInfinite.ts index 98aa60258..ef2b4348d 100644 --- a/src/common/queries/useBurnBlocksInfinite.ts +++ b/src/common/queries/useBurnBlocksInfinite.ts @@ -1,14 +1,13 @@ import { InfiniteData, - UseInfiniteQueryResult, UseSuspenseInfiniteQueryResult, - useInfiniteQuery, useSuspenseInfiniteQuery, } from '@tanstack/react-query'; import { BurnBlock } from '@stacks/blockchain-api-client'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { DEFAULT_BURN_BLOCKS_LIMIT } from '../constants/constants'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; @@ -16,40 +15,24 @@ import { TWO_MINUTES } from './query-stale-time'; export const BURN_BLOCKS_QUERY_KEY = 'burnBlocks'; -// TODO: move code into useBurnBlocks -export function useBurnBlocks( - options: any = {} -): UseInfiniteQueryResult>> { - const api = useApi(); - return useInfiniteQuery({ - queryKey: [BURN_BLOCKS_QUERY_KEY], - queryFn: ({ pageParam }: { pageParam: number }) => - api.burnBlocksApi.getBurnBlocks({ - limit: DEFAULT_BURN_BLOCKS_LIMIT, - offset: pageParam, - }), - getNextPageParam, - initialPageParam: 0, - staleTime: TWO_MINUTES, - ...options, - }); -} - export function useSuspenseBurnBlocks( limit = DEFAULT_BURN_BLOCKS_LIMIT, options: any = {}, queryKeyExtension?: string ): UseSuspenseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseInfiniteQuery({ queryKey: queryKeyExtension ? [BURN_BLOCKS_QUERY_KEY, limit, queryKeyExtension] : [BURN_BLOCKS_QUERY_KEY, limit], - queryFn: ({ pageParam }: { pageParam: number }) => { - return api.burnBlocksApi.getBurnBlocks({ - limit, - offset: pageParam, + queryFn: async ({ pageParam }: { pageParam: number }) => { + const { data, error } = await apiClient.GET('/extended/v2/burn-blocks/', { + params: { query: { limit, offset: pageParam } }, }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, getNextPageParam, initialPageParam: 0, diff --git a/src/common/queries/useConfirmedTransactionsInfinite.ts b/src/common/queries/useConfirmedTransactionsInfinite.ts index d5c8d8a42..89b73a252 100644 --- a/src/common/queries/useConfirmedTransactionsInfinite.ts +++ b/src/common/queries/useConfirmedTransactionsInfinite.ts @@ -1,20 +1,9 @@ -import { - UseInfiniteQueryResult, - UseQueryOptions, - UseSuspenseInfiniteQueryResult, - UseSuspenseQueryOptions, - useInfiniteQuery, - useSuspenseInfiniteQuery, -} from '@tanstack/react-query'; -import { InfiniteData } from '@tanstack/react-query'; +import { InfiniteData, UseInfiniteQueryResult, useInfiniteQuery } from '@tanstack/react-query'; -import { - GetTransactionListOrderEnum, - GetTransactionListSortByEnum, -} from '@stacks/blockchain-api-client'; -import { Block, Transaction } from '@stacks/stacks-blockchain-api-types'; +import { Transaction } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { DEFAULT_LIST_LIMIT } from '../constants/constants'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; @@ -34,12 +23,12 @@ export function useConfirmedTransactionsInfinite( toAddress?: string; startTime?: number; endTime?: number; - order?: GetTransactionListOrderEnum; - sortBy?: GetTransactionListSortByEnum; + order?: 'asc' | 'desc' | undefined; + sortBy?: string; } = {}, options: any = {} ): UseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useInfiniteQuery({ queryKey: [ 'confirmedTransactionsInfinite', @@ -52,21 +41,31 @@ export function useConfirmedTransactionsInfinite( ], queryFn: async ({ pageParam }: { pageParam: number }) => { if (fromAddress?.endsWith('.btc')) { - fromAddress = (await searchByBnsName(api, fromAddress))?.result.entity_id || fromAddress; + fromAddress = + (await searchByBnsName(apiClient, fromAddress))?.result.entity_id || fromAddress; } if (toAddress?.endsWith('.btc')) { - toAddress = (await searchByBnsName(api, toAddress))?.result.entity_id || toAddress; + toAddress = (await searchByBnsName(apiClient, toAddress))?.result.entity_id || toAddress; } - return api.transactionsApi.getTransactionList({ - limit: DEFAULT_LIST_LIMIT, - offset: pageParam, - fromAddress, - toAddress, - startTime, - endTime, - order, - sortBy, + + const { data, error } = await apiClient.GET('/extended/v1/tx/', { + params: { + query: { + limit: DEFAULT_LIST_LIMIT, + offset: pageParam, + fromAddress, + toAddress, + startTime, + endTime, + order, + sortBy, + }, + }, }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, initialPageParam: 0, getNextPageParam, @@ -75,21 +74,3 @@ export function useConfirmedTransactionsInfinite( ...options, }); } - -export function useSuspenseConfirmedTransactionsInfinite(): UseSuspenseInfiniteQueryResult< - InfiniteData> -> { - const api = useApi(); - return useSuspenseInfiniteQuery({ - queryKey: ['confirmedTransactionsInfinite'], - queryFn: ({ pageParam }: { pageParam: number }) => - api.transactionsApi.getTransactionList({ - limit: DEFAULT_LIST_LIMIT, - offset: pageParam, - }), - initialPageParam: 0, - getNextPageParam, - staleTime: TWO_MINUTES, - refetchOnWindowFocus: true, - }); -} diff --git a/src/common/queries/useContractById.ts b/src/common/queries/useContractById.ts index 33da0d763..50044455b 100644 --- a/src/common/queries/useContractById.ts +++ b/src/common/queries/useContractById.ts @@ -7,20 +7,25 @@ import { useSuspenseQuery, } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { ContractWithParsedAbi } from '../types/contract'; export function useContractById( contractId?: string, options: any = {} ): UseQueryResult { - const api = useApi(); + const apiClient = useApiClient(); return useQuery({ queryKey: ['contractById', contractId], queryFn: async () => { - const contract = await api.smartContractsApi.getContractById({ - contractId: contractId!, + if (!contractId) return undefined; + const { data: contract, error } = await apiClient.GET('/extended/v1/contract/{contract_id}', { + params: { path: { contract_id: contractId } }, }); + if (error) { + throw new Error(getErrorMessage(error)); + } return { ...contract, abi: contract.abi ? JSON.parse(contract.abi) : undefined, @@ -36,14 +41,18 @@ export function useSuspenseContractById( contractId?: string, options: any = {} ): UseSuspenseQueryResult { - const api = useApi(); + const apiClient = useApiClient(); if (!contractId) throw new Error('Contract ID is required'); return useSuspenseQuery({ queryKey: ['contractById', contractId], queryFn: async () => { - const contract = await api.smartContractsApi.getContractById({ - contractId, + if (!contractId) return undefined; + const { data: contract, error } = await apiClient.GET('/extended/v1/contract/{contract_id}', { + params: { path: { contract_id: contractId } }, }); + if (error) { + throw new Error(getErrorMessage(error)); + } return { ...contract, abi: contract.abi ? JSON.parse(contract.abi) : undefined, diff --git a/src/common/queries/useContractFtMetadata.ts b/src/common/queries/useContractFtMetadata.ts index 80cfe7eb3..0996c7c80 100644 --- a/src/common/queries/useContractFtMetadata.ts +++ b/src/common/queries/useContractFtMetadata.ts @@ -1,12 +1,12 @@ import { useQuery } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { useMetadataApi } from '../api/useApi'; export function useContractFtMetadata(contractId?: string) { - const api = useApi(); + const tokenMetadataApi = useMetadataApi(); return useQuery({ queryKey: ['contract-ft-metadata', contractId], - queryFn: () => api.tokenMetadataApi?.getFtMetadata(contractId!), + queryFn: () => tokenMetadataApi?.getFtMetadata(contractId!), enabled: !!contractId, }); } diff --git a/src/common/queries/useCoreApiInfo.ts b/src/common/queries/useCoreApiInfo.ts index 02d6fba2e..eebf63eed 100644 --- a/src/common/queries/useCoreApiInfo.ts +++ b/src/common/queries/useCoreApiInfo.ts @@ -1,11 +1,11 @@ import { useQuery } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { useGlobalContext } from '../context/useGlobalContext'; export function useCoreApiInfo() { - const api = useApi(); + const { url: activeNetworkUrl } = useGlobalContext().activeNetwork; return useQuery({ queryKey: ['coreApiInfo'], - queryFn: () => api.infoApi.getCoreApiInfo(), + queryFn: () => fetch(`${activeNetworkUrl}/v2/info`).then(res => res.json()), }); } diff --git a/src/common/queries/useCustomNetworkApiInfo.ts b/src/common/queries/useCustomNetworkApiInfo.ts index c141b1ec0..0283d42e0 100644 --- a/src/common/queries/useCustomNetworkApiInfo.ts +++ b/src/common/queries/useCustomNetworkApiInfo.ts @@ -1,20 +1,13 @@ -import { UseQueryOptions, useQuery } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; -import { CoreNodeInfoResponse } from '@stacks/blockchain-api-client/src/generated/models'; - -import { useApi } from '../api/useApi'; import { DEFAULT_V2_INFO_ENDPOINT } from '../constants/constants'; import { ONE_MINUTE } from './query-stale-time'; export const getCustomNetworkApiInfo = (baseUrl: string) => () => fetch(`${baseUrl}${DEFAULT_V2_INFO_ENDPOINT}`).then(res => res.json()); -export function useCustomNetworkApiInfo( - url: string, - options: Omit, 'queryKey'> = {} -) { - const api = useApi(); - return useQuery({ +export function useCustomNetworkApiInfo(url: string, options: any = {}) { + return useQuery({ queryKey: ['customNetworkApiInfo', url], queryFn: getCustomNetworkApiInfo(url), staleTime: ONE_MINUTE, diff --git a/src/common/queries/useFaucet.ts b/src/common/queries/useFaucet.ts index 989c7d4d3..5e89bf9e7 100644 --- a/src/common/queries/useFaucet.ts +++ b/src/common/queries/useFaucet.ts @@ -1,11 +1,20 @@ import { useMutation } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; export function useFaucet() { - const api = useApi(); + const apiClient = useApiClient(); return useMutation({ - mutationFn: ({ address, staking }: { address: string; staking?: boolean }) => - api.faucetsApi.runFaucetStx({ address, ...(staking ? { staking: true } : {}) }), + mutationFn: async ({ address, staking }: { address: string; staking?: boolean }) => { + if (!address) return undefined; + const { data, error } = await apiClient.POST('/extended/v1/faucets/stx', { + body: { address, ...(staking ? { staking: true } : {}) }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, }); } diff --git a/src/common/queries/useFeeTransfer.ts b/src/common/queries/useFeeTransfer.ts index 51974342d..2cbf22c82 100644 --- a/src/common/queries/useFeeTransfer.ts +++ b/src/common/queries/useFeeTransfer.ts @@ -1,11 +1,11 @@ +import { useGlobalContext } from '@/common/context/useGlobalContext'; import { useQuery } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; - export function useFeeTransfer() { - const api = useApi(); + const { url: activeNetworkUrl } = useGlobalContext().activeNetwork; + return useQuery({ queryKey: ['transfer-fees'], - queryFn: () => api.feesApi.getFeeTransfer(), + queryFn: () => fetch(`${activeNetworkUrl}/v2/fees/transfer`).then(res => res.json()), }); } diff --git a/src/common/queries/useFtMetadata.ts b/src/common/queries/useFtMetadata.ts index 59681e04f..a57f61049 100644 --- a/src/common/queries/useFtMetadata.ts +++ b/src/common/queries/useFtMetadata.ts @@ -6,16 +6,16 @@ import { useSuspenseQuery, } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { useMetadataApi } from '../api/useApi'; export function useFtMetadata( contractId?: string, options: any = {} ): UseQueryResult { - const api = useApi(); + const tokenMetadataApi = useMetadataApi(); return useQuery({ queryKey: ['ft-metadata', contractId], - queryFn: () => api.tokenMetadataApi?.getFtMetadata(contractId!), + queryFn: () => tokenMetadataApi?.getFtMetadata(contractId!), retry: false, staleTime: Infinity, refetchOnWindowFocus: false, @@ -28,10 +28,10 @@ export function useSuspenseFtMetadata( contractId: string, options: any = {} ): UseSuspenseQueryResult { - const api = useApi(); + const tokenMetadataApi = useMetadataApi(); return useSuspenseQuery({ queryKey: ['ft-metadata', contractId], - queryFn: () => api.tokenMetadataApi?.getFtMetadata(contractId), + queryFn: () => tokenMetadataApi?.getFtMetadata(contractId), retry: false, staleTime: Infinity, refetchOnWindowFocus: false, diff --git a/src/common/queries/useFtTokens.ts b/src/common/queries/useFtTokens.ts index 87994911d..7bdd6dd7d 100644 --- a/src/common/queries/useFtTokens.ts +++ b/src/common/queries/useFtTokens.ts @@ -2,14 +2,14 @@ import { FtBasicMetadataResponse } from '@hirosystems/token-metadata-api-client'; import { + InfiniteData, UseInfiniteQueryResult, UseSuspenseInfiniteQueryResult, useInfiniteQuery, useSuspenseInfiniteQuery, } from '@tanstack/react-query'; -import { InfiniteData } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { useMetadataApi } from '../api/useApi'; import { DEFAULT_LIST_LIMIT } from '../constants/constants'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; @@ -31,7 +31,7 @@ export const useFtTokens = ( }, options: any = {} ): UseInfiniteQueryResult>> => { - const { tokenMetadataApi } = useApi(); + const tokenMetadataApi = useMetadataApi(); return useInfiniteQuery({ queryKey: ['ftTokens', name, symbol, address, order_by, order], queryFn: ({ pageParam }: { pageParam: number }) => @@ -67,7 +67,7 @@ export const useSuspenseFtTokens = ( }, options: any = {} ): UseSuspenseInfiniteQueryResult>> => { - const { tokenMetadataApi } = useApi(); + const tokenMetadataApi = useMetadataApi(); return useSuspenseInfiniteQuery({ queryKey: ['ftTokens', name, symbol, address, order_by, order], queryFn: ({ pageParam }: { pageParam: number }) => diff --git a/src/common/queries/useMempoolFee.ts b/src/common/queries/useMempoolFee.ts index bc9aa44a2..537e304a8 100644 --- a/src/common/queries/useMempoolFee.ts +++ b/src/common/queries/useMempoolFee.ts @@ -1,14 +1,19 @@ import { useSuspenseQuery } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { ONE_MINUTE } from './query-stale-time'; export function useSuspenseMempoolFee(options: any = {}) { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseQuery({ queryKey: ['mempoolFee'], - queryFn: () => { - return api.mempoolApi.getMempoolFeePriorities(); + queryFn: async () => { + const { data, error } = await apiClient.GET('/extended/v2/mempool/fees'); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, staleTime: ONE_MINUTE, ...options, diff --git a/src/common/queries/useMempoolTransactionsInfinite.ts b/src/common/queries/useMempoolTransactionsInfinite.ts index a0e12375f..18af3fac5 100644 --- a/src/common/queries/useMempoolTransactionsInfinite.ts +++ b/src/common/queries/useMempoolTransactionsInfinite.ts @@ -1,62 +1,37 @@ -import { - InfiniteData, - UseInfiniteQueryResult, - UseSuspenseInfiniteQueryResult, - useInfiniteQuery, - useSuspenseInfiniteQuery, -} from '@tanstack/react-query'; +import { InfiniteData, UseInfiniteQueryResult, useInfiniteQuery } from '@tanstack/react-query'; -import { - GetMempoolTransactionListOrderByEnum, - GetMempoolTransactionListOrderEnum, -} from '@stacks/blockchain-api-client'; import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { DEFAULT_LIST_LIMIT } from '../constants/constants'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; import { TWO_MINUTES } from './query-stale-time'; export function useMempoolTransactionsInfinite( - sort: GetMempoolTransactionListOrderByEnum = GetMempoolTransactionListOrderByEnum.age, - order: GetMempoolTransactionListOrderEnum = GetMempoolTransactionListOrderEnum.asc + sort = 'age', + order: 'asc' | 'desc' | undefined = 'asc' ): UseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useInfiniteQuery({ queryKey: ['mempoolTransactionsInfinite', sort, order], - queryFn: ({ pageParam }) => - api.transactionsApi.getMempoolTransactionList({ - limit: DEFAULT_LIST_LIMIT, - offset: pageParam || 0, - order, - orderBy: sort, - }), - getNextPageParam, - initialPageParam: 0, - staleTime: TWO_MINUTES, - refetchOnMount: false, - retry: false, - refetchOnReconnect: false, - refetchInterval: false, - refetchIntervalInBackground: false, - }); -} - -export function useSuspenseMempoolTransactionsInfinite( - sort: GetMempoolTransactionListOrderByEnum = GetMempoolTransactionListOrderByEnum.age, - order: GetMempoolTransactionListOrderEnum = GetMempoolTransactionListOrderEnum.asc -): UseSuspenseInfiniteQueryResult>> { - const api = useApi(); - return useSuspenseInfiniteQuery({ - queryKey: ['mempoolTransactionsInfinite', sort, order], - queryFn: ({ pageParam }) => - api.transactionsApi.getMempoolTransactionList({ - limit: DEFAULT_LIST_LIMIT, - offset: pageParam || 0, - order, - orderBy: sort, - }), + queryFn: async ({ pageParam }: { pageParam: number }) => { + const { data, error } = await apiClient.GET('/extended/v1/tx/mempool', { + params: { + query: { + limit: DEFAULT_LIST_LIMIT, + offset: pageParam || 0, + order, + orderBy: sort, + }, + }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, getNextPageParam, initialPageParam: 0, staleTime: TWO_MINUTES, diff --git a/src/common/queries/useMempoolTxStats.ts b/src/common/queries/useMempoolTxStats.ts index 2dbf68c62..1a277a8da 100644 --- a/src/common/queries/useMempoolTxStats.ts +++ b/src/common/queries/useMempoolTxStats.ts @@ -1,30 +1,22 @@ -import { useQuery, useSuspenseQuery } from '@tanstack/react-query'; +import { useSuspenseQuery } from '@tanstack/react-query'; -import { MempoolTransactionStatsResponse } from '@stacks/blockchain-api-client'; +import { OperationResponse } from '@stacks/blockchain-api-client'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { ONE_MINUTE } from './query-stale-time'; -export function useMempoolTransactionStats(options: any = {}) { - const api = useApi(); - return useQuery({ +export function useSuspenseMempoolTransactionStats() { + const apiClient = useApiClient(); + return useSuspenseQuery({ queryKey: ['mempoolTransactionStats'], - queryFn: () => { - return api.transactionsApi.getMempoolTransactionStats(); + queryFn: async () => { + const { data, error } = await apiClient.GET('/extended/v1/tx/mempool/stats'); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, staleTime: ONE_MINUTE, - ...options, - }); -} - -export function useSuspenseMempoolTransactionStats(options: any = {}) { - const api = useApi(); - return useSuspenseQuery({ - queryKey: ['mempoolTransactionStats'], - queryFn: () => { - return api.transactionsApi.getMempoolTransactionStats(); - }, - staleTime: ONE_MINUTE, - ...options, }); } diff --git a/src/common/queries/useMicroblockByHash.ts b/src/common/queries/useMicroblockByHash.ts deleted file mode 100644 index 747086359..000000000 --- a/src/common/queries/useMicroblockByHash.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { UseSuspenseQueryResult, useSuspenseQuery } from '@tanstack/react-query'; - -import { Microblock } from '@stacks/stacks-blockchain-api-types'; - -import { useApi } from '../api/useApi'; - -export function useSuspenseMicroblockByHash( - microblockHash: string, - options: any = {} -): UseSuspenseQueryResult { - const api = useApi(); - return useSuspenseQuery({ - queryKey: ['microblockByHash', microblockHash], - queryFn: () => - api.microblocksApi.getMicroblockByHash({ - hash: microblockHash, - }), - staleTime: Infinity, - ...options, - }); -} diff --git a/src/common/queries/useNftHistory.ts b/src/common/queries/useNftHistory.ts deleted file mode 100644 index 7f0081c62..000000000 --- a/src/common/queries/useNftHistory.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { UseQueryResult, useQuery } from '@tanstack/react-query'; - -import { - NonFungibleTokenHistoryEventList, - NonFungibleTokenHistoryEventWithTxMetadata, -} from '@stacks/stacks-blockchain-api-types'; - -import { useApi } from '../api/useApi'; - -interface NonFungibleTokenHistoryEventListExtended extends NonFungibleTokenHistoryEventList { - results: Array; -} - -export function useNftHistory( - assetIdentifier: string, - value: string, - options: any = {} -): UseQueryResult { - const api = useApi(); - return useQuery({ - queryKey: ['nft-history', assetIdentifier, value], - queryFn: () => - api.nonFungibleTokensApi.getNftHistory({ - assetIdentifier, - value, - }), - ...options, - }); -} diff --git a/src/common/queries/useNftHoldings.ts b/src/common/queries/useNftHoldings.ts index a07d180aa..7a516d457 100644 --- a/src/common/queries/useNftHoldings.ts +++ b/src/common/queries/useNftHoldings.ts @@ -1,47 +1,29 @@ -import { - UseQueryResult, - UseSuspenseQueryResult, - useQuery, - useSuspenseQuery, -} from '@tanstack/react-query'; +import { UseSuspenseQueryResult, useSuspenseQuery } from '@tanstack/react-query'; import { NonFungibleTokenHoldingsList } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { ONE_MINUTE } from './query-stale-time'; -export const useNftHoldings = ( - address?: string, - options: any = {} -): UseQueryResult => { - const api = useApi(); - if (!address) throw new Error('Address is required'); - return useQuery({ - queryKey: ['nftHoldings', address], - queryFn: () => - api.nonFungibleTokensApi.getNftHoldings({ - principal: address!, - limit: 200, - }), - enabled: !!address, - staleTime: ONE_MINUTE, - ...options, - }); -}; - export const useSuspenseNftHoldings = ( address?: string, options: any = {} ): UseSuspenseQueryResult => { - const api = useApi(); + const apiClient = useApiClient(); if (!address) throw new Error('Address is required'); return useSuspenseQuery({ queryKey: ['nftHoldings', address], - queryFn: () => - api.nonFungibleTokensApi.getNftHoldings({ - principal: address, - limit: 200, - }), + queryFn: async () => { + if (!address) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/tokens/nft/holdings', { + params: { query: { principal: address, limit: 200, tx_metadata: false } }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; + }, staleTime: ONE_MINUTE, ...options, }); diff --git a/src/common/queries/useNftMetadata.ts b/src/common/queries/useNftMetadata.ts index e6060522c..6e4832d26 100644 --- a/src/common/queries/useNftMetadata.ts +++ b/src/common/queries/useNftMetadata.ts @@ -1,16 +1,16 @@ import { NftMetadataResponse } from '@hirosystems/token-metadata-api-client'; -import { UseQueryOptions, useQuery, useSuspenseQuery } from '@tanstack/react-query'; +import { UseQueryOptions, useQuery } from '@tanstack/react-query'; -import { useApi } from '../api/useApi'; +import { useMetadataApi } from '../api/useApi'; export const useNftMetadata = ( { contractId, tokenId }: { contractId?: string; tokenId?: number }, options: Omit, 'queryKey' | 'queryFn'> = {} ) => { - const api = useApi(); + const tokenMetadataApi = useMetadataApi(); return useQuery({ queryKey: ['nft-metadata', contractId, tokenId], - queryFn: () => api.tokenMetadataApi?.getNftMetadata(contractId!, tokenId!), + queryFn: () => tokenMetadataApi?.getNftMetadata(contractId!, tokenId!), retry: false, staleTime: Infinity, refetchOnWindowFocus: false, @@ -18,18 +18,3 @@ export const useNftMetadata = ( ...options, }); }; - -export const useSuspenseNftMetadata = ( - { contractId, tokenId }: { contractId: string; tokenId: number }, - options: Omit, 'queryKey' | 'queryFn'> = {} -) => { - const api = useApi(); - return useSuspenseQuery({ - queryKey: ['nft-metadata', contractId, tokenId], - queryFn: () => api.tokenMetadataApi?.getNftMetadata(contractId, tokenId), - retry: false, - staleTime: Infinity, - refetchOnWindowFocus: false, - ...options, - }); -}; diff --git a/src/common/queries/usePoxInfo.ts b/src/common/queries/usePoxInfo.ts index c09063637..8ef995370 100644 --- a/src/common/queries/usePoxInfo.ts +++ b/src/common/queries/usePoxInfo.ts @@ -2,7 +2,7 @@ import { UseSuspenseQueryOptions, useSuspenseQuery } from '@tanstack/react-query import { CoreNodePoxResponse } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { useGlobalContext } from '../context/useGlobalContext'; import { ONE_MINUTE } from './query-stale-time'; export const useSuspensePoxInfo = ( @@ -10,10 +10,10 @@ export const useSuspensePoxInfo = ( Omit, 'queryKey'> > = {} ) => { - const api = useApi(); + const { url: activeNetworkUrl } = useGlobalContext().activeNetwork; return useSuspenseQuery({ queryKey: ['poxInfo'], - queryFn: () => api.infoApi.getPoxInfo(), + queryFn: () => fetch(`${activeNetworkUrl}/v2/pox`).then(res => res.json()), staleTime: ONE_MINUTE, ...options, }); diff --git a/src/common/queries/usePoxInforRaw.ts b/src/common/queries/usePoxInforRaw.ts index 7007239a4..8ac974a49 100644 --- a/src/common/queries/usePoxInforRaw.ts +++ b/src/common/queries/usePoxInforRaw.ts @@ -2,8 +2,6 @@ import { useSuspenseQuery } from '@tanstack/react-query'; import { useGlobalContext } from '../context/useGlobalContext'; -// import { } from '@stacks/stacks-blockchain-api-types'; // TODO: ask for PoxInfo type - interface PoxInfo { contract_id: string; pox_activation_threshold_ustx: number; diff --git a/src/common/queries/useSearchById.ts b/src/common/queries/useSearchById.ts deleted file mode 100644 index 1a686987b..000000000 --- a/src/common/queries/useSearchById.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { UseQueryResult, useQuery } from '@tanstack/react-query'; - -import { SearchSuccessResult } from '@stacks/stacks-blockchain-api-types'; - -import { useApi } from '../api/useApi'; -import { ONE_MINUTE } from './query-stale-time'; - -export function useSearchById(id: string, options: any = {}): UseQueryResult { - const api = useApi(); - return useQuery({ - queryKey: ['search-by-id', id], - queryFn: async () => { - try { - return api.searchApi.searchById({ id, includeMetadata: true }); - } catch (e: any) { - try { - const data = await e.json(); - if (data && 'found' in data) { - throw data; - } - } catch (e) { - throw e; - } - } - }, - staleTime: ONE_MINUTE, - ...options, - }); -} diff --git a/src/common/queries/useSearchQuery.ts b/src/common/queries/useSearchQuery.ts index ef9fee2d7..c24fd025c 100644 --- a/src/common/queries/useSearchQuery.ts +++ b/src/common/queries/useSearchQuery.ts @@ -1,17 +1,13 @@ import { UTCDate } from '@date-fns/utc'; import { useQuery } from '@tanstack/react-query'; -import { - GetTransactionListOrderEnum, - GetTransactionListSortByEnum, -} from '@stacks/blockchain-api-client'; import { Block, Transaction } from '@stacks/stacks-blockchain-api-types'; import { bufferCVFromString, cvToHex, tupleCV } from '@stacks/transactions'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { blur, focus } from '../../features/search/search-slice'; -import { useApi } from '../api/useApi'; import { BTC_BNS_CONTRACT } from '../constants/constants'; -import { useGlobalContext } from '../context/useGlobalContext'; import { useAppDispatch } from '../state/hooks'; import { Network } from '../types/network'; import { @@ -49,18 +45,24 @@ function nftHistoryToSearchResult(nftHistoryEntry: any, bnsName: string): FoundR }; } -export async function searchByBnsName(api: ReturnType, bnsName: string) { +export async function searchByBnsName(apiClient: ReturnType, bnsName: string) { try { - const nftHistory = await api.nonFungibleTokensApi.getNftHistory({ - assetIdentifier: BTC_BNS_CONTRACT, - value: cvToHex( - tupleCV({ - ['name']: bufferCVFromString(bnsName.replace(new RegExp('.btc$'), '')), - ['namespace']: bufferCVFromString('btc'), - }) - ), + const { data: nftHistory } = await apiClient.GET(`/extended/v1/tokens/nft/history`, { + params: { + query: { + asset_identifier: BTC_BNS_CONTRACT, + value: cvToHex( + tupleCV({ + ['name']: bufferCVFromString(bnsName.replace(new RegExp('.btc$'), '')), + ['namespace']: bufferCVFromString('btc'), + }) + ), + tx_metadata: false, + }, + }, }); - if (nftHistory.results.length) { + + if (nftHistory?.results.length) { return nftHistoryToSearchResult(nftHistory.results[0], bnsName); } } catch (e) {} @@ -225,11 +227,10 @@ export function useSearchPageUrl(searchTerm: string, network: Network) { export function useSearchQuery(id: string) { const dispatch = useAppDispatch(); - const { searchApi, blocksApi, nonFungibleTokensApi } = useApi(); const isBtcName = id.endsWith('.btc'); const advancedSearchQuery = parseAdvancedSearchQuery(id); const isAdvancedSearch = advancedSearchQuery.length > 0; - const api = useApi(); + const apiClient = useApiClient(); return useQuery({ queryKey: ['search', isAdvancedSearch ? JSON.stringify(advancedSearchQuery) : id], queryFn: async () => { @@ -254,12 +255,12 @@ export function useSearchQuery(id: string) { const toFilter = advancedSearchQuery.find(({ filterName }) => filterName === 'toAddress'); if (fromFilter?.filterValue?.endsWith('.btc')) { fromFilter.filterValue = - (await searchByBnsName(api, fromFilter.filterValue))?.result.entity_id || + (await searchByBnsName(apiClient, fromFilter.filterValue))?.result.entity_id || fromFilter.filterValue; } if (toFilter?.filterValue?.endsWith('.btc')) { toFilter.filterValue = - (await searchByBnsName(api, toFilter.filterValue))?.result.entity_id || + (await searchByBnsName(apiClient, toFilter.filterValue))?.result.entity_id || toFilter.filterValue; } const fromAddress = advancedSearchQuery.find( @@ -276,17 +277,24 @@ export function useSearchQuery(id: string) { .reduce((acc, { filterValue }) => acc + filterValue + ' ', '') .trim(); // TODO: use term when it's supported - const txsResponse = await api.transactionsApi.getTransactionList({ - limit: 5, - offset: 0, - unanchored: true, - sortBy: GetTransactionListSortByEnum.burn_block_time, - order: GetTransactionListOrderEnum.desc, - ...(fromAddress && { fromAddress: fromAddress }), - ...(toAddress && { toAddress: toAddress }), - ...(startTime && { startTime: Number(startTime) }), - ...(endTime && { endTime: Number(endTime) }), + const { data: txsResponse, error } = await apiClient.GET('/extended/v1/tx/', { + params: { + query: { + limit: 5, + offset: 0, + unanchored: true, + sortBy: 'burn_block_time', + order: 'desc', + ...(fromAddress && { fromAddress: fromAddress }), + ...(toAddress && { toAddress: toAddress }), + ...(startTime && { startTime: Number(startTime) }), + ...(endTime && { endTime: Number(endTime) }), + }, + }, }); + if (error) { + throw new Error(getErrorMessage(error)); + } const txs = (txsResponse?.results as Transaction[]) || ([] as Transaction[]); foundResult = { found: true, @@ -300,18 +308,24 @@ export function useSearchQuery(id: string) { }, }; } else if (isBtcName) { - foundResult = await searchByBnsName(api, id); + foundResult = await searchByBnsName(apiClient, id); } else if (isNumeric(id)) { // Fetch block height if numeric try { - const block = await blocksApi.getBlockByHeight({ height: parseInt(id) }); + const height = parseInt(id); + const { data: block } = await apiClient.GET('/extended/v1/block/by_height/{height}', { + params: { path: { height } }, + }); if (block) { foundResult = blockToSearchResult(block); } } catch (e) {} } else { try { - foundResult = (await searchApi.searchById({ id, includeMetadata: true })) as FoundResult; // TODO: The API needs to add the type + const { data } = await apiClient.GET('/extended/v1/search/{id}', { + params: { path: { id }, query: { include_metadata: true } }, + }); + foundResult = data as FoundResult; } catch (e: any) { try { const data = await e.json(); diff --git a/src/common/queries/useTxById.ts b/src/common/queries/useTxById.ts index d2889c153..ba7053d3d 100644 --- a/src/common/queries/useTxById.ts +++ b/src/common/queries/useTxById.ts @@ -7,22 +7,29 @@ import { import { MempoolTransaction, Transaction } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { DEFAULT_TX_EVENTS_LIMIT } from '../constants/constants'; export function useTxById( txId?: string, options: any = {} ): UseQueryResult { - const api = useApi(); + const apiClient = useApiClient(); return useQuery({ queryKey: ['txById', txId], - queryFn: () => { - return api.transactionsApi.getTransactionById({ - txId: txId!, - eventLimit: DEFAULT_TX_EVENTS_LIMIT, - eventOffset: 0, + queryFn: async () => { + if (!txId) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/tx/{tx_id}', { + params: { + path: { tx_id: txId }, + query: { event_limit: DEFAULT_TX_EVENTS_LIMIT, event_offset: 0 }, + }, }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, staleTime: Infinity, enabled: !!txId, @@ -34,15 +41,21 @@ export function useSuspenseTxById( txId: string, options: any = {} ): UseSuspenseQueryResult { - const api = useApi(); + const apiClient = useApiClient(); return useSuspenseQuery({ queryKey: ['txById', txId], - queryFn: () => { - return api.transactionsApi.getTransactionById({ - txId, - eventLimit: DEFAULT_TX_EVENTS_LIMIT, - eventOffset: 0, + queryFn: async () => { + if (!txId) return undefined; + const { data, error } = await apiClient.GET('/extended/v1/tx/{tx_id}', { + params: { + path: { tx_id: txId }, + query: { event_limit: DEFAULT_TX_EVENTS_LIMIT, event_offset: 0 }, + }, }); + if (error) { + throw new Error(getErrorMessage(error)); + } + return data; }, staleTime: Infinity, ...options, diff --git a/src/common/queries/useTxEventsByIdInfinite.ts b/src/common/queries/useTxEventsByIdInfinite.ts index 6c7cf64ab..c6ef490f4 100644 --- a/src/common/queries/useTxEventsByIdInfinite.ts +++ b/src/common/queries/useTxEventsByIdInfinite.ts @@ -2,7 +2,8 @@ import { InfiniteData, UseInfiniteQueryResult, useInfiniteQuery } from '@tanstac import { Transaction, TransactionEvent } from '@stacks/stacks-blockchain-api-types'; -import { useApi } from '../api/useApi'; +import { getErrorMessage } from '../../api/getErrorMessage'; +import { useApiClient } from '../../api/useApiClient'; import { DEFAULT_TX_EVENTS_LIMIT } from '../constants/constants'; import { GenericResponseType } from '../hooks/useInfiniteQueryResult'; import { getNextPageParam } from '../utils/utils'; @@ -11,18 +12,22 @@ export function useTxEventsByIdInfinite( txId: string, options: any = {} ): UseInfiniteQueryResult>> { - const api = useApi(); + const apiClient = useApiClient(); return useInfiniteQuery({ queryKey: ['tx-by-id-infinite', txId], queryFn: async ({ pageParam }: { pageParam?: number }) => { - const tx = (await api.transactionsApi.getTransactionById({ - txId, - eventLimit: DEFAULT_TX_EVENTS_LIMIT, - eventOffset: pageParam, - })) as Transaction; + const { data: tx, error } = await apiClient.GET('/extended/v1/tx/{tx_id}', { + params: { + path: { tx_id: txId }, + query: { event_limit: DEFAULT_TX_EVENTS_LIMIT, event_offset: pageParam }, + }, + }); + if (error) { + throw new Error(getErrorMessage(error)); + } return { - results: tx.events, - total: tx.event_count, + results: (tx as Transaction).events, + total: (tx as Transaction).event_count, limit: DEFAULT_TX_EVENTS_LIMIT, offset: pageParam, }; diff --git a/src/features/txsFilterAndSort/SortMenu.tsx b/src/features/txsFilterAndSort/SortMenu.tsx index dfa7ab4aa..abb208fd6 100644 --- a/src/features/txsFilterAndSort/SortMenu.tsx +++ b/src/features/txsFilterAndSort/SortMenu.tsx @@ -1,49 +1,42 @@ import { SortDescending } from '@phosphor-icons/react'; import { useCallback, useMemo } from 'react'; -import { - GetMempoolTransactionListOrderByEnum, - GetMempoolTransactionListOrderEnum, - GetTransactionListOrderEnum, - GetTransactionListSortByEnum, -} from '@stacks/blockchain-api-client'; +import { operations } from '@stacks/blockchain-api-client/lib/generated/schema'; import { FilterMenu } from '../../common/components/FilterMenu'; import { useFilterAndSortState } from './useFilterAndSortState'; -function getMempoolTxsSortOptionLabel( - sort: GetMempoolTransactionListOrderByEnum, - order: GetMempoolTransactionListOrderEnum -) { +type MempoolQuery = NonNullable; +type MempoolOrderBy = Exclude; +type MempoolOrder = Exclude; +const MempoolOrderByVals: MempoolOrderBy[] = ['age', 'size', 'fee']; +const MempoolOrderVals: MempoolOrder[] = ['asc', 'desc']; + +type TxsQuery = NonNullable; +type txSortBy = Exclude; +type txOrder = Exclude; +const txSortByVals: txSortBy[] = ['block_height', 'fee', 'burn_block_time']; +const txOrderVals: txOrder[] = ['asc', 'desc']; + +export function getMempoolTxsSortOptionLabel(sort: MempoolOrderBy, order: MempoolOrder) { switch (sort) { - case GetMempoolTransactionListOrderByEnum.age: - return order === GetMempoolTransactionListOrderEnum.asc ? 'Oldest first' : 'Newest first'; - case GetMempoolTransactionListOrderByEnum.size: - return order === GetMempoolTransactionListOrderEnum.asc - ? 'Smallest size first' - : 'Biggest size first'; - case GetMempoolTransactionListOrderByEnum.fee: - return order === GetMempoolTransactionListOrderEnum.asc - ? 'Lowest fee first' - : 'Highest fee first'; + case 'age': + return order === 'asc' ? 'Oldest first' : 'Newest first'; + case 'size': + return order === 'asc' ? 'Smallest size first' : 'Biggest size first'; + case 'fee': + return order === 'asc' ? 'Lowest fee first' : 'Highest fee first'; } } -function getConfirmedTxsSortOptionLabel( - sort: GetTransactionListSortByEnum, - order: GetTransactionListOrderEnum -) { +function getConfirmedTxsSortOptionLabel(sort: string, order: 'asc' | 'desc' | undefined) { switch (sort) { - case GetTransactionListSortByEnum.block_height: - return order === GetTransactionListOrderEnum.asc - ? 'Lowest block height first' - : 'Highest block height first'; - case GetTransactionListSortByEnum.fee: - return order === GetTransactionListOrderEnum.asc - ? 'Lowest transaction fee first' - : 'Highest transaction fee first'; - case GetTransactionListSortByEnum.burn_block_time: - return order === GetTransactionListOrderEnum.asc ? 'Oldest first' : 'Newest first'; + case 'block_height': + return order === 'asc' ? 'Lowest block height first' : 'Highest block height first'; + case 'fee': + return order === 'asc' ? 'Lowest transaction fee first' : 'Highest transaction fee first'; + case 'burn_block_time': + return order === 'asc' ? 'Oldest first' : 'Newest first'; } } @@ -62,19 +55,14 @@ export function MempoolTxsSortMenu() { const menuItems = useMemo( () => - Object.keys(GetMempoolTransactionListOrderByEnum).flatMap(sort => - Object.keys(GetMempoolTransactionListOrderEnum) - .reverse() - .map(order => ({ - onClick: () => { - setMempoolTxsActiveSort(sort as GetMempoolTransactionListOrderByEnum); - setMempoolTxsActiveOrder(order as GetMempoolTransactionListOrderEnum); - }, - label: getMempoolTxsSortOptionLabel( - sort as GetMempoolTransactionListOrderByEnum, - order as GetMempoolTransactionListOrderEnum - ), - })) + MempoolOrderByVals.flatMap(sort => + MempoolOrderVals.reverse().map(order => ({ + onClick: () => { + setMempoolTxsActiveSort(sort); + setMempoolTxsActiveOrder(order); + }, + label: getMempoolTxsSortOptionLabel(sort, order), + })) ), [setMempoolTxsActiveSort, setMempoolTxsActiveOrder] ); @@ -97,19 +85,14 @@ export function ConfirmedTxsSortMenu() { const menuItems = useMemo( () => - Object.keys(GetTransactionListSortByEnum).flatMap(sort => - Object.keys(GetTransactionListOrderEnum) - .reverse() - .map(order => ({ - onClick: () => { - setConfirmedTxsActiveSort(sort as GetTransactionListSortByEnum); - setConfirmedTxsActiveOrder(order as GetTransactionListOrderEnum); - }, - label: getConfirmedTxsSortOptionLabel( - sort as GetTransactionListSortByEnum, - order as GetTransactionListOrderEnum - ), - })) + txSortByVals.flatMap(sort => + txOrderVals.reverse().map(order => ({ + onClick: () => { + setConfirmedTxsActiveSort(sort); + setConfirmedTxsActiveOrder(order); + }, + label: getConfirmedTxsSortOptionLabel(sort, order), + })) ), [setConfirmedTxsActiveSort, setConfirmedTxsActiveOrder] ); diff --git a/src/features/txsFilterAndSort/txsFilterAndSortSlice.ts b/src/features/txsFilterAndSort/txsFilterAndSortSlice.ts index 9fb95765b..476fbc99a 100644 --- a/src/features/txsFilterAndSort/txsFilterAndSortSlice.ts +++ b/src/features/txsFilterAndSort/txsFilterAndSortSlice.ts @@ -1,14 +1,17 @@ import { PayloadAction, Reducer, createSelector, createSlice } from '@reduxjs/toolkit'; -import { - GetMempoolTransactionListOrderByEnum, - GetMempoolTransactionListOrderEnum, - GetTransactionListOrderEnum, -} from '@stacks/blockchain-api-client'; -import { GetTransactionListSortByEnum } from '@stacks/blockchain-api-client'; +import { operations } from '@stacks/blockchain-api-client/lib/generated/schema'; import { RootState } from '../../common/state/store'; +type MempoolQuery = NonNullable; +type MempoolOrderBy = Exclude; +type MempoolOrder = Exclude; + +type TxsQuery = NonNullable; +type TxSortBy = Exclude; +type TxOrder = Exclude; + export enum TxFilterAndSortTypes { HomepageTxFilter = 'HomepageTxFilter', TxsPageTxFilter = 'TxsPageTxFilter', @@ -23,19 +26,19 @@ export type TxFilters = { [key in TxFilterAndSortTypes]: TxFilterAndSortState }; export interface TxFilterAndSortState { activeFilters: string[]; - activeMempoolTxsSort: GetMempoolTransactionListOrderByEnum; - activeMempoolTxsOrder: GetMempoolTransactionListOrderEnum; - activeConfirmedTxsSort: GetTransactionListSortByEnum; - activeConfirmedTxsOrder: GetTransactionListOrderEnum; + activeMempoolTxsSort: MempoolOrderBy; + activeMempoolTxsOrder: MempoolOrder; + activeConfirmedTxsSort: TxSortBy; + activeConfirmedTxsOrder: TxOrder; isVisible: boolean; } export const initialState: TxFilterAndSortState = { activeFilters: [], - activeMempoolTxsSort: GetMempoolTransactionListOrderByEnum.age, - activeMempoolTxsOrder: GetMempoolTransactionListOrderEnum.desc, - activeConfirmedTxsSort: GetTransactionListSortByEnum.burn_block_time, - activeConfirmedTxsOrder: GetTransactionListOrderEnum.desc, + activeMempoolTxsSort: 'age', + activeMempoolTxsOrder: 'desc', + activeConfirmedTxsSort: 'burn_block_time', + activeConfirmedTxsOrder: 'desc', isVisible: false, }; @@ -48,26 +51,17 @@ const createSliceOptions = (filterType: TxFilterAndSortTypes) => ({ }, setMempoolTxsActiveSort: ( state: TxFilterAndSortState, - action: PayloadAction + action: PayloadAction ) => { state.activeMempoolTxsSort = action.payload; }, - setMempoolTxsActiveOrder: ( - state: TxFilterAndSortState, - action: PayloadAction - ) => { + setMempoolTxsActiveOrder: (state: TxFilterAndSortState, action: PayloadAction) => { state.activeMempoolTxsOrder = action.payload; }, - setConfirmedTxsActiveSort: ( - state: TxFilterAndSortState, - action: PayloadAction - ) => { + setConfirmedTxsActiveSort: (state: TxFilterAndSortState, action: PayloadAction) => { state.activeConfirmedTxsSort = action.payload; }, - setConfirmedTxsActiveOrder: ( - state: TxFilterAndSortState, - action: PayloadAction - ) => { + setConfirmedTxsActiveOrder: (state: TxFilterAndSortState, action: PayloadAction) => { state.activeConfirmedTxsOrder = action.payload; }, }, diff --git a/src/features/txsFilterAndSort/useFilterAndSortState.tsx b/src/features/txsFilterAndSort/useFilterAndSortState.tsx index 2a00d6e1d..1abe02f5d 100644 --- a/src/features/txsFilterAndSort/useFilterAndSortState.tsx +++ b/src/features/txsFilterAndSort/useFilterAndSortState.tsx @@ -1,16 +1,19 @@ 'use client'; -import { - GetMempoolTransactionListOrderByEnum, - GetMempoolTransactionListOrderEnum, - GetTransactionListOrderEnum, - GetTransactionListSortByEnum, -} from '@stacks/blockchain-api-client'; +import { operations } from '@stacks/blockchain-api-client/lib/generated/schema'; import { useAppDispatch, useAppSelector } from '../../common/state/hooks'; import { txFilterAndSort } from './txsFilterAndSortSlice'; import { useFilterAndSortScope } from './useFilterAndSortScope'; +type MempoolQuery = NonNullable; +type MempoolOrderBy = Exclude; +type MempoolOrder = Exclude; + +type TxsQuery = NonNullable; +type TxSortBy = Exclude; +type TxOrder = Exclude; + export const useFilterAndSortState = () => { const dispatch = useAppDispatch(); const filterScope = useFilterAndSortScope(); @@ -19,19 +22,19 @@ export const useFilterAndSortState = () => { dispatch(txFilterAndSort[filterScope].actions.setActiveFilters(filters)); }; - const setMempoolTxsActiveSort = (sort: GetMempoolTransactionListOrderByEnum) => { + const setMempoolTxsActiveSort = (sort: MempoolOrderBy) => { dispatch(txFilterAndSort[filterScope].actions.setMempoolTxsActiveSort(sort)); }; - const setMempoolTxsActiveOrder = (order: GetMempoolTransactionListOrderEnum) => { + const setMempoolTxsActiveOrder = (order: MempoolOrder) => { dispatch(txFilterAndSort[filterScope].actions.setMempoolTxsActiveOrder(order)); }; - const setConfirmedTxsActiveSort = (sort: GetTransactionListSortByEnum) => { + const setConfirmedTxsActiveSort = (sort: TxSortBy) => { dispatch(txFilterAndSort[filterScope].actions.setConfirmedTxsActiveSort(sort)); }; - const setConfirmedTxsActiveOrder = (order: GetTransactionListOrderEnum) => { + const setConfirmedTxsActiveOrder = (order: TxOrder) => { dispatch(txFilterAndSort[filterScope].actions.setConfirmedTxsActiveOrder(order)); };