From dd39852d2d9898c1cb1ac5ae479df05a799b9f05 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Wed, 29 May 2024 13:54:42 -0500 Subject: [PATCH] refactor: use stacks query pkg, closes #68 and #5070 --- package.json | 5 +- pnpm-lock.yaml | 52 ++--- src/app/app.tsx | 1 + src/app/common/api/fetch-wrapper.ts | 44 ----- src/app/common/asset-utils.ts | 19 -- .../common/hooks/account/use-account-names.ts | 9 +- .../account/use-refresh-all-account-data.ts | 6 +- .../hooks/balance/use-total-balance.tsx | 6 +- src/app/common/hooks/use-key-actions.ts | 2 +- src/app/common/hooks/use-loading.ts | 1 - .../hooks/use-submit-stx-transaction.ts | 2 +- src/app/components/balance/stx-balance.tsx | 3 +- .../loaders/sip10-tokens-loader.tsx | 4 +- .../components/loaders/stx-balance-loader.tsx | 2 +- .../loaders/stx20-tokens-loader.tsx | 3 +- src/app/components/nonce-setter.tsx | 7 +- .../stacks-transaction-item.tsx | 13 +- .../features/activity-list/activity-list.tsx | 18 +- .../submitted-transaction-item.tsx | 78 ++++---- .../submitted-transaction-list.layout.tsx | 4 +- .../submitted-transaction-list.tsx | 2 +- .../submitted-transaction-list.utils.ts | 6 +- .../stacks-transaction/ft-transfer-item.tsx | 3 +- .../stacks-transaction/stx-transfer-item.tsx | 4 +- .../sip10-token-asset-list-unsupported.tsx | 7 +- .../sip10-token-asset-list.tsx | 7 +- .../features/collectibles/collectibles.tsx | 2 +- .../stacks/stacks-crypto-assets.tsx | 18 +- .../components/increase-fee-actions.tsx | 7 +- .../hooks/use-selected-tx.ts | 7 - .../increase-stx-fee-dialog.tsx | 72 +++---- .../settings/network/network-list-item.tsx | 3 +- .../components/clarity-value-list.tsx | 3 +- .../function-argument-item.tsx | 24 ++- .../hooks/use-stacks-transaction-summary.ts | 8 +- .../hooks/use-transaction-error.ts | 5 +- .../stacks-transaction-signer.tsx | 14 +- .../transaction-error/error-messages.tsx | 6 +- src/app/pages/home/components/send-button.tsx | 4 +- .../receive/components/receive-tokens.tsx | 5 +- .../hooks/use-recipient-bns-name.tsx | 4 +- .../recipient-bns-name-type-field.tsx | 3 +- .../components/bitcoin-recipient-field.tsx | 2 +- .../components/stacks-recipient-field.tsx | 2 +- .../form/sip10/sip10-token-send-form.tsx | 7 +- .../form/sip10/use-sip10-send-form.tsx | 2 +- .../stacks/use-stacks-common-send-form.tsx | 8 +- .../form/stx/use-stx-send-form.tsx | 14 +- src/app/pages/swap/alex-swap-container.tsx | 2 +- .../components/swap-asset-item.tsx | 4 +- .../components/swap-asset-list.tsx | 2 +- .../components/swap-details/swap-details.tsx | 4 +- src/app/pages/swap/hooks/use-alex-swap.tsx | 7 +- src/app/pages/swap/hooks/use-swap-form.tsx | 7 +- src/app/pages/swap/swap.context.ts | 2 +- .../transaction-request.tsx | 14 +- .../alex-sdk/alex-sdk-latest-prices.query.ts | 13 -- .../alex-sdk-swappable-currency.query.ts | 25 --- .../query/common/alex-sdk/alex-sdk.hooks.ts | 113 ----------- .../remote-config/remote-config.query.ts | 4 - src/app/query/query-config.ts | 17 +- .../stacks/balance/account-balance.hooks.ts | 61 ------ .../stacks/balance/account-balance.query.ts | 56 ------ src/app/query/stacks/bns/bns.hooks.ts | 15 -- src/app/query/stacks/bns/bns.query.ts | 63 ------- src/app/query/stacks/bns/bns.utils.ts | 178 ------------------ .../query/stacks/contract/contract.hooks.ts | 36 ---- .../query/stacks/contract/contract.query.ts | 41 ---- src/app/query/stacks/fees/fees.hooks.ts | 118 ------------ src/app/query/stacks/fees/fees.query.ts | 71 ------- src/app/query/stacks/fees/fees.utils.ts | 98 ---------- src/app/query/stacks/hiro-rate-limiter.ts | 28 --- src/app/query/stacks/info/block-time.query.ts | 14 -- src/app/query/stacks/info/info.hooks.ts | 5 - src/app/query/stacks/mempool/mempool.hooks.ts | 97 ---------- src/app/query/stacks/mempool/mempool.query.ts | 40 ---- src/app/query/stacks/mempool/mempool.utils.ts | 60 ------ src/app/query/stacks/network/network.hooks.ts | 6 - src/app/query/stacks/network/network.query.ts | 34 ---- .../stacks/nonce/account-nonces.hooks.ts | 22 --- .../stacks/nonce/account-nonces.query.ts | 51 ----- .../stacks/nonce/account-nonces.utils.ts | 155 --------------- .../query/stacks/sip10/sip10-tokens.hooks.ts | 93 --------- .../query/stacks/sip10/sip10-tokens.spec.ts | 32 ---- .../query/stacks/sip10/sip10-tokens.utils.ts | 52 ----- src/app/query/stacks/stacks-client.ts | 76 -------- .../query/stacks/stx20/stx20-tokens.hooks.ts | 28 --- .../query/stacks/stx20/stx20-tokens.query.ts | 23 --- .../fungible-token-metadata.query.ts | 105 ----------- .../non-fungible-token-holdings.query.ts | 69 ------- .../non-fungible-token-metadata.hooks.ts | 21 --- .../non-fungible-token-metadata.query.ts | 56 ------ .../token-metadata/token-metadata.utils.ts | 22 --- .../transactions/transactions-by-id.query.ts | 59 ------ .../transactions-with-transfers.hooks.ts | 8 - .../transactions-with-transfers.query.ts | 47 ----- .../stacks/stacks-account.models.ts | 11 -- src/app/store/common/api-clients.hooks.ts | 10 +- .../software-keys/software-key.actions.ts | 6 +- .../submitted-transactions.hooks.ts | 34 +++- .../submitted-transactions.slice.ts | 4 +- .../store/transactions/contract-call.hooks.ts | 8 +- .../transactions/post-conditions.hooks.ts | 4 +- src/app/store/transactions/raw.hooks.ts | 39 ---- src/app/store/transactions/raw.ts | 3 - .../transactions/token-transfer.hooks.ts | 12 +- .../store/transactions/transaction.hooks.ts | 7 +- src/shared/constants.ts | 6 - src/shared/models/account.model.ts | 47 ----- src/shared/models/api-types.ts | 6 - src/shared/models/contract-types.ts | 9 - src/shared/route-urls.ts | 2 +- tests/mocks/mock-apis.ts | 2 +- .../mocks/mock-stacks-fees.ts | 0 tests/specs/send/send-btc.spec.ts | 13 +- 115 files changed, 324 insertions(+), 2581 deletions(-) delete mode 100644 src/app/common/api/fetch-wrapper.ts delete mode 100644 src/app/features/dialogs/increase-fee-dialog/hooks/use-selected-tx.ts delete mode 100644 src/app/query/common/alex-sdk/alex-sdk-latest-prices.query.ts delete mode 100644 src/app/query/common/alex-sdk/alex-sdk-swappable-currency.query.ts delete mode 100644 src/app/query/common/alex-sdk/alex-sdk.hooks.ts delete mode 100644 src/app/query/stacks/balance/account-balance.hooks.ts delete mode 100644 src/app/query/stacks/balance/account-balance.query.ts delete mode 100644 src/app/query/stacks/bns/bns.hooks.ts delete mode 100644 src/app/query/stacks/bns/bns.query.ts delete mode 100644 src/app/query/stacks/bns/bns.utils.ts delete mode 100644 src/app/query/stacks/contract/contract.hooks.ts delete mode 100644 src/app/query/stacks/contract/contract.query.ts delete mode 100644 src/app/query/stacks/fees/fees.hooks.ts delete mode 100644 src/app/query/stacks/fees/fees.query.ts delete mode 100644 src/app/query/stacks/fees/fees.utils.ts delete mode 100644 src/app/query/stacks/hiro-rate-limiter.ts delete mode 100644 src/app/query/stacks/info/block-time.query.ts delete mode 100644 src/app/query/stacks/info/info.hooks.ts delete mode 100644 src/app/query/stacks/mempool/mempool.hooks.ts delete mode 100644 src/app/query/stacks/mempool/mempool.query.ts delete mode 100644 src/app/query/stacks/mempool/mempool.utils.ts delete mode 100644 src/app/query/stacks/network/network.hooks.ts delete mode 100644 src/app/query/stacks/network/network.query.ts delete mode 100644 src/app/query/stacks/nonce/account-nonces.hooks.ts delete mode 100644 src/app/query/stacks/nonce/account-nonces.query.ts delete mode 100644 src/app/query/stacks/nonce/account-nonces.utils.ts delete mode 100644 src/app/query/stacks/sip10/sip10-tokens.hooks.ts delete mode 100644 src/app/query/stacks/sip10/sip10-tokens.spec.ts delete mode 100644 src/app/query/stacks/sip10/sip10-tokens.utils.ts delete mode 100644 src/app/query/stacks/stacks-client.ts delete mode 100644 src/app/query/stacks/stx20/stx20-tokens.hooks.ts delete mode 100644 src/app/query/stacks/stx20/stx20-tokens.query.ts delete mode 100644 src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query.ts delete mode 100644 src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-holdings.query.ts delete mode 100644 src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.hooks.ts delete mode 100644 src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.query.ts delete mode 100644 src/app/query/stacks/token-metadata/token-metadata.utils.ts delete mode 100644 src/app/query/stacks/transactions/transactions-by-id.query.ts delete mode 100644 src/app/query/stacks/transactions/transactions-with-transfers.hooks.ts delete mode 100644 src/app/query/stacks/transactions/transactions-with-transfers.query.ts delete mode 100644 src/app/store/transactions/raw.hooks.ts delete mode 100644 src/app/store/transactions/raw.ts delete mode 100644 src/shared/models/account.model.ts delete mode 100644 src/shared/models/api-types.ts delete mode 100644 src/shared/models/contract-types.ts rename src/app/query/stacks/fees/fee.query.mocks.ts => tests/mocks/mock-stacks-fees.ts (100%) diff --git a/package.json b/package.json index 1445fdddc0d..7bf41079eed 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "@leather-wallet/bitcoin": "0.7.2", "@leather-wallet/crypto": "1.0.1", "@leather-wallet/models": "0.8.0", - "@leather-wallet/query": "0.8.3", + "@leather-wallet/query": "0.8.4", "@leather-wallet/tokens": "0.5.2", "@leather-wallet/utils": "0.8.1", "@ledgerhq/hw-transport-webusb": "6.27.19", @@ -268,7 +268,7 @@ "@sentry/react": "7.106.0", "@sentry/webpack-plugin": "2.17.0", "@stacks/connect-react": "22.2.0", - "@stacks/stacks-blockchain-api-types": "6.3.4", + "@stacks/stacks-blockchain-api-types": "7.8.2", "@storybook/addon-docs": "8.0.1", "@storybook/addon-essentials": "8.0.1", "@storybook/addon-interactions": "8.0.1", @@ -360,6 +360,7 @@ "webpack-shell-plugin": "0.5.0" }, "resolutions": { + "braces": "3.0.3", "nanoid": "3.3.4", "socket.io-parser": "4.2.4", "wrap-ansi": "7.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b7d4ebec59f..f6be117bbaf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: + braces: 3.0.3 nanoid: 3.3.4 socket.io-parser: 4.2.4 wrap-ansi: 7.0.0 @@ -43,8 +44,8 @@ importers: specifier: 0.8.0 version: 0.8.0 '@leather-wallet/query': - specifier: 0.8.3 - version: 0.8.3(@stacks/network@6.13.0(encoding@0.1.13))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 0.8.4 + version: 0.8.4(@stacks/network@6.13.0(encoding@0.1.13))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@leather-wallet/tokens': specifier: 0.5.2 version: 0.5.2 @@ -428,8 +429,8 @@ importers: specifier: 22.2.0 version: 22.2.0(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@stacks/stacks-blockchain-api-types': - specifier: 6.3.4 - version: 6.3.4 + specifier: 7.8.2 + version: 7.8.2 '@storybook/addon-docs': specifier: 8.0.1 version: 8.0.1(encoding@0.1.13) @@ -2508,8 +2509,8 @@ packages: '@leather-wallet/prettier-config@0.4.1': resolution: {integrity: sha512-8J1OqfAPtP64MUp3yERQVKnWdp7wHLBjcWlnu68qvS6obxWKkXTZVArHyvADkHRdpmu7rvEzhFRi+fyw00Ui/Q==} - '@leather-wallet/query@0.8.3': - resolution: {integrity: sha512-QRVxRWwmgjUVyiXi2db0Vcf4oFn1KNv1kGvIjmip5p8SFLMzHiPqS93761PPht7rAxpw+ivlyvVbu/Flt1jwyA==} + '@leather-wallet/query@0.8.4': + resolution: {integrity: sha512-Hqqmt9E7ogYumA52zEfHhYyuHBp45svf2tR2yByk7/G+dCuGM8Ejy00sQKYs7iTRfLmjwEGBaBSuKQb6P8OLWA==} peerDependencies: react: '*' @@ -3834,15 +3835,6 @@ packages: '@stacks/rpc-client@1.0.3': resolution: {integrity: sha512-lao7MKCq39VA86v2rJzmgjHKG5bg9LWdLSzvktuEy3lfatVki/hRm6sitkmNhYVcdUVp3YV9gyW6mvu7U9weWw==} - '@stacks/stacks-blockchain-api-types@6.3.4': - resolution: {integrity: sha512-zrjKPGJN4p1azzmh8j0Yj+ZjQ0L9F01qJjAxOtBpapmFbGr1NUuPT1GthIg76y+dobdjSDPN39LpoJG/FbWFLw==} - - '@stacks/stacks-blockchain-api-types@7.11.0': - resolution: {integrity: sha512-R1H5qVJ+KkytWEGrQ4Jk2Ba2NjXexRPniFyY4MVmv+gm5eZ4rtfkmIwsGwGUNo9egUG+KvfKPROaVmiiN0930w==} - - '@stacks/stacks-blockchain-api-types@7.3.6': - resolution: {integrity: sha512-Y8/knKsXKVTtCO46aETAzkBGDrilMyi9cPcuW20UQSPZISD/MApxdEJUOml+OOgvkywO844TSdYqYmtG5D1xWQ==} - '@stacks/stacks-blockchain-api-types@7.8.2': resolution: {integrity: sha512-wcDSdgIZx/ttQfUTPtGJOIyEkTOjmCsC79TaIyxTIiihSgrGppqTuzkwHD/DyuQkcJtUZvDTxMsAXkBKShE1kw==} @@ -6260,8 +6252,8 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} brorand@1.1.0: @@ -7938,8 +7930,8 @@ packages: resolution: {integrity: sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==} engines: {node: '>= 0.4.0'} - fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} finalhandler@1.2.0: @@ -15508,7 +15500,7 @@ snapshots: - '@vue/compiler-sfc' - supports-color - '@leather-wallet/query@0.8.3(@stacks/network@6.13.0(encoding@0.1.13))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@leather-wallet/query@0.8.4(@stacks/network@6.13.0(encoding@0.1.13))(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@fungible-systems/zone-file': 2.0.0 '@hirosystems/token-metadata-api-client': 1.2.0(encoding@0.1.13) @@ -17409,7 +17401,7 @@ snapshots: '@stacks/blockchain-api-client@6.3.4(encoding@0.1.13)': dependencies: - '@stacks/stacks-blockchain-api-types': 7.3.6 + '@stacks/stacks-blockchain-api-types': 7.8.2 '@types/ws': 7.4.7 cross-fetch: 3.1.5(encoding@0.1.13) eventemitter3: 4.0.7 @@ -17501,12 +17493,6 @@ snapshots: transitivePeerDependencies: - encoding - '@stacks/stacks-blockchain-api-types@6.3.4': {} - - '@stacks/stacks-blockchain-api-types@7.11.0': {} - - '@stacks/stacks-blockchain-api-types@7.3.6': {} - '@stacks/stacks-blockchain-api-types@7.8.2': {} '@stacks/storage@6.15.0(encoding@0.1.13)': @@ -21150,9 +21136,9 @@ snapshots: dependencies: balanced-match: 1.0.2 - braces@3.0.2: + braces@3.0.3: dependencies: - fill-range: 7.0.1 + fill-range: 7.1.1 brorand@1.1.0: {} @@ -21449,7 +21435,7 @@ snapshots: chokidar@3.6.0: dependencies: anymatch: 3.1.3 - braces: 3.0.2 + braces: 3.0.3 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -21507,7 +21493,7 @@ snapshots: clarity-codegen@0.3.5(@stacks/transactions@6.15.0(encoding@0.1.13)): dependencies: - '@stacks/stacks-blockchain-api-types': 7.11.0 + '@stacks/stacks-blockchain-api-types': 7.8.2 '@stacks/transactions': 6.15.0(encoding@0.1.13) axios: 1.7.2 lodash: 4.17.21 @@ -23255,7 +23241,7 @@ snapshots: filesize@8.0.7: {} - fill-range@7.0.1: + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -25658,7 +25644,7 @@ snapshots: micromatch@4.0.5: dependencies: - braces: 3.0.2 + braces: 3.0.3 picomatch: 2.3.1 miller-rabin@4.0.1: diff --git a/src/app/app.tsx b/src/app/app.tsx index a49c4d6de69..3af6470f9f5 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -26,6 +26,7 @@ const reactQueryDevToolsEnabled = process.env.REACT_QUERY_DEVTOOLS_ENABLED === ' function ConnectedApp() { const network = useCurrentNetwork(); + return ( = 400; -} - -function trackApiError(url: string, statusCode: number) { - void analytics.track('api_error', { origin: new URL(url).origin, statusCode, url }); -} - -/** - * @deprecated Use `axios` directly instead. Fetch only needed for interation - * with generated stacks blockchain api library - */ -export async function wrappedFetch(input: RequestInfo, init: RequestInit = {}) { - const initHeaders = init.headers || {}; - // eslint-disable-next-line no-restricted-globals - const resp = await fetch(input, { - ...init, - credentials: 'omit', - headers: { ...initHeaders, ...leatherHeaders }, - }); - if (isErrorCode(resp.status)) trackApiError(resp.url, resp.status); - return resp; -} - -axios.interceptors.request.use(request => { - if (request.url?.includes('hiro.so')) - Object.entries(leatherHeaders).forEach(([key, value]) => request.headers.set(key, value)); - - return request; -}); - -axios.interceptors.response.use(response => { - if (isErrorCode(response.status)) trackApiError(response.config.url ?? '', response.status); - return response; -}); diff --git a/src/app/common/asset-utils.ts b/src/app/common/asset-utils.ts index 13b3ba2be65..66660702b54 100644 --- a/src/app/common/asset-utils.ts +++ b/src/app/common/asset-utils.ts @@ -5,25 +5,6 @@ import { isMoneyGreaterThanZero, } from '@leather-wallet/utils'; -export function sortAssetsByName(assets: T) { - return assets - .sort((a, b) => { - if (a.name < b.name) return -1; - if (a.name > b.name) return 1; - return 0; - }) - .sort((a, b) => { - if (a.name === 'STX') return -1; - if (b.name !== 'STX') return 1; - return 0; - }) - .sort((a, b) => { - if (a.name === 'BTC') return -1; - if (b.name !== 'BTC') return 1; - return 0; - }); -} - export function migratePositiveAssetBalancesToTop(assets: T) { const assetsWithPositiveBalance = assets.filter(asset => asset.balance.amount.isGreaterThan(0)); const assetsWithZeroBalance = assets.filter(asset => asset.balance.amount.isEqualTo(0)); diff --git a/src/app/common/hooks/account/use-account-names.ts b/src/app/common/hooks/account/use-account-names.ts index ccdd9925d75..13909a30b7e 100644 --- a/src/app/common/hooks/account/use-account-names.ts +++ b/src/app/common/hooks/account/use-account-names.ts @@ -1,28 +1,27 @@ import { useMemo } from 'react'; +import { useGetBnsNamesOwnedByAddressQuery } from '@leather-wallet/query'; import { isUndefined } from '@leather-wallet/utils'; import { parseIfValidPunycode } from '@app/common/utils'; import { formatAccountName } from '@app/common/utils/format-account-name'; import { getAutogeneratedAccountDisplayName } from '@app/common/utils/get-account-display-name'; -import { useCurrentAccountNames } from '@app/query/stacks/bns/bns.hooks'; -import { useGetBnsNamesOwnedByAddress } from '@app/query/stacks/bns/bns.query'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; export function useCurrentAccountDisplayName() { const account = useCurrentStacksAccount(); - const { data: names = [] } = useCurrentAccountNames(); + const names = useGetBnsNamesOwnedByAddressQuery(account?.address ?? '').data?.names; return useMemo(() => { if (isUndefined(account?.index) && (!account || typeof account?.index !== 'number')) return 'Account'; - if (names[0]) return parseIfValidPunycode(names[0]); + if (names?.length && names[0]) return parseIfValidPunycode(names[0]); return getAutogeneratedAccountDisplayName(account?.index); }, [account, names]); } export function useAccountDisplayName({ address, index }: { index: number; address: string }) { - const query = useGetBnsNamesOwnedByAddress(address, { + const query = useGetBnsNamesOwnedByAddressQuery(address, { select: resp => { const names = resp.names ?? []; return formatAccountName(names[0]) || getAutogeneratedAccountDisplayName(index); diff --git a/src/app/common/hooks/account/use-refresh-all-account-data.ts b/src/app/common/hooks/account/use-refresh-all-account-data.ts index ded147b05d1..e18623bee50 100644 --- a/src/app/common/hooks/account/use-refresh-all-account-data.ts +++ b/src/app/common/hooks/account/use-refresh-all-account-data.ts @@ -1,13 +1,15 @@ import { useCallback } from 'react'; +import { useAccountMempoolQuery } from '@leather-wallet/query'; import { delay } from '@leather-wallet/utils'; -import { useCurrentAccountMempool } from '@app/query/stacks/mempool/mempool.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; // TODO: Can this be removed? It seems like we should be able // to use react-query itself to do this if needed? export function useRefreshAllAccountData() { - const { refetch } = useCurrentAccountMempool(); + const address = useCurrentStacksAccountAddress(); + const { refetch } = useAccountMempoolQuery(address); return useCallback( async (ms?: number) => { if (typeof ms === 'number') await delay(ms); diff --git a/src/app/common/hooks/balance/use-total-balance.tsx b/src/app/common/hooks/balance/use-total-balance.tsx index 9b490830990..eada203e6f7 100644 --- a/src/app/common/hooks/balance/use-total-balance.tsx +++ b/src/app/common/hooks/balance/use-total-balance.tsx @@ -1,10 +1,12 @@ import { useMemo } from 'react'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; +import { + useCryptoCurrencyMarketDataMeanAverage, + useStxCryptoAssetBalance, +} from '@leather-wallet/query'; import { baseCurrencyAmountInQuote, createMoney, i18nFormatCurrency } from '@leather-wallet/utils'; import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; -import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; interface UseTotalBalanceArgs { btcAddress: string; diff --git a/src/app/common/hooks/use-key-actions.ts b/src/app/common/hooks/use-key-actions.ts index 576ddf1d067..f46f79608d5 100644 --- a/src/app/common/hooks/use-key-actions.ts +++ b/src/app/common/hooks/use-key-actions.ts @@ -24,8 +24,8 @@ export function useKeyActions() { const analytics = useAnalytics(); const dispatch = useAppDispatch(); const defaultKeyDetails = useCurrentKeyDetails(); - const stxClient = useStacksClient(); const btcClient = useBitcoinClient(); + const stxClient = useStacksClient(); return useMemo( () => ({ diff --git a/src/app/common/hooks/use-loading.ts b/src/app/common/hooks/use-loading.ts index 3d48ef662f6..eeedecf601c 100644 --- a/src/app/common/hooks/use-loading.ts +++ b/src/app/common/hooks/use-loading.ts @@ -1,7 +1,6 @@ import { useLoadingState } from '@app/store/ui/ui.hooks'; export enum LoadingKeys { - INCREASE_FEE_DRAWER = 'loading/INCREASE_FEE_DRAWER', SUBMIT_SEND_FORM_TRANSACTION = 'loading/SUBMIT_SEND_FORM_TRANSACTION', SUBMIT_SWAP_TRANSACTION = 'loading/SUBMIT_SWAP_TRANSACTION', SUBMIT_TRANSACTION_REQUEST = 'loading/SUBMIT_TRANSACTION_REQUEST', diff --git a/src/app/common/hooks/use-submit-stx-transaction.ts b/src/app/common/hooks/use-submit-stx-transaction.ts index 261c8ce3368..9632667144b 100644 --- a/src/app/common/hooks/use-submit-stx-transaction.ts +++ b/src/app/common/hooks/use-submit-stx-transaction.ts @@ -51,7 +51,7 @@ export function useSubmitTransactionCallback({ loadingKey }: UseSubmitTransactio logger.info('Transaction broadcast', response); submittedTransactionsActions.newTransactionSubmitted({ rawTx: bytesToHex(transaction.serialize()), - txId: safelyFormatHexTxid(response.txid), + txid: safelyFormatHexTxid(response.txid), }); await delay(500); diff --git a/src/app/components/balance/stx-balance.tsx b/src/app/components/balance/stx-balance.tsx index 9ea9e4e826d..5f9a89afc22 100644 --- a/src/app/components/balance/stx-balance.tsx +++ b/src/app/components/balance/stx-balance.tsx @@ -1,7 +1,8 @@ import { useMemo } from 'react'; +import { useStxCryptoAssetBalance } from '@leather-wallet/query'; + import { stacksValue } from '@app/common/stacks-utils'; -import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; import { Caption } from '@app/ui/components/typography/caption'; interface StxBalanceProps { diff --git a/src/app/components/loaders/sip10-tokens-loader.tsx b/src/app/components/loaders/sip10-tokens-loader.tsx index e547e4b751c..14cf70583d1 100644 --- a/src/app/components/loaders/sip10-tokens-loader.tsx +++ b/src/app/components/loaders/sip10-tokens-loader.tsx @@ -1,8 +1,8 @@ import { + type Sip10CryptoAssetFilter, type Sip10TokenAssetDetails, useFilteredSip10Tokens, -} from '@app/query/stacks/sip10/sip10-tokens.hooks'; -import type { Sip10CryptoAssetFilter } from '@app/query/stacks/sip10/sip10-tokens.utils'; +} from '@leather-wallet/query'; interface Sip10TokensLoaderProps { address: string; diff --git a/src/app/components/loaders/stx-balance-loader.tsx b/src/app/components/loaders/stx-balance-loader.tsx index a132572c385..416aea90090 100644 --- a/src/app/components/loaders/stx-balance-loader.tsx +++ b/src/app/components/loaders/stx-balance-loader.tsx @@ -1,7 +1,7 @@ import type { StxCryptoAssetBalance } from '@leather-wallet/models'; +import { useStxCryptoAssetBalance } from '@leather-wallet/query'; import { isFetchedWithSuccess } from '@app/query/query-config'; -import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; interface StxBalanceLoaderProps { address: string; diff --git a/src/app/components/loaders/stx20-tokens-loader.tsx b/src/app/components/loaders/stx20-tokens-loader.tsx index 796f649320d..a2be397d53e 100644 --- a/src/app/components/loaders/stx20-tokens-loader.tsx +++ b/src/app/components/loaders/stx20-tokens-loader.tsx @@ -1,6 +1,5 @@ import type { CryptoAssetBalance, Stx20CryptoAssetInfo } from '@leather-wallet/models'; - -import { useStx20Tokens } from '@app/query/stacks/stx20/stx20-tokens.hooks'; +import { useStx20Tokens } from '@leather-wallet/query'; interface Stx20TokensLoaderProps { address: string; diff --git a/src/app/components/nonce-setter.tsx b/src/app/components/nonce-setter.tsx index debe4d18413..fa5445383ba 100644 --- a/src/app/components/nonce-setter.tsx +++ b/src/app/components/nonce-setter.tsx @@ -2,15 +2,18 @@ import { useAsync } from 'react-async-hook'; import { useFormikContext } from 'formik'; +import { useNextNonce } from '@leather-wallet/query'; + import { StacksSendFormValues, StacksTransactionFormValues } from '@shared/models/form.model'; -import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; export function NonceSetter() { const { setFieldValue, touched, values } = useFormikContext< StacksSendFormValues | StacksTransactionFormValues >(); - const { data: nextNonce } = useNextNonce(); + const stxAddress = useCurrentStacksAccountAddress(); + const { data: nextNonce } = useNextNonce(stxAddress); useAsync(async () => { if (nextNonce?.nonce && !touched.nonce && values.nonce !== nextNonce.nonce) { diff --git a/src/app/components/stacks-transaction-item/stacks-transaction-item.tsx b/src/app/components/stacks-transaction-item/stacks-transaction-item.tsx index 7b1f8baeeaa..5de63ce4ef8 100644 --- a/src/app/components/stacks-transaction-item/stacks-transaction-item.tsx +++ b/src/app/components/stacks-transaction-item/stacks-transaction-item.tsx @@ -1,4 +1,4 @@ -import { createSearchParams, useLocation, useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { StacksTx } from '@leather-wallet/models'; @@ -17,7 +17,6 @@ import { whenPageMode } from '@app/common/utils'; import { openIndexPageInNewTab } from '@app/common/utils/open-in-new-tab'; import { TransactionTitle } from '@app/components/transaction/transaction-title'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { useRawTxIdState } from '@app/store/transactions/raw.hooks'; import { TransactionItemLayout } from '../transaction-item/transaction-item.layout'; import { IncreaseFeeButton } from './increase-fee-button'; @@ -43,7 +42,6 @@ export function StacksTransactionItem({ const { handleOpenStacksTxLink } = useStacksExplorerLink(); const currentAccount = useCurrentStacksAccount(); const analytics = useAnalytics(); - const [_, setRawTxId] = useRawTxIdState(); const { pathname } = useLocation(); const navigate = useNavigate(); const { whenWallet } = useWalletType(); @@ -60,17 +58,16 @@ export function StacksTransactionItem({ const onIncreaseFee = () => { if (!transaction) return; - setRawTxId(transaction.tx_id); - const urlSearchParams = `?${createSearchParams({ txId: transaction.tx_id })}`; + const routeUrl = RouteUrls.IncreaseStxFee.replace(':txid', transaction.tx_id); whenWallet({ ledger: () => whenPageMode({ - full: () => navigate(RouteUrls.IncreaseStxFee), - popup: () => openIndexPageInNewTab(RouteUrls.IncreaseStxFee, urlSearchParams), + full: () => navigate(routeUrl), + popup: () => openIndexPageInNewTab(routeUrl), })(), - software: () => navigate(RouteUrls.IncreaseStxFee), + software: () => navigate(routeUrl), })(); }; diff --git a/src/app/features/activity-list/activity-list.tsx b/src/app/features/activity-list/activity-list.tsx index e5d361634c5..9542f6a5ead 100644 --- a/src/app/features/activity-list/activity-list.tsx +++ b/src/app/features/activity-list/activity-list.tsx @@ -1,19 +1,21 @@ -import { useMemo } from 'react'; +import { useEffect, useMemo } from 'react'; import { Outlet } from 'react-router-dom'; import uniqby from 'lodash.uniqby'; import { useBitcoinPendingTransactions, + useGetAccountTransactionsWithTransfersQuery, useGetBitcoinTransactionsByAddressesQuery, + useStacksPendingTransactions, } from '@leather-wallet/query'; import { LoadingSpinner } from '@app/components/loading-spinner'; import { useConfigBitcoinEnabled } from '@app/query/common/remote-config/remote-config.query'; -import { useStacksPendingTransactions } from '@app/query/stacks/mempool/mempool.hooks'; -import { useGetAccountTransactionsWithTransfersQuery } from '@app/query/stacks/transactions/transactions-with-transfers.query'; import { useZeroIndexTaprootAddress } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useUpdateSubmittedTransactions } from '@app/store/submitted-transactions/submitted-transactions.hooks'; import { useSubmittedTransactions } from '@app/store/submitted-transactions/submitted-transactions.selectors'; import { convertBitcoinTxsToListType, convertStacksTxsToListType } from './activity-list.utils'; @@ -44,6 +46,8 @@ function useTrBitcoinAddress() { export function ActivityList() { const nsBitcoinAddress = useNsBitcoinAddress(); const trBitcoinAddress = useTrBitcoinAddress(); + const stxAddress = useCurrentStacksAccountAddress(); + const updateSubmittedTxs = useUpdateSubmittedTransactions(); const [ { isInitialLoading: isInitialLoadingNsBitcoinTransactions, data: nsBitcoinTransactions = [] }, @@ -62,14 +66,18 @@ export function ActivityList() { const { isInitialLoading: isInitialLoadingStacksTransactions, data: stacksTransactionsWithTransfers, - } = useGetAccountTransactionsWithTransfersQuery(); + } = useGetAccountTransactionsWithTransfersQuery(stxAddress); const { query: { isInitialLoading: isInitialLoadingStacksPendingTransactions }, transactions: stacksPendingTransactions, - } = useStacksPendingTransactions(); + } = useStacksPendingTransactions(stxAddress); const submittedTransactions = useSubmittedTransactions(); const isBitcoinEnabled = useConfigBitcoinEnabled(); + useEffect(() => { + updateSubmittedTxs(stacksPendingTransactions); + }, [stacksPendingTransactions, updateSubmittedTxs]); + const isInitialLoading = isInitialLoadingNsBitcoinTransactions || isInitialLoadingTrBitcoinTransactions || diff --git a/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-item.tsx b/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-item.tsx index f0647cbc222..6212e3d204b 100644 --- a/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-item.tsx +++ b/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-item.tsx @@ -1,23 +1,24 @@ import { StacksTransaction } from '@stacks/transactions'; -import { Box, HStack, Stack } from 'leather-styles/jsx'; +import { HStack, styled } from 'leather-styles/jsx'; +import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; import { useStacksExplorerLink } from '@app/common/hooks/use-stacks-explorer-link'; import { getTxSenderAddress } from '@app/common/transactions/stacks/transaction.utils'; -import { usePressable } from '@app/components/item-hover'; import { TransactionTitle } from '@app/components/transaction/transaction-title'; +import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; import { Caption } from '@app/ui/components/typography/caption'; -import { Title } from '@app/ui/components/typography/title'; +import { Pressable } from '@app/ui/pressable/pressable'; import { SubmittedTransactionIcon } from './submitted-transaction-icon'; import { getSubmittedTransactionDetails } from './submitted-transaction-list.utils'; interface SubmittedTransactionItemProps { transaction: StacksTransaction; - txId: string; + txid: string; } -export function SubmittedTransactionItem({ transaction, txId }: SubmittedTransactionItemProps) { - const [component, bind] = usePressable(true); +export function SubmittedTransactionItem({ transaction, txid }: SubmittedTransactionItemProps) { + const analytics = useAnalytics(); const { handleOpenStacksTxLink } = useStacksExplorerLink(); if (!transaction) return null; @@ -25,45 +26,46 @@ export function SubmittedTransactionItem({ transaction, txId }: SubmittedTransac const submittedTransactionDetails = getSubmittedTransactionDetails({ payload: transaction.payload, senderAddress: getTxSenderAddress(transaction), - txId, + txid, }); + const openTxLink = () => { + void analytics.track('view_transaction'); + handleOpenStacksTxLink({ + searchParams: new URLSearchParams('&submitted=true'), + txid, + }); + }; + if (!submittedTransactionDetails) return null; const { caption, title, value } = submittedTransactionDetails; return ( - - - handleOpenStacksTxLink({ - searchParams: new URLSearchParams('&submitted=true'), - txid: txId, - }) + + } + titleLeft={} + captionLeft={ + + + {caption} + + + Submitted + + } - position="relative" - gap="space.04" - zIndex={2} - > - - - - - - {caption} - - Submitted - - - - {value && {value}} - - - {component} - + titleRight={{value}} + /> + ); } diff --git a/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.layout.tsx b/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.layout.tsx index 8104cf533bc..30daec3a516 100644 --- a/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.layout.tsx +++ b/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.layout.tsx @@ -8,9 +8,9 @@ import { SubmittedTransaction } from '@app/store/submitted-transactions/submitte type SubmittedTransactionListItemProps = SubmittedTransaction; -export function SubmittedTransactionListItem({ rawTx, txId }: SubmittedTransactionListItemProps) { +export function SubmittedTransactionListItem({ rawTx, txid }: SubmittedTransactionListItemProps) { const submittedTx = useMemo(() => deserializeTransaction(rawTx), [rawTx]); - return ; + return ; } interface SubmittedTransactionListLayoutProps { diff --git a/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.tsx b/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.tsx index 2f93a7a43ed..c00dc320340 100644 --- a/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.tsx +++ b/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.tsx @@ -13,7 +13,7 @@ export function SubmittedTransactionList({ txs }: SubmittedTransactionListProps) {txs.map(tx => { if (!tx) return null; - return ; + return ; })} ); diff --git a/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.utils.ts b/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.utils.ts index 78bcdd3a7e2..22aa5eaa670 100644 --- a/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.utils.ts +++ b/src/app/features/activity-list/components/submitted-transaction-list/submitted-transaction-list.utils.ts @@ -14,19 +14,19 @@ interface SubmittedTransactionDetails { interface GetSubmittedTransactionDetailsArgs { payload: StacksTransaction['payload']; senderAddress?: string; - txId: string; + txid: string; } export function getSubmittedTransactionDetails({ payload, senderAddress, - txId, + txid, }: GetSubmittedTransactionDetailsArgs): SubmittedTransactionDetails | null { switch (payload.payloadType) { case PayloadType.TokenTransfer: return { caption: getTxCaption({ tx_type: 'token_transfer', - tx_id: txId, + tx_id: txid, } as StacksTx), title: 'Stacks', value: `-${stacksValue({ diff --git a/src/app/features/activity-list/components/transaction-list/stacks-transaction/ft-transfer-item.tsx b/src/app/features/activity-list/components/transaction-list/stacks-transaction/ft-transfer-item.tsx index 6cd40868eb1..995c26884fe 100644 --- a/src/app/features/activity-list/components/transaction-list/stacks-transaction/ft-transfer-item.tsx +++ b/src/app/features/activity-list/components/transaction-list/stacks-transaction/ft-transfer-item.tsx @@ -1,6 +1,7 @@ import type { AddressTransactionWithTransfers } from '@stacks/stacks-blockchain-api-types'; import { FtTransfer } from '@leather-wallet/models'; +import { isFtAsset, useGetFungibleTokenMetadataQuery } from '@leather-wallet/query'; import { logger } from '@shared/logger'; @@ -12,8 +13,6 @@ import { import { getPrincipalFromContractId } from '@app/common/utils'; import { StacksAssetAvatar } from '@app/components/stacks-asset-avatar'; import { StacksTransactionItem } from '@app/components/stacks-transaction-item/stacks-transaction-item'; -import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query'; -import { isFtAsset } from '@app/query/stacks/token-metadata/token-metadata.utils'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { ArrowDownIcon } from '@app/ui/icons/arrow-down-icon'; import { ArrowUpIcon } from '@app/ui/icons/arrow-up-icon'; diff --git a/src/app/features/activity-list/components/transaction-list/stacks-transaction/stx-transfer-item.tsx b/src/app/features/activity-list/components/transaction-list/stacks-transaction/stx-transfer-item.tsx index 908ee9e93c2..5fac8c5659f 100644 --- a/src/app/features/activity-list/components/transaction-list/stacks-transaction/stx-transfer-item.tsx +++ b/src/app/features/activity-list/components/transaction-list/stacks-transaction/stx-transfer-item.tsx @@ -9,6 +9,8 @@ import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/s import { ArrowDownIcon } from '@app/ui/icons/arrow-down-icon'; import { ArrowUpIcon } from '@app/ui/icons/arrow-up-icon'; +import { TxTransferIconWrapper } from './tx-transfer-icon-wrapper'; + interface StxTransferItemProps { stxTransfer: StxTransfer; parentTx: AddressTransactionWithTransfers; @@ -28,7 +30,7 @@ export function StxTransferItem({ stxTransfer, parentTx }: StxTransferItemProps) return ( } link={parentTx.tx.tx_id} title={title} value={value} diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx index db02882b6fd..b5aaf71fed9 100644 --- a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx @@ -2,8 +2,11 @@ import { useState } from 'react'; import { Stack, styled } from 'leather-styles/jsx'; -import { useAlexCurrencyPriceAsMarketData } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import type { Sip10TokenAssetDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; +import { + type Sip10TokenAssetDetails, + useAlexCurrencyPriceAsMarketData, +} from '@leather-wallet/query'; + import { Accordion } from '@app/ui/components/accordion/accordion'; import { Sip10TokenAssetItem } from './sip10-token-asset-item'; diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx index a29ac8eee2d..80030602010 100644 --- a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx @@ -1,8 +1,11 @@ import { Stack } from 'leather-styles/jsx'; +import { + type Sip10TokenAssetDetails, + useAlexCurrencyPriceAsMarketData, +} from '@leather-wallet/query'; + import { getPrincipalFromContractId } from '@app/common/utils'; -import { useAlexCurrencyPriceAsMarketData } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import type { Sip10TokenAssetDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; import { Sip10TokenAssetItem } from './sip10-token-asset-item'; diff --git a/src/app/features/collectibles/collectibles.tsx b/src/app/features/collectibles/collectibles.tsx index e6a34ce1c2f..1e4d47d84e5 100644 --- a/src/app/features/collectibles/collectibles.tsx +++ b/src/app/features/collectibles/collectibles.tsx @@ -51,7 +51,7 @@ export function Collectibles() { {isNftMetadataEnabled && ( - {account => } + {account => } )} diff --git a/src/app/features/collectibles/components/stacks/stacks-crypto-assets.tsx b/src/app/features/collectibles/components/stacks/stacks-crypto-assets.tsx index 265871ee7d5..13244ef881c 100644 --- a/src/app/features/collectibles/components/stacks/stacks-crypto-assets.tsx +++ b/src/app/features/collectibles/components/stacks/stacks-crypto-assets.tsx @@ -1,21 +1,23 @@ import { useEffect } from 'react'; +import { + useGetBnsNamesOwnedByAddressQuery, + useStacksNonFungibleTokensMetadata, +} from '@leather-wallet/query'; + import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; import { parseIfValidPunycode } from '@app/common/utils'; -import { useCurrentAccountNames } from '@app/query/stacks/bns/bns.hooks'; -import { useStacksNonFungibleTokensMetadata } from '@app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.hooks'; -import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; import { StacksBnsName } from './stacks-bns-name'; import { StacksNonFungibleTokens } from './stacks-non-fungible-tokens'; interface StacksCryptoAssetsProps { - account: StacksAccount; + address: string; } -export function StacksCryptoAssets({ account }: StacksCryptoAssetsProps) { - const { data: names = [] } = useCurrentAccountNames(); +export function StacksCryptoAssets({ address }: StacksCryptoAssetsProps) { + const names = useGetBnsNamesOwnedByAddressQuery(address).data?.names; - const stacksNftsMetadataResp = useStacksNonFungibleTokensMetadata(account); + const stacksNftsMetadataResp = useStacksNonFungibleTokensMetadata(address); const analytics = useAnalytics(); useEffect(() => { @@ -29,7 +31,7 @@ export function StacksCryptoAssets({ account }: StacksCryptoAssetsProps) { return ( <> - {names.map(name => ( + {(names ?? []).map(name => ( ))} {stacksNftsMetadataResp.map((nft, i) => { diff --git a/src/app/features/dialogs/increase-fee-dialog/components/increase-fee-actions.tsx b/src/app/features/dialogs/increase-fee-dialog/components/increase-fee-actions.tsx index e9bd5aa9bfe..f00b31b7c37 100644 --- a/src/app/features/dialogs/increase-fee-dialog/components/increase-fee-actions.tsx +++ b/src/app/features/dialogs/increase-fee-dialog/components/increase-fee-actions.tsx @@ -1,19 +1,18 @@ import { useFormikContext } from 'formik'; -import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading'; import { useWalletType } from '@app/common/use-wallet-type'; import { Button } from '@app/ui/components/button/button'; interface IncreaseFeeActionsProps { - isDisabled?: boolean; isBroadcasting?: boolean; + isDisabled?: boolean; + isLoading?: boolean; onCancel(): void; } export function IncreaseFeeActions(props: IncreaseFeeActionsProps) { - const { onCancel, isDisabled, isBroadcasting } = props; + const { isBroadcasting, isDisabled, isLoading, onCancel } = props; const { handleSubmit } = useFormikContext(); - const { isLoading } = useLoading(LoadingKeys.INCREASE_FEE_DRAWER); const { whenWallet } = useWalletType(); const actionText = whenWallet({ ledger: 'Confirm on Ledger', software: 'Submit' }); diff --git a/src/app/features/dialogs/increase-fee-dialog/hooks/use-selected-tx.ts b/src/app/features/dialogs/increase-fee-dialog/hooks/use-selected-tx.ts deleted file mode 100644 index 83975b47aef..00000000000 --- a/src/app/features/dialogs/increase-fee-dialog/hooks/use-selected-tx.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useTransactionById } from '@app/query/stacks/transactions/transactions-by-id.query'; -import { useRawTxIdState } from '@app/store/transactions/raw.hooks'; - -export function useSelectedTx() { - const [rawTxId] = useRawTxIdState(); - return useTransactionById(rawTxId || '').data; -} diff --git a/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx b/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx index 633be877658..9ea31ed930e 100644 --- a/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx +++ b/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx @@ -1,17 +1,21 @@ import { Suspense, useCallback, useEffect } from 'react'; -import { Outlet, useLocation, useNavigate, useSearchParams } from 'react-router-dom'; +import { Outlet, useLocation, useNavigate, useParams } from 'react-router-dom'; +import { + useStacksRawTransaction, + useStxAvailableUnlockedBalance, + useTransactionById, +} from '@leather-wallet/query'; +import { microStxToStx, stxToMicroStx } from '@leather-wallet/utils'; +import { type StacksTransaction } from '@stacks/transactions'; import BigNumber from 'bignumber.js'; import { Formik } from 'formik'; import { Flex, Stack } from 'leather-styles/jsx'; import * as yup from 'yup'; -import { microStxToStx, stxToMicroStx } from '@leather-wallet/utils'; - import { RouteUrls } from '@shared/route-urls'; import { useRefreshAllAccountData } from '@app/common/hooks/account/use-refresh-all-account-data'; -import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading'; import { stacksValue } from '@app/common/stacks-utils'; import { safelyFormatHexTxid } from '@app/common/utils/safe-handle-txid'; import { stxFeeValidator } from '@app/common/validation/forms/fee-validators'; @@ -19,9 +23,8 @@ import { LoadingSpinner } from '@app/components/loading-spinner'; import { StacksTransactionItem } from '@app/components/stacks-transaction-item/stacks-transaction-item'; import { useStacksBroadcastTransaction } from '@app/features/stacks-transaction-request/hooks/use-stacks-broadcast-transaction'; import { useToast } from '@app/features/toasts/use-toast'; -import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useSubmittedTransactionsActions } from '@app/store/submitted-transactions/submitted-transactions.hooks'; -import { useRawDeserializedTxState, useRawTxIdState } from '@app/store/transactions/raw.hooks'; import { Dialog } from '@app/ui/components/containers/dialog/dialog'; import { Footer } from '@app/ui/components/containers/footers/footer'; import { DialogHeader } from '@app/ui/components/containers/headers/dialog-header'; @@ -30,47 +33,31 @@ import { Caption } from '@app/ui/components/typography/caption'; import { IncreaseFeeActions } from './components/increase-fee-actions'; import { IncreaseFeeField } from './components/increase-fee-field'; -import { useSelectedTx } from './hooks/use-selected-tx'; export function IncreaseStxFeeDialog() { - const [rawTxId, setRawTxId] = useRawTxIdState(); - const { isLoading, setIsIdle } = useLoading(LoadingKeys.INCREASE_FEE_DRAWER); const navigate = useNavigate(); const location = useLocation(); - const [searchParams] = useSearchParams(); - const txIdFromParams = searchParams.get('txId'); + const { txid } = useParams(); const toast = useToast(); const refreshAccountData = useRefreshAllAccountData(); - const tx = useSelectedTx(); - const [, setTxId] = useRawTxIdState(); - const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); + const { data: tx, isLoading: isLoadingTx } = useTransactionById(txid || ''); + const stxAddress = useCurrentStacksAccountAddress(); + const availableUnlockedBalance = useStxAvailableUnlockedBalance(stxAddress); const submittedTransactionsActions = useSubmittedTransactionsActions(); - const rawTx = useRawDeserializedTxState(); + const { isLoading: isLoadingRawTx, rawTx } = useStacksRawTransaction(txid || ''); const { stacksBroadcastTransaction } = useStacksBroadcastTransaction({ token: 'STX', isIncreaseFeeTransaction: true, }); - const fee = Number(rawTx?.auth.spendingCondition?.fee); - useEffect(() => { - if (tx?.tx_status !== 'pending' && rawTx) { - setTxId(null); + if (tx && tx.tx_status !== 'pending') { toast.info('Your transaction went through! No need to speed it up.'); } - }, [rawTx, tx?.tx_status, setTxId, toast]); - - useEffect(() => { - if (!rawTxId && txIdFromParams) { - setRawTxId(txIdFromParams); - } - if (isLoading && !rawTxId) { - setIsIdle(); - } - }, [isLoading, rawTxId, setIsIdle, setRawTxId, txIdFromParams]); + }, [toast, tx, tx?.tx_status]); const onSubmit = useCallback( - async (values: any) => { + async (values: any, rawTx: StacksTransaction) => { if (!tx || !rawTx) return; rawTx.setFee(stxToMicroStx(values.fee).toString()); const txId = tx.tx_id || safelyFormatHexTxid(rawTx.txid()); @@ -78,23 +65,20 @@ export function IncreaseStxFeeDialog() { submittedTransactionsActions.transactionReplacedByFee(txId); await stacksBroadcastTransaction(rawTx); }, - [tx, rawTx, refreshAccountData, submittedTransactionsActions, stacksBroadcastTransaction] + [tx, refreshAccountData, submittedTransactionsActions, stacksBroadcastTransaction] ); - if (!tx || !fee) return ; + if (isLoadingRawTx || isLoadingTx) return ; + if (!txid) return null; + const fee = Number(rawTx?.auth.spendingCondition?.fee); const validationSchema = yup.object({ fee: stxFeeValidator(availableUnlockedBalance) }); - const onClose = () => { - setRawTxId(null); - navigate(RouteUrls.Home); - }; - return ( <> onSubmit(values, rawTx)} validateOnChange={false} validateOnBlur={false} validateOnMount={false} @@ -103,17 +87,15 @@ export function IncreaseStxFeeDialog() { {props => ( <> navigate(RouteUrls.Home)} header={} footer={
{ - setTxId(null); - navigate(RouteUrls.Home); - }} isDisabled={stxToMicroStx(props.values.fee).isEqualTo(fee)} + isLoading={isLoadingRawTx || isLoadingTx} + onCancel={() => navigate(RouteUrls.Home)} />
} @@ -134,7 +116,7 @@ export function IncreaseStxFeeDialog() { {tx && } - {availableUnlockedBalance?.amount && ( + {availableUnlockedBalance.amount && ( Balance: {stacksValue({ diff --git a/src/app/features/settings/network/network-list-item.tsx b/src/app/features/settings/network/network-list-item.tsx index 5d6ca15873c..ece9fddc1d6 100644 --- a/src/app/features/settings/network/network-list-item.tsx +++ b/src/app/features/settings/network/network-list-item.tsx @@ -1,6 +1,7 @@ +import { useNetworkStatus } from '@leather-wallet/query'; + import { defaultCurrentNetwork } from '@shared/constants'; -import { useNetworkStatus } from '@app/query/stacks/network/network.hooks'; import { useCurrentNetworkId, useNetworks } from '@app/store/networks/networks.selectors'; import { NetworkListItemLayout } from './components/network-list-item.layout'; diff --git a/src/app/features/stacks-message-signer/components/clarity-value-list.tsx b/src/app/features/stacks-message-signer/components/clarity-value-list.tsx index bef325e030a..53750c3d8c5 100644 --- a/src/app/features/stacks-message-signer/components/clarity-value-list.tsx +++ b/src/app/features/stacks-message-signer/components/clarity-value-list.tsx @@ -1,6 +1,5 @@ import { bytesToAscii, bytesToHex } from '@stacks/common'; -import { ClarityType, ClarityValue, cvToString } from '@stacks/transactions'; -import { principalToString } from '@stacks/transactions/dist/esm/clarity/types/principalCV'; +import { ClarityType, ClarityValue, cvToString, principalToString } from '@stacks/transactions'; import { TupleDisplayer, diff --git a/src/app/features/stacks-transaction-request/contract-call-details/function-argument-item.tsx b/src/app/features/stacks-transaction-request/contract-call-details/function-argument-item.tsx index 9c737d2d958..ec61ccc2c80 100644 --- a/src/app/features/stacks-transaction-request/contract-call-details/function-argument-item.tsx +++ b/src/app/features/stacks-transaction-request/contract-call-details/function-argument-item.tsx @@ -1,19 +1,37 @@ import { cvToString, deserializeCV, getCVTypeString } from '@stacks/transactions'; +import { useContractFunction } from '@leather-wallet/query'; +import { formatContractId } from '@leather-wallet/utils'; + +import { logger } from '@shared/logger'; + import { Row } from '@app/features/stacks-transaction-request/row'; -import { useContractFunction } from '@app/query/stacks/contract/contract.hooks'; +import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; interface FunctionArgumentProps { arg: string; index: number; } -export function FunctionArgumentItem(props: FunctionArgumentProps): React.JSX.Element { +export function FunctionArgumentItem(props: FunctionArgumentProps) { const { arg, index, ...rest } = props; - const contractFunction = useContractFunction(); + const transactionRequest = useTransactionRequestState(); + const contractFunction = useContractFunction(transactionRequest); const argCV = deserializeCV(Buffer.from(arg, 'hex')); const strValue = cvToString(argCV); + if (!transactionRequest || transactionRequest.txType !== 'contract_call') return null; + + if (!contractFunction) { + logger.error( + `Attempting to call a function (\`${transactionRequest.functionName}\`) that ` + + `does not exist on contract ${formatContractId( + transactionRequest.contractAddress, + transactionRequest.contractName + )}` + ); + } + return ( {contractFunction?.args[index].name || 'unknown'}} diff --git a/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts b/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts index 90ff22c3cdd..7fef9b40775 100644 --- a/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts +++ b/src/app/features/stacks-transaction-request/hooks/use-stacks-transaction-summary.ts @@ -11,7 +11,10 @@ import { } from '@stacks/transactions'; import BigNumber from 'bignumber.js'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; +import { + useCryptoCurrencyMarketDataMeanAverage, + useGetStackNetworkBlockTimeQuery, +} from '@leather-wallet/query'; import { baseCurrencyAmountInQuote, convertToMoneyTypeWithDefaultOfZero, @@ -26,14 +29,13 @@ import { CryptoCurrencies } from '@shared/models/currencies.model'; import { getEstimatedConfirmationTime } from '@app/common/transactions/stacks/transaction.utils'; import { removeTrailingNullCharacters } from '@app/common/utils'; -import { useStacksBlockTime } from '@app/query/stacks/info/info.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; export function useStacksTransactionSummary(token: CryptoCurrencies) { // TODO: unsafe type assumption const tokenMarketData = useCryptoCurrencyMarketDataMeanAverage(token as 'BTC' | 'STX'); const { isTestnet } = useCurrentNetworkState(); - const { data: blockTime } = useStacksBlockTime(); + const { data: blockTime } = useGetStackNetworkBlockTimeQuery(); function formSentSummaryTxState(txId: string, signedTx: StacksTransaction, decimals?: number) { return { diff --git a/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts b/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts index f8f9a78e33a..3616a0d73f4 100644 --- a/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts +++ b/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts @@ -4,6 +4,7 @@ import { ContractCallPayload, TransactionTypes } from '@stacks/connect'; import BigNumber from 'bignumber.js'; import { useFormikContext } from 'formik'; +import { useContractInterface, useStxAvailableUnlockedBalance } from '@leather-wallet/query'; import { stxToMicroStx } from '@leather-wallet/utils'; import { StacksTransactionFormValues } from '@shared/models/form.model'; @@ -12,8 +13,6 @@ import { useDefaultRequestParams } from '@app/common/hooks/use-default-request-s import { initialSearchParams } from '@app/common/initial-search-params'; import { validateStacksAddress } from '@app/common/stacks-utils'; import { TransactionErrorReason } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; -import { useContractInterface } from '@app/query/stacks/contract/contract.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; @@ -28,7 +27,7 @@ export function useTransactionError() { const { values } = useFormikContext(); const currentAccount = useCurrentStacksAccount(); - const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); + const availableUnlockedBalance = useStxAvailableUnlockedBalance(currentAccount?.address ?? ''); return useMemo(() => { if (!origin) return TransactionErrorReason.ExpiredRequest; diff --git a/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx b/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx index f8be5d67fcf..fd76d49252a 100644 --- a/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx +++ b/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx @@ -7,6 +7,11 @@ import { Flex } from 'leather-styles/jsx'; import * as yup from 'yup'; import { FeeTypes } from '@leather-wallet/models'; +import { + useCalculateStacksTxFees, + useNextNonce, + useStxAvailableUnlockedBalance, +} from '@leather-wallet/query'; import { stxToMicroStx } from '@leather-wallet/utils'; import { HIGH_FEE_WARNING_LEARN_MORE_URL_STX } from '@shared/constants'; @@ -27,9 +32,7 @@ import { PostConditionModeWarning } from '@app/features/stacks-transaction-reque import { PostConditions } from '@app/features/stacks-transaction-request/post-conditions/post-conditions'; import { StxTransferDetails } from '@app/features/stacks-transaction-request/stx-transfer-details/stx-transfer-details'; import { TransactionError } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; -import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; -import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; import { Link } from '@app/ui/components/link/link'; @@ -56,9 +59,10 @@ export function StacksTransactionSigner({ const transactionRequest = useTransactionRequestState(); const { data: stxFees } = useCalculateStacksTxFees(stacksTransaction); const analytics = useAnalytics(); - const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); + const stxAddress = useCurrentStacksAccountAddress(); + const availableUnlockedBalance = useStxAvailableUnlockedBalance(stxAddress); const navigate = useNavigate(); - const { data: nextNonce } = useNextNonce(); + const { data: nextNonce } = useNextNonce(stxAddress); const { search } = useLocation(); useOnMount(() => { diff --git a/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx b/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx index f4b190d7135..fa82c937eb9 100644 --- a/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx +++ b/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx @@ -4,6 +4,7 @@ import { Navigate } from 'react-router-dom'; import { STXTransferPayload, TransactionTypes } from '@stacks/connect'; import { Flex, HStack, Stack } from 'leather-styles/jsx'; +import { useStxAvailableUnlockedBalance } from '@leather-wallet/query'; import { truncateMiddle } from '@leather-wallet/utils'; import { RouteUrls } from '@shared/route-urls'; @@ -14,7 +15,7 @@ import { useScrollLock } from '@app/common/hooks/use-scroll-lock'; import { stacksValue } from '@app/common/stacks-utils'; import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog'; import { ErrorMessage } from '@app/features/stacks-transaction-request/transaction-error/error-message'; -import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; import { Button } from '@app/ui/components/button/button'; @@ -60,7 +61,8 @@ export const FeeInsufficientFundsErrorMessage = memo(props => { export const StxTransferInsufficientFundsErrorMessage = memo(props => { const pendingTransaction = useTransactionRequestState(); - const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); + const stxAddress = useCurrentStacksAccountAddress(); + const availableUnlockedBalance = useStxAvailableUnlockedBalance(stxAddress); return ( diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/hooks/use-recipient-bns-name.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/hooks/use-recipient-bns-name.tsx index 8183600cf5c..cc31600d41e 100644 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/hooks/use-recipient-bns-name.tsx +++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/hooks/use-recipient-bns-name.tsx @@ -2,12 +2,12 @@ import { useCallback, useState } from 'react'; import { useFormikContext } from 'formik'; +import { type StacksClient, useStacksClient } from '@leather-wallet/query'; + import { FormErrorMessages } from '@shared/error-messages'; import { logger } from '@shared/logger'; import { BitcoinSendFormValues, StacksSendFormValues } from '@shared/models/form.model'; -import { StacksClient } from '@app/query/stacks/stacks-client'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; // Handles validating the BNS name lookup diff --git a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-bns-name-type-field.tsx b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-bns-name-type-field.tsx index 80ae47f32c9..f711027c84a 100644 --- a/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-bns-name-type-field.tsx +++ b/src/app/pages/send/send-crypto-asset-form/components/recipient-fields/recipient-bns-name-type-field.tsx @@ -2,10 +2,11 @@ import { useEffect, useState } from 'react'; import { useFormikContext } from 'formik'; +import type { StacksClient } from '@leather-wallet/query'; + import { BitcoinSendFormValues, StacksSendFormValues } from '@shared/models/form.model'; import { RecipientAddressTypeField } from '@app/pages/send/send-crypto-asset-form/components/recipient-address-type-field'; -import { StacksClient } from '@app/query/stacks/stacks-client'; import { RecipientAddressDisplayer } from './components/recipient-address-displayer'; import { useRecipientBnsName } from './hooks/use-recipient-bns-name'; diff --git a/src/app/pages/send/send-crypto-asset-form/family/bitcoin/components/bitcoin-recipient-field.tsx b/src/app/pages/send/send-crypto-asset-form/family/bitcoin/components/bitcoin-recipient-field.tsx index 25baae6cf92..2eff5592d6f 100644 --- a/src/app/pages/send/send-crypto-asset-form/family/bitcoin/components/bitcoin-recipient-field.tsx +++ b/src/app/pages/send/send-crypto-asset-form/family/bitcoin/components/bitcoin-recipient-field.tsx @@ -1,4 +1,4 @@ -import { fetchBtcNameOwner } from '@app/query/stacks/bns/bns.utils'; +import { fetchBtcNameOwner } from '@leather-wallet/query'; import { RecipientField } from '../../../components/recipient-fields/recipient-field'; diff --git a/src/app/pages/send/send-crypto-asset-form/family/stacks/components/stacks-recipient-field.tsx b/src/app/pages/send/send-crypto-asset-form/family/stacks/components/stacks-recipient-field.tsx index 9c93ffe8755..0e86af2e843 100644 --- a/src/app/pages/send/send-crypto-asset-form/family/stacks/components/stacks-recipient-field.tsx +++ b/src/app/pages/send/send-crypto-asset-form/family/stacks/components/stacks-recipient-field.tsx @@ -1,4 +1,4 @@ -import { fetchNameOwner } from '@app/query/stacks/bns/bns.utils'; +import { fetchNameOwner } from '@leather-wallet/query'; import { RecipientField } from '../../../components/recipient-fields/recipient-field'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form.tsx index 4cf413e2691..eabdb1aa72c 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form.tsx @@ -1,12 +1,12 @@ import { useNavigate, useParams } from 'react-router-dom'; import type { CryptoAssetBalance, MarketData, Sip10CryptoAssetInfo } from '@leather-wallet/models'; +import { useAlexCurrencyPriceAsMarketData, useSip10Token } from '@leather-wallet/query'; import { RouteUrls } from '@shared/route-urls'; import { useToast } from '@app/features/toasts/use-toast'; -import { useAlexCurrencyPriceAsMarketData } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import { useSip10Token } from '@app/query/stacks/sip10/sip10-tokens.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { Sip10TokenSendFormContainer } from './sip10-token-send-form-container'; @@ -19,7 +19,8 @@ interface Sip10TokenSendFormLoaderProps { } function Sip10TokenSendFormLoader({ children }: Sip10TokenSendFormLoaderProps) { const { contractId } = useParams(); - const token = useSip10Token(contractId ?? ''); + const stxAddress = useCurrentStacksAccountAddress(); + const token = useSip10Token(stxAddress, contractId ?? ''); const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); const toast = useToast(); const navigate = useNavigate(); diff --git a/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx index 83d17b18e5c..45de3eaafcb 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx @@ -4,6 +4,7 @@ import { FormikHelpers } from 'formik'; import * as yup from 'yup'; import type { CryptoAssetBalance, Sip10CryptoAssetInfo } from '@leather-wallet/models'; +import { useCalculateStacksTxFees } from '@leather-wallet/query'; import { convertAmountToBaseUnit } from '@leather-wallet/utils'; import { logger } from '@shared/logger'; @@ -11,7 +12,6 @@ import { StacksSendFormValues } from '@shared/models/form.model'; import { getSafeImageCanonicalUri } from '@app/common/stacks-utils'; import { stacksFungibleTokenAmountValidator } from '@app/common/validation/forms/amount-validators'; -import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useFtTokenTransferUnsignedTx, useGenerateFtTokenTransferUnsignedTx, diff --git a/src/app/pages/send/send-crypto-asset-form/form/stacks/use-stacks-common-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stacks/use-stacks-common-send-form.tsx index a762de44aef..70e41204043 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stacks/use-stacks-common-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stacks/use-stacks-common-send-form.tsx @@ -2,6 +2,7 @@ import BigNumber from 'bignumber.js'; import { FormikHelpers } from 'formik'; import { FeeTypes, type Money } from '@leather-wallet/models'; +import { useNextNonce } from '@leather-wallet/query'; import { isEmpty } from '@leather-wallet/utils'; import { HIGH_FEE_AMOUNT_STX } from '@shared/constants'; @@ -11,7 +12,6 @@ import { StacksSendFormValues } from '@shared/models/form.model'; import { stxMemoValidator } from '@app/common/validation/forms/memo-validators'; import { stxRecipientValidator } from '@app/common/validation/forms/recipient-validators'; import { nonceValidator } from '@app/common/validation/nonce-validators'; -import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; @@ -28,8 +28,8 @@ export function useStacksCommonSendForm({ availableTokenBalance, }: UseStacksCommonSendFormArgs) { const routeState = useSendFormRouteState(); - const { data: nextNonce } = useNextNonce(); - const currentAccountStxAddress = useCurrentStacksAccountAddress(); + const stxAddress = useCurrentStacksAccountAddress(); + const { data: nextNonce } = useNextNonce(stxAddress); const currentNetwork = useCurrentNetworkState(); const initialValues: StacksSendFormValues = createDefaultInitialFormValues({ @@ -58,7 +58,7 @@ export function useStacksCommonSendForm({ initialValues, availableTokenBalance, checkFormValidation, - recipient: stxRecipientValidator(currentAccountStxAddress, currentNetwork), + recipient: stxRecipientValidator(stxAddress, currentNetwork), memo: stxMemoValidator(FormErrorMessages.MemoExceedsLimit), nonce: nonceValidator, }; diff --git a/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx index d81c4ad2c2e..5f71ce2a123 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx @@ -3,6 +3,11 @@ import { useMemo } from 'react'; import { FormikHelpers } from 'formik'; import * as yup from 'yup'; +import { + useCalculateStacksTxFees, + useStacksValidateFeeByNonce, + useStxAvailableUnlockedBalance, +} from '@leather-wallet/query'; import { convertAmountToBaseUnit } from '@leather-wallet/utils'; import { STX_DECIMALS } from '@shared/constants'; @@ -15,9 +20,7 @@ import { } from '@app/common/validation/forms/amount-validators'; import { stxFeeValidator } from '@app/common/validation/forms/fee-validators'; import { useUpdatePersistedSendFormValues } from '@app/features/popup-send-form-restoration/use-update-persisted-send-form-values'; -import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; -import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; -import { useStacksValidateFeeByNonce } from '@app/query/stacks/mempool/mempool.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useGenerateStxTokenTransferUnsignedTx, useStxTokenTransferUnsignedTxState, @@ -32,8 +35,9 @@ export function useStxSendForm() { const generateTx = useGenerateStxTokenTransferUnsignedTx(); const { onFormStateChange } = useUpdatePersistedSendFormValues(); const sendFormNavigate = useSendFormNavigate(); - const { changeFeeByNonce } = useStacksValidateFeeByNonce(); - const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); + const address = useCurrentStacksAccountAddress(); + const { changeFeeByNonce } = useStacksValidateFeeByNonce(address); + const availableUnlockedBalance = useStxAvailableUnlockedBalance(address); const sendMaxBalance = useMemo( () => diff --git a/src/app/pages/swap/alex-swap-container.tsx b/src/app/pages/swap/alex-swap-container.tsx index 25a4476f7c0..96417348e14 100644 --- a/src/app/pages/swap/alex-swap-container.tsx +++ b/src/app/pages/swap/alex-swap-container.tsx @@ -11,6 +11,7 @@ import { } from '@stacks/transactions'; import BigNumber from 'bignumber.js'; +import { defaultSwapFee } from '@leather-wallet/query'; import { isDefined, isUndefined } from '@leather-wallet/utils'; import { logger } from '@shared/logger'; @@ -21,7 +22,6 @@ import { migratePositiveAssetBalancesToTop } from '@app/common/asset-utils'; import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading'; import { useWalletType } from '@app/common/use-wallet-type'; import { NonceSetter } from '@app/components/nonce-setter'; -import { defaultSwapFee } from '@app/query/common/alex-sdk/alex-sdk.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useGenerateStacksContractCallUnsignedTx } from '@app/store/transactions/contract-call.hooks'; import { useSignStacksTransaction } from '@app/store/transactions/transaction.hooks'; diff --git a/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx b/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx index 539b21ab720..df342fd71e2 100644 --- a/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx +++ b/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx @@ -1,11 +1,9 @@ import { SwapSelectors } from '@tests/selectors/swap.selectors'; +import { type SwapAsset, isFtAsset, useGetFungibleTokenMetadataQuery } from '@leather-wallet/query'; import { formatMoneyWithoutSymbol } from '@leather-wallet/utils'; import { convertAssetBalanceToFiat } from '@app/common/asset-utils'; -import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query'; -import { isFtAsset } from '@app/query/stacks/token-metadata/token-metadata.utils'; import { Avatar, defaultFallbackDelay, getAvatarFallback } from '@app/ui/components/avatar/avatar'; import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; import { Pressable } from '@app/ui/pressable/pressable'; diff --git a/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-list.tsx b/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-list.tsx index 726fae57c51..49c3ba6c2ff 100644 --- a/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-list.tsx +++ b/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-list.tsx @@ -5,6 +5,7 @@ import BigNumber from 'bignumber.js'; import { useFormikContext } from 'formik'; import { Stack } from 'leather-styles/jsx'; +import type { SwapAsset } from '@leather-wallet/query'; import { convertAmountToFractionalUnit, createMoney, @@ -15,7 +16,6 @@ import { import { RouteUrls } from '@shared/route-urls'; import { useSwapContext } from '@app/pages/swap/swap.context'; -import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks'; import { SwapFormValues } from '../../../hooks/use-swap-form'; import { SwapAssetItem } from './swap-asset-item'; diff --git a/src/app/pages/swap/components/swap-details/swap-details.tsx b/src/app/pages/swap/components/swap-details/swap-details.tsx index 3571c47b207..f0f612565be 100644 --- a/src/app/pages/swap/components/swap-details/swap-details.tsx +++ b/src/app/pages/swap/components/swap-details/swap-details.tsx @@ -2,6 +2,7 @@ import { SwapSelectors } from '@tests/selectors/swap.selectors'; import BigNumber from 'bignumber.js'; import { HStack, styled } from 'leather-styles/jsx'; +import { useGetStackNetworkBlockTimeQuery } from '@leather-wallet/query'; import { createMoneyFromDecimal, formatMoneyPadded, @@ -12,7 +13,6 @@ import { import { getEstimatedConfirmationTime } from '@app/common/transactions/stacks/transaction.utils'; import { SwapSubmissionData, useSwapContext } from '@app/pages/swap/swap.context'; -import { useStacksBlockTime } from '@app/query/stacks/info/info.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; import { ChevronUpIcon } from '@app/ui/icons/chevron-up-icon'; @@ -37,7 +37,7 @@ const sponsoredFeeLabel = export function SwapDetails() { const { swapSubmissionData } = useSwapContext(); const { isTestnet } = useCurrentNetworkState(); - const { data: blockTime } = useStacksBlockTime(); + const { data: blockTime } = useGetStackNetworkBlockTimeQuery(); if ( isUndefined(swapSubmissionData) || diff --git a/src/app/pages/swap/hooks/use-alex-swap.tsx b/src/app/pages/swap/hooks/use-alex-swap.tsx index d66bc80cd59..bc60f984621 100644 --- a/src/app/pages/swap/hooks/use-alex-swap.tsx +++ b/src/app/pages/swap/hooks/use-alex-swap.tsx @@ -2,10 +2,12 @@ import { useState } from 'react'; import BigNumber from 'bignumber.js'; +import { type SwapAsset, useAlexSwappableAssets } from '@leather-wallet/query'; + import { logger } from '@shared/logger'; import { alex } from '@shared/utils/alex-sdk'; -import { type SwapAsset, useAlexSwappableAssets } from '@app/query/common/alex-sdk/alex-sdk.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { SwapSubmissionData } from '../swap.context'; @@ -15,7 +17,8 @@ export function useAlexSwap() { const [swapSubmissionData, setSwapSubmissionData] = useState(); const [slippage, _setSlippage] = useState(0.04); const [isFetchingExchangeRate, setIsFetchingExchangeRate] = useState(false); - const { data: swapAssets = [] } = useAlexSwappableAssets(); + const address = useCurrentStacksAccountAddress(); + const { data: swapAssets = [] } = useAlexSwappableAssets(address); async function fetchQuoteAmount( base: SwapAsset, diff --git a/src/app/pages/swap/hooks/use-swap-form.tsx b/src/app/pages/swap/hooks/use-swap-form.tsx index 26e90044ba7..56447871fb2 100644 --- a/src/app/pages/swap/hooks/use-swap-form.tsx +++ b/src/app/pages/swap/hooks/use-swap-form.tsx @@ -2,13 +2,13 @@ import BigNumber from 'bignumber.js'; import * as yup from 'yup'; import { FeeTypes } from '@leather-wallet/models'; +import { type SwapAsset, useNextNonce } from '@leather-wallet/query'; import { convertAmountToFractionalUnit, createMoney } from '@leather-wallet/utils'; import { FormErrorMessages } from '@shared/error-messages'; import { StacksTransactionFormValues } from '@shared/models/form.model'; -import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useSwapContext } from '../swap.context'; @@ -21,7 +21,8 @@ export interface SwapFormValues extends StacksTransactionFormValues { export function useSwapForm() { const { isFetchingExchangeRate } = useSwapContext(); - const { data: nextNonce } = useNextNonce(); + const stxAddress = useCurrentStacksAccountAddress(); + const { data: nextNonce } = useNextNonce(stxAddress); const initialValues: SwapFormValues = { fee: '0', diff --git a/src/app/pages/swap/swap.context.ts b/src/app/pages/swap/swap.context.ts index deec0ddbd6f..f24d89d488e 100644 --- a/src/app/pages/swap/swap.context.ts +++ b/src/app/pages/swap/swap.context.ts @@ -1,6 +1,6 @@ import { createContext, useContext } from 'react'; -import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks'; +import type { SwapAsset } from '@leather-wallet/query'; import { SwapFormValues } from './hooks/use-swap-form'; diff --git a/src/app/pages/transaction-request/transaction-request.tsx b/src/app/pages/transaction-request/transaction-request.tsx index a24d284b8f1..44d7f24ddcc 100644 --- a/src/app/pages/transaction-request/transaction-request.tsx +++ b/src/app/pages/transaction-request/transaction-request.tsx @@ -6,6 +6,11 @@ import { Flex } from 'leather-styles/jsx'; import * as yup from 'yup'; import { FeeTypes } from '@leather-wallet/models'; +import { + useCalculateStacksTxFees, + useNextNonce, + useStxAvailableUnlockedBalance, +} from '@leather-wallet/query'; import { HIGH_FEE_WARNING_LEARN_MORE_URL_STX } from '@shared/constants'; import { logger } from '@shared/logger'; @@ -30,9 +35,7 @@ import { PostConditions } from '@app/features/stacks-transaction-request/post-co import { StxTransferDetails } from '@app/features/stacks-transaction-request/stx-transfer-details/stx-transfer-details'; import { SubmitAction } from '@app/features/stacks-transaction-request/submit-action'; import { TransactionError } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; -import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; -import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; import { useGenerateUnsignedStacksTransaction, @@ -48,8 +51,9 @@ function TransactionRequestBase() { const { data: stxFees } = useCalculateStacksTxFees(unsignedTx.transaction); const analytics = useAnalytics(); const generateUnsignedTx = useGenerateUnsignedStacksTransaction(); - const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); - const { data: nextNonce } = useNextNonce(); + const stxAddress = useCurrentStacksAccountAddress(); + const availableUnlockedBalance = useStxAvailableUnlockedBalance(stxAddress); + const { data: nextNonce } = useNextNonce(stxAddress); const navigate = useNavigate(); const { stacksBroadcastTransaction } = useStacksBroadcastTransaction({ token: 'STX' }); diff --git a/src/app/query/common/alex-sdk/alex-sdk-latest-prices.query.ts b/src/app/query/common/alex-sdk/alex-sdk-latest-prices.query.ts deleted file mode 100644 index 2cc29031120..00000000000 --- a/src/app/query/common/alex-sdk/alex-sdk-latest-prices.query.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { alex } from '@shared/utils/alex-sdk'; - -export function useAlexSdkLatestPricesQuery() { - return useQuery(['alex-sdk-latest-prices'], async () => alex.getLatestPrices(), { - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, - retryDelay: 1000 * 60, - staleTime: 1000 * 60 * 10, - }); -} diff --git a/src/app/query/common/alex-sdk/alex-sdk-swappable-currency.query.ts b/src/app/query/common/alex-sdk/alex-sdk-swappable-currency.query.ts deleted file mode 100644 index ba62c6001cb..00000000000 --- a/src/app/query/common/alex-sdk/alex-sdk-swappable-currency.query.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import type { TokenInfo } from 'alex-sdk'; - -import { alex } from '@shared/utils/alex-sdk'; - -import type { AppUseQueryConfig } from '@app/query/query-config'; - -const queryOptions = { - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, - retryDelay: 1000 * 60, - staleTime: 1000 * 60 * 10, -}; - -export function useAlexSdkSwappableCurrencyQuery( - options?: AppUseQueryConfig -) { - return useQuery({ - queryKey: ['alex-sdk-swappable-currencies'], - queryFn: async () => alex.fetchSwappableCurrency(), - ...queryOptions, - ...options, - }); -} diff --git a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts deleted file mode 100644 index 316a6ee8ff7..00000000000 --- a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { useCallback } from 'react'; - -import { Currency, type TokenInfo } from 'alex-sdk'; -import BigNumber from 'bignumber.js'; - -import { - type MarketData, - type Money, - createMarketData, - createMarketPair, -} from '@leather-wallet/models'; -import { convertAmountToFractionalUnit, createMoney, isDefined } from '@leather-wallet/utils'; - -import { logger } from '@shared/logger'; - -import { sortAssetsByName } from '@app/common/asset-utils'; -import { getPrincipalFromContractId } from '@app/common/utils'; -import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; -import { useTransferableSip10Tokens } from '@app/query/stacks/sip10/sip10-tokens.hooks'; -import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { getAvatarFallback } from '@app/ui/components/avatar/avatar'; - -import { useAlexSdkLatestPricesQuery } from './alex-sdk-latest-prices.query'; -import { useAlexSdkSwappableCurrencyQuery } from './alex-sdk-swappable-currency.query'; - -export interface SwapAsset { - address?: string; - balance: Money; - currency: Currency; - displayName?: string; - fallback: string; - icon: string; - name: string; - marketData: MarketData | null; - principal: string; -} - -export const defaultSwapFee = createMoney(1000000, 'STX'); - -export function useAlexCurrencyPriceAsMarketData() { - const { data: supportedCurrencies = [] } = useAlexSdkSwappableCurrencyQuery(); - const { data: prices } = useAlexSdkLatestPricesQuery(); - - return useCallback( - (principal: string, symbol: string) => { - const tokenInfo = supportedCurrencies - .filter(isDefined) - .find(token => getPrincipalFromContractId(token.contractAddress) === principal); - if (!prices || !tokenInfo) - return createMarketData(createMarketPair(symbol, 'USD'), createMoney(0, 'USD')); - const currency = tokenInfo.id as Currency; - const price = convertAmountToFractionalUnit(new BigNumber(prices[currency] ?? 0), 2); - return createMarketData(createMarketPair(symbol, 'USD'), createMoney(price, 'USD')); - }, - [prices, supportedCurrencies] - ); -} - -function useCreateSwapAsset() { - const address = useCurrentStacksAccountAddress(); - const { data: prices } = useAlexSdkLatestPricesQuery(); - const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); - const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); - const sip10Tokens = useTransferableSip10Tokens(address); - - return useCallback( - (tokenInfo?: TokenInfo): SwapAsset | undefined => { - if (!prices) return; - if (!tokenInfo) { - logger.error('No token data found to swap'); - return; - } - - const currency = tokenInfo.id as Currency; - const principal = getPrincipalFromContractId(tokenInfo.contractAddress); - const availableBalance = sip10Tokens.find(token => token.info.contractId === principal) - ?.balance.availableBalance; - - const swapAsset = { - currency, - fallback: getAvatarFallback(tokenInfo.name), - icon: tokenInfo.icon, - name: tokenInfo.name, - principal, - }; - - if (currency === Currency.STX) { - return { - ...swapAsset, - balance: availableUnlockedBalance, - displayName: 'Stacks', - marketData: priceAsMarketData(principal, availableUnlockedBalance.symbol), - }; - } - - return { - ...swapAsset, - balance: availableBalance ?? createMoney(0, tokenInfo.name, tokenInfo.decimals), - marketData: availableBalance - ? priceAsMarketData(principal, availableBalance.symbol) - : priceAsMarketData(principal, tokenInfo.name), - }; - }, - [availableUnlockedBalance, priceAsMarketData, prices, sip10Tokens] - ); -} - -export function useAlexSwappableAssets() { - const createSwapAsset = useCreateSwapAsset(); - return useAlexSdkSwappableCurrencyQuery({ - select: resp => sortAssetsByName(resp.map(createSwapAsset).filter(isDefined)), - }); -} diff --git a/src/app/query/common/remote-config/remote-config.query.ts b/src/app/query/common/remote-config/remote-config.query.ts index de7920a4a27..3623a17e8ae 100644 --- a/src/app/query/common/remote-config/remote-config.query.ts +++ b/src/app/query/common/remote-config/remote-config.query.ts @@ -11,10 +11,6 @@ export { useActiveFiatProviders, useHasFiatProviders, useRecoverUninscribedTaprootUtxosFeatureEnabled, - useConfigFeeEstimationsMaxEnabled, - useConfigFeeEstimationsMaxValues, - useConfigFeeEstimationsMinEnabled, - useConfigFeeEstimationsMinValues, useConfigNftMetadataEnabled, useConfigRunesEnabled, useConfigSwapEnabled, diff --git a/src/app/query/query-config.ts b/src/app/query/query-config.ts index ec1998617d7..5e8cc86808e 100644 --- a/src/app/query/query-config.ts +++ b/src/app/query/query-config.ts @@ -1,19 +1,6 @@ -import { - type QueryObserverSuccessResult, - UseQueryOptions, - type UseQueryResult, -} from '@tanstack/react-query'; - -type AllowedReactQueryConfigOptions = keyof Pick< - UseQueryOptions, - 'select' | 'initialData' | 'onSuccess' | 'onSettled' | 'onError' | 'suspense' ->; - -export type AppUseQueryConfig = Pick< - UseQueryOptions, - AllowedReactQueryConfigOptions ->; +import { type QueryObserverSuccessResult, type UseQueryResult } from '@tanstack/react-query'; +// TODO #40: Import from query pkg and remove export function isFetchedWithSuccess( query: UseQueryResult ): query is QueryObserverSuccessResult { diff --git a/src/app/query/stacks/balance/account-balance.hooks.ts b/src/app/query/stacks/balance/account-balance.hooks.ts deleted file mode 100644 index 7f13ffdec85..00000000000 --- a/src/app/query/stacks/balance/account-balance.hooks.ts +++ /dev/null @@ -1,61 +0,0 @@ -import BigNumber from 'bignumber.js'; - -import type { Money, StxCryptoAssetBalance } from '@leather-wallet/models'; -import { createMoney, subtractMoney, sumMoney } from '@leather-wallet/utils'; - -import { AccountBalanceStxKeys, type AddressBalanceResponse } from '@shared/models/account.model'; - -import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { accountBalanceStxKeys } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; - -import { - useMempoolTxsInboundBalance, - useMempoolTxsOutboundBalance, -} from '../mempool/mempool.hooks'; -import { useStacksAccountBalanceQuery } from './account-balance.query'; - -function createStxMoney(resp: AddressBalanceResponse) { - return Object.fromEntries( - accountBalanceStxKeys.map(key => [key, { amount: new BigNumber(resp.stx[key]), symbol: 'STX' }]) - ) as Record; -} - -function createStxCryptoAssetBalance( - stxMoney: Record, - inboundBalance: Money, - outboundBalance: Money -): StxCryptoAssetBalance { - const totalBalance = createMoney(stxMoney.balance.amount, 'STX'); - const unlockedBalance = subtractMoney(stxMoney.balance, stxMoney.locked); - - return { - availableBalance: subtractMoney(totalBalance, outboundBalance), - availableUnlockedBalance: subtractMoney(unlockedBalance, outboundBalance), - inboundBalance, - lockedBalance: createMoney(stxMoney.locked.amount, 'STX'), - outboundBalance, - pendingBalance: subtractMoney(sumMoney([totalBalance, inboundBalance]), outboundBalance), - totalBalance, - unlockedBalance, - }; -} - -export function useStxCryptoAssetBalance(address: string) { - const inboundBalance = useMempoolTxsInboundBalance(address); - const outboundBalance = useMempoolTxsOutboundBalance(address); - return useStacksAccountBalanceQuery(address, { - select: resp => - createStxCryptoAssetBalance(createStxMoney(resp), inboundBalance, outboundBalance), - }); -} - -export function useCurrentStxAvailableUnlockedBalance() { - const address = useCurrentStacksAccountAddress(); - return useStxCryptoAssetBalance(address).data?.unlockedBalance ?? createMoney(0, 'STX'); -} - -export function useStacksAccountBalanceFungibleTokens(address: string) { - return useStacksAccountBalanceQuery(address, { - select: resp => resp.fungible_tokens, - }); -} diff --git a/src/app/query/stacks/balance/account-balance.query.ts b/src/app/query/stacks/balance/account-balance.query.ts deleted file mode 100644 index 300a2222a52..00000000000 --- a/src/app/query/stacks/balance/account-balance.query.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { AddressBalanceResponse } from '@shared/models/account.model'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { StacksClient } from '@app/query/stacks/stacks-client'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; - -const staleTime = 1 * 60 * 1000; - -const balanceQueryOptions = { - staleTime, - keepPreviousData: false, - refetchOnMount: true, -} as const; - -function fetchAccountBalance(client: StacksClient, signal?: AbortSignal) { - return async (principal: string) => { - // Coercing type with client-side one that's more accurate - return client.accountsApi.getAccountBalance( - { - principal, - }, - { - signal, - } - ) as Promise; - }; -} - -type FetchAccountBalanceResp = Awaited>>; - -export function useStacksAccountBalanceQuery( - address: string, - options?: AppUseQueryConfig -) { - const client = useStacksClient(); - const limiter = useHiroApiRateLimiter(); - const network = useCurrentNetworkState(); - - return useQuery({ - enabled: !!address, - queryKey: ['get-address-stx-balance', address, network.id], - queryFn: async ({ signal }) => { - return limiter.add(() => fetchAccountBalance(client, signal)(address), { - signal, - throwOnTimeout: true, - }); - }, - ...balanceQueryOptions, - ...options, - }); -} diff --git a/src/app/query/stacks/bns/bns.hooks.ts b/src/app/query/stacks/bns/bns.hooks.ts deleted file mode 100644 index 1e428ee51cf..00000000000 --- a/src/app/query/stacks/bns/bns.hooks.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { logger } from '@shared/logger'; - -import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; - -import { useGetBnsNamesOwnedByAddress } from './bns.query'; - -export function useCurrentAccountNames() { - const principal = useCurrentStacksAccountAddress(); - return useGetBnsNamesOwnedByAddress(principal, { - select: resp => { - if (principal === '') logger.error('No principal defined'); - return resp.names ?? []; - }, - }); -} diff --git a/src/app/query/stacks/bns/bns.query.ts b/src/app/query/stacks/bns/bns.query.ts deleted file mode 100644 index 073532c58f8..00000000000 --- a/src/app/query/stacks/bns/bns.query.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BnsNamesOwnByAddressResponse } from '@stacks/stacks-blockchain-api-types'; -import { useQuery } from '@tanstack/react-query'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { QueryPrefixes } from '@app/query/query-prefixes'; -import { StacksClient } from '@app/query/stacks/stacks-client'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; -import { fetchNamesForAddress } from './bns.utils'; - -const staleTime = 24 * 60 * 60 * 1000; // 24 hours - -const bnsQueryOptions = { - cacheTime: Infinity, - staleTime: staleTime, - refetchOnMount: false, - refetchInterval: false, - refetchOnReconnect: false, -} as const; - -type BnsNameFetcher = (address: string) => Promise; - -interface GetBnsNameFetcherFactoryArgs { - client: StacksClient; - isTestnet: boolean; - signal?: AbortSignal; -} - -function getBnsNameFetcherFactory({ - client, - isTestnet, - signal, -}: GetBnsNameFetcherFactoryArgs): BnsNameFetcher { - return async (address: string) => { - return fetchNamesForAddress({ client, address, isTestnet, signal }); - }; -} - -type BnsNameFetcherResp = Awaited>>; - -export function useGetBnsNamesOwnedByAddress( - address: string, - options?: AppUseQueryConfig -) { - const client = useStacksClient(); - const limiter = useHiroApiRateLimiter(); - const { isTestnet } = useCurrentNetworkState(); - return useQuery({ - enabled: address !== '', - queryKey: [QueryPrefixes.BnsNamesByAddress, address], - queryFn: async ({ signal }) => { - return limiter.add(() => fetchNamesForAddress({ client, address, isTestnet, signal }), { - signal, - priority: 2, - throwOnTimeout: true, - }); - }, - ...bnsQueryOptions, - ...options, - }); -} diff --git a/src/app/query/stacks/bns/bns.utils.ts b/src/app/query/stacks/bns/bns.utils.ts deleted file mode 100644 index 2f95bd779b3..00000000000 --- a/src/app/query/stacks/bns/bns.utils.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { parseZoneFile } from '@fungible-systems/zone-file'; -import { asciiToBytes, bytesToAscii } from '@stacks/common'; -import { BnsNamesOwnByAddressResponse } from '@stacks/stacks-blockchain-api-types'; -import { - BufferCV, - ClarityType, - OptionalCV, - PrincipalCV, - TupleCV, - UIntCV, - bufferCV, - cvToHex, - deserializeCV, - standardPrincipalCV, - tupleCV, -} from '@stacks/transactions'; -import { principalToString } from '@stacks/transactions/dist/esm/clarity/types/principalCV'; - -import { isString, isUndefined } from '@leather-wallet/utils'; - -import { StacksClient } from '@app/query/stacks/stacks-client'; - -const bnsContractConsts = { - contractAddress: 'SP1JTCR202ECC6333N7ZXD7MK7E3ZTEEE1MJ73C60', - contractName: 'bnsx-registry', -} as const; - -// Fetch an address's "primary name" from the BNSx contract. -async function fetchBnsxName( - client: StacksClient, - address: string, - signal?: AbortSignal -): Promise { - try { - const addressCV = standardPrincipalCV(address); - const addressHex = cvToHex(addressCV); - const res = await client.smartContractsApi.callReadOnlyFunction( - { - ...bnsContractConsts, - functionName: 'get-primary-name', - tip: 'latest', - readOnlyFunctionArgs: { - sender: address, - arguments: [addressHex], - }, - }, - { signal } - ); - if (!res.okay || !res.result) return null; - const { result } = res; - const cv = deserializeCV(result) as OptionalCV< - TupleCV<{ name: BufferCV; namespace: BufferCV }> - >; - if (cv.type === ClarityType.OptionalNone) return null; - const { name, namespace } = cv.value.data; - const fullName = `${bytesToAscii(name.buffer)}.${bytesToAscii(namespace.buffer)}`; - return fullName; - } catch (error) { - return null; - } -} - -// Fetch the owner of a BNSx name -// If the name is not registered in BNSx, returns null. -// -// Subdomains don't exist on-chain, so if the name looks like a subdomain, -// the function exits early with `null`. -async function fetchBnsxOwner(client: StacksClient, fqn: string): Promise { - const nameParts = fqn.split('.'); - - // If the name includes a subdomain, it's not on-chain. Return null - if (nameParts.length !== 2) return null; - - const [name, namespace] = nameParts; - const nameCV = tupleCV({ - name: bufferCV(asciiToBytes(name)), - namespace: bufferCV(asciiToBytes(namespace)), - }); - - const res = await client.smartContractsApi.callReadOnlyFunction({ - ...bnsContractConsts, - functionName: 'get-name-properties', - tip: 'latest', - readOnlyFunctionArgs: { - // Sender is irrelevant - sender: bnsContractConsts.contractAddress, - arguments: [cvToHex(nameCV)], - }, - }); - - if (!res.okay || !res.result) return null; - const { result } = res; - const cv = deserializeCV(result) as OptionalCV>; - if (cv.type === ClarityType.OptionalNone) return null; - const ownerCV = cv.value.data.owner; - return principalToString(ownerCV); -} - -// Fetch names owned by an address. -// -// If `isTestnet` is `false` (aka mainnet), names are fetched from the -// BNSx contract directly. -interface FetchNamesForAddressArgs { - client: StacksClient; - address: string; - isTestnet: boolean; - signal?: AbortSignal; -} -export async function fetchNamesForAddress({ - client, - address, - isTestnet, - signal, -}: FetchNamesForAddressArgs): Promise { - const fetchFromApi = async () => { - return client.namesApi.getNamesOwnedByAddress({ address, blockchain: 'stacks' }, { signal }); - }; - if (isTestnet) { - return fetchFromApi(); - } - - // Return BNSx name if available, otherwise return names from API. - const [bnsxName, bnsNames] = await Promise.all([ - fetchBnsxName(client, address, signal), - fetchFromApi(), - ]); - const bnsName = 'names' in bnsNames ? bnsNames.names[0] : null; - const names: string[] = []; - if (bnsName) names.push(bnsName); - if (bnsxName) names.push(bnsxName); - return { names }; -} - -/** - * Fetch the owner of a name. - * - * If on mainnet, this function concurrently fetches a BNSx owner from the contract - * and a BNS owner from the API. - */ -export async function fetchNameOwner(client: StacksClient, name: string, isTestnet: boolean) { - const fetchFromApi = async () => { - const res = await client.namesApi.getNameInfo({ name }); - if (isUndefined(res.address)) return null; - if (!isString(res.address) || res.address.length === 0) return null; - return res.address; - }; - if (isTestnet) { - return fetchFromApi(); - } - - const [bnsxOwner, apiOwner] = await Promise.all([fetchBnsxOwner(client, name), fetchFromApi()]); - return bnsxOwner ?? apiOwner; -} - -/** - * Fetch the zonefile-based BTC address for a specific name. - * The BTC address is found via the `_btc._addr` TXT record, - * as specified in https://www.newinternetlabs.com/blog/standardizing-names-for-bitcoin-addresses/ - * - * The value returned from this function is not validated. - */ -export async function fetchBtcNameOwner( - client: StacksClient, - name: string -): Promise { - try { - const nameResponse = await client.namesApi.getNameInfo({ name }); - const zonefile = parseZoneFile(nameResponse.zonefile); - if (!zonefile.txt) return null; - const btcRecord = zonefile.txt.find(record => record.name === '_btc._addr'); - if (isUndefined(btcRecord)) return null; - const txtValue = btcRecord.txt; - return isString(txtValue) ? txtValue : txtValue[0] ?? null; - } catch (error) { - // name not found or invalid zonefile - return null; - } -} diff --git a/src/app/query/stacks/contract/contract.hooks.ts b/src/app/query/stacks/contract/contract.hooks.ts deleted file mode 100644 index cfc0d1a6cb7..00000000000 --- a/src/app/query/stacks/contract/contract.hooks.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { ContractCallPayload } from '@stacks/connect'; -import type { ContractInterfaceFunction } from '@stacks/rpc-client'; - -import { logger } from '@shared/logger'; - -import { formatContractId } from '@app/common/utils'; -import { useGetContractInterface } from '@app/query/stacks/contract/contract.query'; -import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; - -export function useContractInterface(transactionRequest: ContractCallPayload | null) { - return useGetContractInterface(transactionRequest).data; -} - -export function useContractFunction() { - const transactionRequest = useTransactionRequestState(); - const contractInterface = useContractInterface(transactionRequest as ContractCallPayload); - - if (!transactionRequest || transactionRequest.txType !== 'contract_call' || !contractInterface) - return; - - const selectedFunction = contractInterface.functions.find((func: ContractInterfaceFunction) => { - return func.name === transactionRequest.functionName; - }); - - if (!selectedFunction) { - logger.error( - `Attempting to call a function (\`${transactionRequest.functionName}\`) that ` + - `does not exist on contract ${formatContractId( - transactionRequest.contractAddress, - transactionRequest.contractName - )}` - ); - } - - return selectedFunction; -} diff --git a/src/app/query/stacks/contract/contract.query.ts b/src/app/query/stacks/contract/contract.query.ts deleted file mode 100644 index fc20b8a8a7d..00000000000 --- a/src/app/query/stacks/contract/contract.query.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ContractCallPayload, TransactionTypes } from '@stacks/connect'; -import { useQuery } from '@tanstack/react-query'; - -import { ContractInterfaceResponseWithFunctions } from '@shared/models/contract-types'; - -import { useStacksClient } from '@app/store/common/api-clients.hooks'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; - -export function useGetContractInterface(transactionRequest: ContractCallPayload | null) { - const { smartContractsApi } = useStacksClient(); - const limiter = useHiroApiRateLimiter(); - - async function fetchContractInterface() { - if (!transactionRequest || transactionRequest?.txType !== TransactionTypes.ContractCall) return; - const contractAddress = transactionRequest.contractAddress; - const contractName = transactionRequest.contractName; - return limiter.add( - () => - smartContractsApi.getContractInterface({ - contractAddress, - contractName, - }) as unknown as Promise, - { - throwOnTimeout: true, - } - ); - } - - return useQuery({ - enabled: transactionRequest?.txType === TransactionTypes.ContractCall && !!transactionRequest, - queryKey: [ - 'contract-interface', - transactionRequest?.contractName, - transactionRequest?.contractAddress, - ], - queryFn: fetchContractInterface, - staleTime: 30 * 60 * 1000, - refetchOnWindowFocus: false, - }); -} diff --git a/src/app/query/stacks/fees/fees.hooks.ts b/src/app/query/stacks/fees/fees.hooks.ts deleted file mode 100644 index b56c9bd2305..00000000000 --- a/src/app/query/stacks/fees/fees.hooks.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { useMemo } from 'react'; - -import { StacksTransaction } from '@stacks/transactions'; - -import { - FeeCalculationTypes, - type Fees, - type Money, - StacksFeeEstimate, -} from '@leather-wallet/models'; -import type { StacksTxFeeEstimation } from '@leather-wallet/query'; -import { createMoney } from '@leather-wallet/utils'; - -import { - useConfigFeeEstimationsMaxEnabled, - useConfigFeeEstimationsMaxValues, - useConfigFeeEstimationsMinEnabled, - useConfigFeeEstimationsMinValues, -} from '@app/query/common/remote-config/remote-config.query'; -import { useGetStacksTransactionFeeEstimationQuery } from '@app/query/stacks/fees/fees.query'; - -import { - defaultFeesMaxValuesAsMoney, - defaultFeesMinValuesAsMoney, - defaultStacksFees, - feeEstimationQueryFailedSilently, - getDefaultSimulatedFeeEstimations, - getEstimatedUnsignedStacksTxByteLength, - getFeeEstimationsWithCappedValues, - getSerializedUnsignedStacksTxPayload, -} from './fees.utils'; - -function useFeeEstimationsMaxValues() { - const configFeeEstimationsMaxEnabled = useConfigFeeEstimationsMaxEnabled(); - const configFeeEstimationsMaxValues = useConfigFeeEstimationsMaxValues(); - - if (configFeeEstimationsMaxEnabled === false) return; - return configFeeEstimationsMaxValues || defaultFeesMaxValuesAsMoney; -} - -function useFeeEstimationsMinValues() { - const configFeeEstimationsMinEnabled = useConfigFeeEstimationsMinEnabled(); - const configFeeEstimationsMinValues = useConfigFeeEstimationsMinValues(); - - if (configFeeEstimationsMinEnabled === false) return; - return configFeeEstimationsMinValues || defaultFeesMinValuesAsMoney; -} - -interface ParseStacksTxFeeEstimationResponseArgs { - feeEstimation: StacksTxFeeEstimation; - maxValues?: Money[]; - minValues?: Money[]; - txByteLength: number | null; -} -function parseStacksTxFeeEstimationResponse({ - feeEstimation, - maxValues, - minValues, - txByteLength, -}: ParseStacksTxFeeEstimationResponseArgs): Fees { - if (!!feeEstimation.error) return defaultStacksFees; - if (txByteLength && feeEstimationQueryFailedSilently(feeEstimation)) { - return { - blockchain: 'stacks', - estimates: getDefaultSimulatedFeeEstimations(txByteLength), - calculation: FeeCalculationTypes.DefaultSimulated, - }; - } - - const stacksFeeEstimates: StacksFeeEstimate[] = feeEstimation.estimations.map(estimate => { - return { - fee: createMoney(estimate.fee, 'STX'), - feeRate: estimate.fee_rate, - }; - }); - - if (feeEstimation.estimations && feeEstimation.estimations.length) { - const feeEstimationsWithCappedValues = getFeeEstimationsWithCappedValues( - stacksFeeEstimates, - maxValues, - minValues - ); - return { - blockchain: 'stacks', - estimates: feeEstimationsWithCappedValues, - calculation: FeeCalculationTypes.FeesCapped, - }; - } - return { - blockchain: 'stacks', - estimates: stacksFeeEstimates ?? [], - calculation: FeeCalculationTypes.Api, - }; -} - -export function useCalculateStacksTxFees(unsignedTx?: StacksTransaction) { - const feeEstimationsMaxValues = useFeeEstimationsMaxValues(); - const feeEstimationsMinValues = useFeeEstimationsMinValues(); - - const { txByteLength, txPayload } = useMemo(() => { - if (!unsignedTx) return { txByteLength: null, txPayload: '' }; - - return { - txByteLength: getEstimatedUnsignedStacksTxByteLength(unsignedTx), - txPayload: getSerializedUnsignedStacksTxPayload(unsignedTx), - }; - }, [unsignedTx]); - - return useGetStacksTransactionFeeEstimationQuery(txByteLength, txPayload, { - select: resp => - parseStacksTxFeeEstimationResponse({ - feeEstimation: resp, - maxValues: feeEstimationsMaxValues, - minValues: feeEstimationsMinValues, - txByteLength, - }), - }); -} diff --git a/src/app/query/stacks/fees/fees.query.ts b/src/app/query/stacks/fees/fees.query.ts deleted file mode 100644 index 8fa46805a92..00000000000 --- a/src/app/query/stacks/fees/fees.query.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; -import PQueue from 'p-queue'; - -import type { StacksTxFeeEstimation } from '@leather-wallet/query'; - -import { logger } from '@shared/logger'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; -import { defaultApiFeeEstimations } from './fees.utils'; - -function fetchTransactionFeeEstimation(currentNetwork: any, limiter: PQueue) { - return async (estimatedLen: number | null, transactionPayload: string) => { - const resp = await limiter.add( - () => - axios.post( - currentNetwork.chain.stacks.url + '/v2/fees/transaction', - { - estimated_len: estimatedLen, - transaction_payload: transactionPayload, - } - ), - { - priority: 2, - throwOnTimeout: true, - } - ); - return resp.data; - }; -} - -type FetchTransactionFeeEstimationResp = Awaited< - ReturnType> ->; - -export function useGetStacksTransactionFeeEstimationQuery< - T extends unknown = FetchTransactionFeeEstimationResp, ->( - estimatedLen: number | null, - transactionPayload: string, - options?: AppUseQueryConfig -) { - const currentNetwork = useCurrentNetworkState(); - const limiter = useHiroApiRateLimiter(); - - return useQuery({ - enabled: transactionPayload !== '', - queryKey: ['stacks-tx-fee-estimation', transactionPayload], - queryFn: async () => { - try { - return await fetchTransactionFeeEstimation(currentNetwork, limiter)( - estimatedLen, - transactionPayload - ); - } catch (err) { - logger.error('Error getting stacks tx fee estimation', { err }); - return { - cost_scalar_change_by_byte: 0, - estimated_cost: {}, - estimated_cost_scalar: 0, - estimations: defaultApiFeeEstimations, - error: err ?? 'Error', - } as StacksTxFeeEstimation; - } - }, - ...options, - }); -} diff --git a/src/app/query/stacks/fees/fees.utils.ts b/src/app/query/stacks/fees/fees.utils.ts deleted file mode 100644 index 90816b71fae..00000000000 --- a/src/app/query/stacks/fees/fees.utils.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { bytesToHex } from '@stacks/common'; -import { StacksTransaction, serializePayload } from '@stacks/transactions'; -import { BigNumber } from 'bignumber.js'; - -import { FeeCalculationTypes, Fees, Money, StacksFeeEstimate } from '@leather-wallet/models'; -import type { FeeEstimation, StacksTxFeeEstimation } from '@leather-wallet/query'; -import { createMoney } from '@leather-wallet/utils'; - -import { DEFAULT_FEE_RATE } from '@shared/constants'; - -const defaultFeesMaxValues = [500000, 750000, 2000000]; -const defaultFeesMinValues = [2500, 3000, 3500]; - -export const defaultFeesMaxValuesAsMoney = [ - createMoney(defaultFeesMaxValues[0], 'STX'), - createMoney(defaultFeesMaxValues[1], 'STX'), - createMoney(defaultFeesMaxValues[2], 'STX'), -]; -export const defaultFeesMinValuesAsMoney = [ - createMoney(defaultFeesMinValues[0], 'STX'), - createMoney(defaultFeesMinValues[1], 'STX'), - createMoney(defaultFeesMinValues[2], 'STX'), -]; - -export const defaultApiFeeEstimations: FeeEstimation[] = [ - { fee: defaultFeesMinValues[0], fee_rate: 0 }, - { fee: defaultFeesMinValues[1], fee_rate: 0 }, - { fee: defaultFeesMinValues[2], fee_rate: 0 }, -]; - -const defaultStacksFeeEstimates: StacksFeeEstimate[] = [ - { fee: defaultFeesMinValuesAsMoney[0], feeRate: 0 }, - { fee: defaultFeesMinValuesAsMoney[1], feeRate: 0 }, - { fee: defaultFeesMinValuesAsMoney[2], feeRate: 0 }, -]; - -export const defaultStacksFees: Fees = { - blockchain: 'stacks', - estimates: defaultStacksFeeEstimates, - calculation: FeeCalculationTypes.Default, -}; - -export function feeEstimationQueryFailedSilently(feeEstimation: StacksTxFeeEstimation) { - return !!(feeEstimation && (!!feeEstimation.error || !feeEstimation.estimations.length)); -} - -export function getEstimatedUnsignedStacksTxByteLength(transaction: StacksTransaction) { - return transaction.serialize().byteLength; -} - -export function getSerializedUnsignedStacksTxPayload(transaction: StacksTransaction) { - return bytesToHex(serializePayload(transaction.payload)); -} - -export function getFeeEstimationsWithCappedValues( - feeEstimations: StacksFeeEstimate[], - feeEstimationsMaxValues: Money[] | undefined, - feeEstimationsMinValues: Money[] | undefined -) { - return feeEstimations.map((feeEstimation, index) => { - if ( - feeEstimationsMaxValues && - feeEstimation.fee.amount.isGreaterThan(feeEstimationsMaxValues[index].amount) - ) { - return { fee: feeEstimationsMaxValues[index], feeRate: 0 }; - } else if ( - feeEstimationsMinValues && - feeEstimation.fee.amount.isLessThan(feeEstimationsMinValues[index].amount) - ) { - return { fee: feeEstimationsMinValues[index], feeRate: 0 }; - } else { - return feeEstimation; - } - }); -} - -function calculateFeeFromFeeRate(txBytes: number, feeRate: number) { - return new BigNumber(txBytes).multipliedBy(feeRate); -} - -const marginFromDefaultFeeDecimalPercent = 0.1; - -export function getDefaultSimulatedFeeEstimations( - estimatedByteLength: number -): StacksFeeEstimate[] { - const fee = calculateFeeFromFeeRate(estimatedByteLength, DEFAULT_FEE_RATE); - return [ - { - fee: createMoney(fee.multipliedBy(1 - marginFromDefaultFeeDecimalPercent), 'STX'), - feeRate: 0, - }, - { fee: createMoney(fee, 'STX'), feeRate: 0 }, - { - fee: createMoney(fee.multipliedBy(1 + marginFromDefaultFeeDecimalPercent), 'STX'), - feeRate: 0, - }, - ]; -} diff --git a/src/app/query/stacks/hiro-rate-limiter.ts b/src/app/query/stacks/hiro-rate-limiter.ts deleted file mode 100644 index 8ca587591b9..00000000000 --- a/src/app/query/stacks/hiro-rate-limiter.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ChainID } from '@stacks/transactions'; -import PQueue from 'p-queue'; - -import { whenStacksChainId } from '@shared/crypto/stacks/stacks.utils'; - -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -const hiroStacksMainnetApiLimiter = new PQueue({ - interval: 60000, - intervalCap: 100, - timeout: 60000, -}); - -const hiroStacksTestnetApiLimiter = new PQueue({ - concurrency: 20, - interval: 60000, - intervalCap: 20, - timeout: 60000, -}); - -export function useHiroApiRateLimiter() { - const currentNetwork = useCurrentNetworkState(); - - return whenStacksChainId(currentNetwork.chain.stacks.chainId)({ - [ChainID.Mainnet]: hiroStacksMainnetApiLimiter, - [ChainID.Testnet]: hiroStacksTestnetApiLimiter, - }); -} diff --git a/src/app/query/stacks/info/block-time.query.ts b/src/app/query/stacks/info/block-time.query.ts deleted file mode 100644 index eb8064cbd3d..00000000000 --- a/src/app/query/stacks/info/block-time.query.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { useStacksClient } from '@app/store/common/api-clients.hooks'; - -export function useGetStackNetworkBlockTimeQuery() { - const client = useStacksClient(); - - return useQuery({ - queryKey: ['stacks-block-time'], - queryFn: () => client.infoApi.getNetworkBlockTimes(), - refetchOnWindowFocus: false, - refetchOnMount: false, - }); -} diff --git a/src/app/query/stacks/info/info.hooks.ts b/src/app/query/stacks/info/info.hooks.ts deleted file mode 100644 index addd188edbf..00000000000 --- a/src/app/query/stacks/info/info.hooks.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { useGetStackNetworkBlockTimeQuery } from './block-time.query'; - -export function useStacksBlockTime() { - return useGetStackNetworkBlockTimeQuery(); -} diff --git a/src/app/query/stacks/mempool/mempool.hooks.ts b/src/app/query/stacks/mempool/mempool.hooks.ts deleted file mode 100644 index d8a2e41955a..00000000000 --- a/src/app/query/stacks/mempool/mempool.hooks.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { useMemo } from 'react'; - -import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; - -import { increaseValueByOneMicroStx, microStxToStx } from '@leather-wallet/utils'; - -import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; -import { useTransactionsById } from '@app/query/stacks/transactions/transactions-by-id.query'; -import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; - -import { useStacksConfirmedTransactions } from '../transactions/transactions-with-transfers.hooks'; -import { useAccountMempoolQuery } from './mempool.query'; -import { calculatePendingTxsMoneyBalance } from './mempool.utils'; - -const droppedCache = new Map(); - -function useAccountMempoolTransactions(address: string) { - const analytics = useAnalytics(); - const query = useAccountMempoolQuery(address); - const accountMempoolTxs = query.data; - const mempoolTxs = (accountMempoolTxs ? accountMempoolTxs.results : []) as MempoolTransaction[]; - const results = mempoolTxs.filter( - tx => tx.tx_status === 'pending' && !droppedCache.has(tx.tx_id) - ); - const txs = useTransactionsById(results.map(tx => tx.tx_id)); - return useMemo(() => { - return { - query, - transactions: txs - .map(tx => tx.data) - .filter(tx => { - if (typeof tx === 'undefined') return false; - if (droppedCache.has(tx.tx_id)) return false; - if (tx.tx_status !== 'pending') { - // Stale txs persist in the mempool endpoint so we - // need to cache dropped txids to prevent unneeded fetches - void analytics.track('transaction_added_to_dropped_cache'); - droppedCache.set(tx.tx_id, true); - return false; - } - return true; - }), - }; - }, [txs, query, analytics]); -} - -export function useStacksPendingTransactions() { - const address = useCurrentStacksAccountAddress(); - const { query, transactions } = useAccountMempoolTransactions(address ?? ''); - return useMemo(() => { - const nonEmptyTransactions = transactions.filter(tx => !!tx) as MempoolTransaction[]; - return { query, transactions: nonEmptyTransactions }; - }, [query, transactions]); -} - -export function useCurrentAccountMempool() { - const address = useCurrentStacksAccountAddress(); - return useAccountMempoolQuery(address ?? ''); -} - -export function useMempoolTxsInboundBalance(address: string) { - const { transactions: pendingTransactions } = useStacksPendingTransactions(); - const confirmedTxs = useStacksConfirmedTransactions(); - - return calculatePendingTxsMoneyBalance({ - address, - confirmedTxs, - pendingTxs: pendingTransactions, - type: 'inbound', - }); -} - -export function useMempoolTxsOutboundBalance(address: string) { - const { transactions: pendingTransactions } = useStacksPendingTransactions(); - const confirmedTxs = useStacksConfirmedTransactions(); - - return calculatePendingTxsMoneyBalance({ - address, - confirmedTxs, - pendingTxs: pendingTransactions, - type: 'outbound', - }); -} - -export function useStacksValidateFeeByNonce() { - const { transactions } = useStacksPendingTransactions(); - - function changeFeeByNonce({ nonce, fee }: { nonce: number; fee: number }) { - return transactions.reduce((updatedFee, tx) => { - if (Number(tx.nonce) === nonce && microStxToStx(tx.fee_rate).toNumber() >= fee) { - return increaseValueByOneMicroStx(microStxToStx(tx.fee_rate)); - } - return updatedFee; - }, fee); - } - return { changeFeeByNonce }; -} diff --git a/src/app/query/stacks/mempool/mempool.query.ts b/src/app/query/stacks/mempool/mempool.query.ts deleted file mode 100644 index 64f782d1b70..00000000000 --- a/src/app/query/stacks/mempool/mempool.query.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; -import { useQuery } from '@tanstack/react-query'; - -import { safelyFormatHexTxid } from '@app/common/utils/safe-handle-txid'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { useSubmittedTransactionsActions } from '@app/store/submitted-transactions/submitted-transactions.hooks'; -import { useSubmittedTransactions } from '@app/store/submitted-transactions/submitted-transactions.selectors'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; - -export function useAccountMempoolQuery(address: string) { - const client = useStacksClient(); - const submittedTransactions = useSubmittedTransactions(); - const submittedTransactionsActions = useSubmittedTransactionsActions(); - const limiter = useHiroApiRateLimiter(); - - async function accountMempoolFetcher() { - return limiter.add( - () => client.transactionsApi.getAddressMempoolTransactions({ address, limit: 50 }), - { - throwOnTimeout: true, - } - ); - } - - return useQuery({ - enabled: !!address, - queryKey: ['account-mempool', address], - queryFn: accountMempoolFetcher, - onSuccess: data => { - const pendingTxids = (data.results as MempoolTransaction[]).map(tx => tx.tx_id); - submittedTransactions.map(tx => { - if (pendingTxids.includes(safelyFormatHexTxid(tx.txId))) - return submittedTransactionsActions.transactionEnteredMempool(tx.txId); - return; - }); - }, - refetchOnWindowFocus: false, - }); -} diff --git a/src/app/query/stacks/mempool/mempool.utils.ts b/src/app/query/stacks/mempool/mempool.utils.ts deleted file mode 100644 index f2ec640f6f1..00000000000 --- a/src/app/query/stacks/mempool/mempool.utils.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { - MempoolTokenTransferTransaction, - MempoolTransaction, - Transaction, -} from '@stacks/stacks-blockchain-api-types'; - -import { createMoney, sumNumbers } from '@leather-wallet/utils'; - -type PendingTransactionType = 'inbound' | 'outbound'; - -function getInboundPendingTxs( - address: string, - confirmedTxs: Transaction[], - pendingTxs: MempoolTransaction[] -) { - return pendingTxs.filter(tx => { - if (confirmedTxs.some(confirmedTx => confirmedTx.nonce === tx.nonce)) { - return false; - } - return tx.tx_type === 'token_transfer' && tx.token_transfer.recipient_address === address; - }) as unknown as MempoolTokenTransferTransaction[]; -} - -function getOutboundPendingTxs( - address: string, - confirmedTxs: Transaction[], - pendingTxs: MempoolTransaction[] -) { - return pendingTxs.filter(tx => { - if (confirmedTxs.some(confirmedTx => confirmedTx.nonce === tx.nonce)) { - return false; - } - return tx.tx_type === 'token_transfer' && tx.sender_address === address; - }) as unknown as MempoolTokenTransferTransaction[]; -} - -interface CalculatePendingTxsMoneyBalanceArgs { - address: string; - confirmedTxs: Transaction[]; - pendingTxs: MempoolTransaction[]; - type: PendingTransactionType; -} -export function calculatePendingTxsMoneyBalance({ - address, - confirmedTxs, - pendingTxs, - type, -}: CalculatePendingTxsMoneyBalanceArgs) { - const filteredPendingTxs = - type === 'inbound' - ? getInboundPendingTxs(address, confirmedTxs, pendingTxs) - : getOutboundPendingTxs(address, confirmedTxs, pendingTxs); - - const tokenTransferTxsBalance = sumNumbers( - filteredPendingTxs.map(tx => Number(tx.token_transfer.amount)) - ); - const pendingTxsFeesBalance = sumNumbers(filteredPendingTxs.map(tx => Number(tx.fee_rate))); - - return createMoney(tokenTransferTxsBalance.plus(pendingTxsFeesBalance), 'STX'); -} diff --git a/src/app/query/stacks/network/network.hooks.ts b/src/app/query/stacks/network/network.hooks.ts deleted file mode 100644 index 1ff3468458f..00000000000 --- a/src/app/query/stacks/network/network.hooks.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { useGetNetworkStatus } from './network.query'; - -export function useNetworkStatus(url: string) { - const result = useGetNetworkStatus(url); - return result.isSuccess; -} diff --git a/src/app/query/stacks/network/network.query.ts b/src/app/query/stacks/network/network.query.ts deleted file mode 100644 index 0deaaa73b47..00000000000 --- a/src/app/query/stacks/network/network.query.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; -import PQueue from 'p-queue'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; - -const staleTime = 15 * 60 * 1000; // 15 min - -const networkStatusQueryOptions = { - keepPreviousData: true, - cacheTime: staleTime, - refetchOnMount: false, - refetchInterval: false, - refetchOnReconnect: false, -} as const; - -async function getNetworkStatusFetcher(url: string, limiter: PQueue) { - const resp = await limiter.add(() => axios.get(url, { timeout: 30000 }), { - throwOnTimeout: true, - priority: 1, - }); - - return resp.data; -} - -export function useGetNetworkStatus(url: string) { - const limiter = useHiroApiRateLimiter(); - - return useQuery({ - queryKey: ['network-status', url], - queryFn: () => getNetworkStatusFetcher(url, limiter), - ...networkStatusQueryOptions, - }); -} diff --git a/src/app/query/stacks/nonce/account-nonces.hooks.ts b/src/app/query/stacks/nonce/account-nonces.hooks.ts deleted file mode 100644 index 87ee2705a64..00000000000 --- a/src/app/query/stacks/nonce/account-nonces.hooks.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useStacksPendingTransactions } from '@app/query/stacks/mempool/mempool.hooks'; -import { useGetAccountNoncesQuery } from '@app/query/stacks/nonce/account-nonces.query'; -import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; - -import { useStacksConfirmedTransactions } from '../transactions/transactions-with-transfers.hooks'; -import { parseAccountNoncesResponse } from './account-nonces.utils'; - -export function useNextNonce() { - const currentAccount = useCurrentStacksAccount(); - const confirmedTransactions = useStacksConfirmedTransactions(); - const { transactions: pendingTransactions } = useStacksPendingTransactions(); - - return useGetAccountNoncesQuery({ - select: resp => - parseAccountNoncesResponse({ - addressNonces: resp, - confirmedTransactions, - pendingTransactions, - senderAddress: currentAccount?.address ?? '', - }), - }); -} diff --git a/src/app/query/stacks/nonce/account-nonces.query.ts b/src/app/query/stacks/nonce/account-nonces.query.ts deleted file mode 100644 index a26bb36490b..00000000000 --- a/src/app/query/stacks/nonce/account-nonces.query.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import PQueue from 'p-queue'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; -import { StacksClient } from '../stacks-client'; - -const accountNoncesQueryOptions = { - refetchOnMount: 'always', - refetchOnReconnect: 'always', - refetchOnWindowFocus: 'always', -} as const; - -function fetchAccountNonces(client: StacksClient, limiter: PQueue) { - return async (principal: string) => { - if (!principal) return; - - return limiter.add( - () => - client.accountsApi.getAccountNonces({ - principal, - }), - { - throwOnTimeout: true, - } - ); - }; -} - -type FetchAccountNoncesResp = Awaited>>; - -export function useGetAccountNoncesQuery( - options?: AppUseQueryConfig -) { - const principal = useCurrentStacksAccountAddress(); - const network = useCurrentNetworkState(); - const client = useStacksClient(); - const limiter = useHiroApiRateLimiter(); - - return useQuery({ - enabled: !!principal, - queryKey: ['account-nonces', principal, network], - queryFn: () => fetchAccountNonces(client, limiter)(principal), - ...accountNoncesQueryOptions, - ...options, - }); -} diff --git a/src/app/query/stacks/nonce/account-nonces.utils.ts b/src/app/query/stacks/nonce/account-nonces.utils.ts deleted file mode 100644 index bd98a53fe74..00000000000 --- a/src/app/query/stacks/nonce/account-nonces.utils.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { AddressNonces } from '@stacks/blockchain-api-client/lib/generated'; -import { MempoolTransaction, Transaction } from '@stacks/stacks-blockchain-api-types'; - -enum NonceTypes { - apiSuggestedNonce = 'api-suggested-nonce', - clientFallbackNonce = 'client-fallback-nonce', - undefinedNonce = 'undefined-nonce', -} - -interface NextNonce { - nonce?: number; - nonceType: NonceTypes; -} - -function confirmedTxsNoncesIncludesPossibleClientFallbackNonce( - clientFallbackNonce: number, - confirmedTransactions: Transaction[] -) { - return confirmedTransactions.find(tx => tx.nonce === clientFallbackNonce); -} - -function apiSuggestsImpossibleZeroNonceWithConfirmedTxs( - confirmedTxsNonces: number[], - possibleNextNonce: number -) { - return possibleNextNonce === 0 && confirmedTxsNonces.length > 0; -} - -function compareApiMissingNoncesWithPendingTxsNonces( - confirmedTransactions: Transaction[], - detectedMissingNonces: number[], - lastConfirmedTxNonceIncremented: number, - lastPendingTxNonceIncremented: number, - pendingTxsNonces: number[], - possibleNextNonce: number, - pendingTxsNoncesIncludesApiPossibleNextNonce: boolean -) { - const remainingMissingNonces = detectedMissingNonces.filter( - nonce => !pendingTxsNonces.includes(nonce) - ); - if (remainingMissingNonces.length > 0) - return { nonce: Math.min(...remainingMissingNonces), nonceType: NonceTypes.apiSuggestedNonce }; - if (pendingTxsNoncesIncludesApiPossibleNextNonce) { - if ( - // Check if suggesting the lastPendingTxNonceIncremented is actually already confirmed - confirmedTxsNoncesIncludesPossibleClientFallbackNonce( - lastPendingTxNonceIncremented, - confirmedTransactions - ) - ) - return { nonce: lastConfirmedTxNonceIncremented, nonceType: NonceTypes.clientFallbackNonce }; - return { - nonce: lastPendingTxNonceIncremented, - nonceType: NonceTypes.clientFallbackNonce, - }; - } - return { nonce: possibleNextNonce, nonceType: NonceTypes.apiSuggestedNonce }; -} - -function findAnyMissingPendingTxsNonces(pendingNonces: number[]) { - const maxNonce = Math.max(...pendingNonces); - const minNonce = Math.min(...pendingNonces); - const missingNonces = []; - - for (let i = minNonce; i <= maxNonce; i++) { - if (!pendingNonces.includes(i)) missingNonces.push(i); - } - return missingNonces; -} - -interface ParseAccountNoncesResponseArgs { - addressNonces?: AddressNonces; - confirmedTransactions: Transaction[]; - pendingTransactions: MempoolTransaction[]; - senderAddress: string; -} -export function parseAccountNoncesResponse({ - addressNonces, - confirmedTransactions, - pendingTransactions, - senderAddress, -}: ParseAccountNoncesResponseArgs): NextNonce { - if (!addressNonces) return { nonce: undefined, nonceType: NonceTypes.undefinedNonce }; - - const detectedMissingNonces = addressNonces.detected_missing_nonces; - const lastExecutedNonce = addressNonces.last_executed_tx_nonce; - const possibleNextNonce = addressNonces.possible_next_nonce; - - const firstMissingNonce = detectedMissingNonces?.sort()[0]; - const pendingTxsNonces = pendingTransactions - .filter(tx => tx.sender_address === senderAddress) - ?.map(tx => tx.nonce); - const lastPendingTxNonce = pendingTxsNonces[0]; - const confirmedTxsNonces = confirmedTransactions - .filter(tx => tx.sender_address === senderAddress) - ?.map(tx => tx.nonce); - const lastConfirmedTxNonceIncremented = confirmedTxsNonces.length && confirmedTxsNonces[0] + 1; - const lastPendingTxNonceIncremented = lastPendingTxNonce + 1; - const pendingTxsNoncesIncludesApiPossibleNextNonce = pendingTxsNonces.includes(possibleNextNonce); - // Make sure any pending tx nonces are not already confirmed - const pendingTxsMissingNonces = findAnyMissingPendingTxsNonces(pendingTxsNonces).filter( - nonce => !confirmedTxsNonces.includes(nonce) - ); - const firstPendingMissingNonce = pendingTxsMissingNonces.sort()[0]; - - const hasApiMissingNonces = detectedMissingNonces?.length > 0; - const hasPendingTxsNonces = pendingTxsNonces.length > 0; - const pendingTxsHasMissingNonces = pendingTxsMissingNonces.length > 0; - const apiReturnsMissingNoncesAndPendingTransactions = hasApiMissingNonces && hasPendingTxsNonces; - const apiReturnsMissingNoncesButNoPendingTransactions = - hasApiMissingNonces && !hasPendingTxsNonces; - const apiReturnsPendingTransactionsWithPossibleNextNonce = - hasPendingTxsNonces && pendingTxsNoncesIncludesApiPossibleNextNonce; - const lastExecutedNonceIsNotTheFirstMissingNonce = lastExecutedNonce !== firstMissingNonce; - - if (apiSuggestsImpossibleZeroNonceWithConfirmedTxs(confirmedTxsNonces, possibleNextNonce)) - return { - nonce: lastConfirmedTxNonceIncremented, - nonceType: NonceTypes.clientFallbackNonce, - }; - - if (apiReturnsMissingNoncesAndPendingTransactions) { - return compareApiMissingNoncesWithPendingTxsNonces( - confirmedTransactions, - detectedMissingNonces, - lastConfirmedTxNonceIncremented, - lastPendingTxNonceIncremented, - pendingTxsNonces, - possibleNextNonce, - pendingTxsNoncesIncludesApiPossibleNextNonce - ); - } - - if (apiReturnsMissingNoncesButNoPendingTransactions && lastExecutedNonceIsNotTheFirstMissingNonce) - return { nonce: firstMissingNonce, nonceType: NonceTypes.apiSuggestedNonce }; - - if (apiReturnsPendingTransactionsWithPossibleNextNonce) { - if (pendingTxsHasMissingNonces) - return { nonce: firstPendingMissingNonce, nonceType: NonceTypes.clientFallbackNonce }; - if ( - // Check if suggesting the lastPendingTxNonceIncremented is actually already confirmed - confirmedTxsNoncesIncludesPossibleClientFallbackNonce( - lastPendingTxNonceIncremented, - confirmedTransactions - ) - ) - return { nonce: lastConfirmedTxNonceIncremented, nonceType: NonceTypes.clientFallbackNonce }; - return { - nonce: lastPendingTxNonceIncremented, - nonceType: NonceTypes.clientFallbackNonce, - }; - } - - return { nonce: possibleNextNonce, nonceType: NonceTypes.apiSuggestedNonce }; -} diff --git a/src/app/query/stacks/sip10/sip10-tokens.hooks.ts b/src/app/query/stacks/sip10/sip10-tokens.hooks.ts deleted file mode 100644 index 24f21632c30..00000000000 --- a/src/app/query/stacks/sip10/sip10-tokens.hooks.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { useMemo } from 'react'; - -import type { BaseCryptoAssetBalance, Sip10CryptoAssetInfo } from '@leather-wallet/models'; -import { isDefined, isUndefined } from '@leather-wallet/utils'; - -import { useAlexSwappableAssets } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; - -import { useStacksAccountBalanceFungibleTokens } from '../balance/account-balance.hooks'; -import { - useGetFungibleTokensBalanceMetadataQuery, - useGetFungibleTokensMetadataQuery, -} from '../token-metadata/fungible-tokens/fungible-token-metadata.query'; -import { type Sip10CryptoAssetFilter, filterSip10Tokens } from './sip10-tokens.utils'; - -function useSip10TokensCryptoAssetBalance(address: string) { - const { data: tokens = {} } = useStacksAccountBalanceFungibleTokens(address); - return useGetFungibleTokensBalanceMetadataQuery(tokens); -} - -function useSip10TokensCryptoAssetInfo(address: string) { - const { data: tokens = {} } = useStacksAccountBalanceFungibleTokens(address); - return useGetFungibleTokensMetadataQuery(Object.keys(tokens)); -} - -export interface Sip10TokenAssetDetails { - balance: BaseCryptoAssetBalance; - info: Sip10CryptoAssetInfo; -} - -function useSip10Tokens(address: string): { - isInitialLoading: boolean; - tokens: Sip10TokenAssetDetails[]; -} { - const balancesResults = useSip10TokensCryptoAssetBalance(address); - const infoResults = useSip10TokensCryptoAssetInfo(address); - - return useMemo(() => { - // We can potentially use the 'combine' option in react-query v5 to replace this? - // https://tanstack.com/query/latest/docs/framework/react/reference/useQueries#combine - const isInitialLoading = - balancesResults.some(query => query.isInitialLoading) || - infoResults.some(query => query.isInitialLoading); - const tokenBalances = balancesResults - .map(query => query.data) - .filter(isDefined) - .filter(token => token.balance.availableBalance.amount.isGreaterThan(0)); - const tokenInfo = infoResults.map(query => query.data).filter(isDefined); - const tokens = tokenInfo.map(info => { - const tokenBalance = tokenBalances.find( - balance => balance.contractId === info.contractId - )?.balance; - if (isUndefined(tokenBalance)) return; - return { - balance: tokenBalance, - info, - }; - }); - - return { - isInitialLoading, - tokens: tokens.filter(isDefined), - }; - }, [balancesResults, infoResults]); -} - -export function useSip10Token(contractId: string) { - const address = useCurrentStacksAccountAddress(); - const { tokens = [] } = useSip10Tokens(address); - return useMemo( - () => tokens.find(token => token.info.contractId === contractId), - [contractId, tokens] - ); -} - -interface UseSip10TokensArgs { - address: string; - filter?: Sip10CryptoAssetFilter; -} -export function useFilteredSip10Tokens({ address, filter = 'all' }: UseSip10TokensArgs) { - const { isInitialLoading, tokens = [] } = useSip10Tokens(address); - const { data: swapAssets = [] } = useAlexSwappableAssets(); - const filteredTokens = useMemo( - () => filterSip10Tokens(swapAssets, tokens, filter), - [swapAssets, tokens, filter] - ); - return { isInitialLoading, tokens: filteredTokens }; -} - -export function useTransferableSip10Tokens(address: string) { - const { tokens = [] } = useSip10Tokens(address); - return useMemo(() => tokens.filter(token => token.info.canTransfer), [tokens]); -} diff --git a/src/app/query/stacks/sip10/sip10-tokens.spec.ts b/src/app/query/stacks/sip10/sip10-tokens.spec.ts deleted file mode 100644 index e62c56ecff6..00000000000 --- a/src/app/query/stacks/sip10/sip10-tokens.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import type { FtMetadataResponse } from '@hirosystems/token-metadata-api-client'; - -import { isTransferableSip10Token } from './sip10-tokens.utils'; - -describe(isTransferableSip10Token.name, () => { - test('assets with a name, symbol and decimals are allowed to be transferred', () => { - const asset: Partial = { - decimals: 9, - name: 'SteLLa the Cat', - symbol: 'CAT', - }; - expect(isTransferableSip10Token(asset)).toBeTruthy(); - }); - - test('a token with no decimals is transferable', () => { - const asset: Partial = { - decimals: 0, - name: 'SteLLa the Cat', - symbol: 'CAT', - }; - expect(isTransferableSip10Token(asset)).toBeTruthy(); - }); - - test('assets missing either name, symbol or decimals may not be transferred', () => { - const asset: Partial = { - name: 'Test token', - symbol: 'TEST', - decimals: undefined, - }; - expect(isTransferableSip10Token(asset)).toBeFalsy(); - }); -}); diff --git a/src/app/query/stacks/sip10/sip10-tokens.utils.ts b/src/app/query/stacks/sip10/sip10-tokens.utils.ts deleted file mode 100644 index 40434be7a3a..00000000000 --- a/src/app/query/stacks/sip10/sip10-tokens.utils.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { FtMetadataResponse } from '@hirosystems/token-metadata-api-client'; - -import type { CryptoAssetBalance, Sip10CryptoAssetInfo } from '@leather-wallet/models'; -import { isUndefined } from '@leather-wallet/utils'; - -import { getStacksContractIdStringParts } from '@app/common/stacks-utils'; -import { getPrincipalFromContractId, getTicker } from '@app/common/utils'; -import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks'; - -export function isTransferableSip10Token(token: Partial) { - return !isUndefined(token.decimals) && !isUndefined(token.name) && !isUndefined(token.symbol); -} - -export function createSip10CryptoAssetInfo( - key: string, - ftAsset: FtMetadataResponse -): Sip10CryptoAssetInfo { - const { contractAssetName } = getStacksContractIdStringParts(key); - const name = ftAsset.name || contractAssetName; - - return { - canTransfer: isTransferableSip10Token(ftAsset), - contractId: key, - decimals: ftAsset.decimals ?? 0, - hasMemo: isTransferableSip10Token(ftAsset), - imageCanonicalUri: ftAsset.image_canonical_uri ?? '', - name, - symbol: ftAsset.symbol || getTicker(name), - }; -} - -export type Sip10CryptoAssetFilter = 'all' | 'supported' | 'unsupported'; - -export function filterSip10Tokens( - swapAssets: SwapAsset[], - tokens: { - balance: CryptoAssetBalance; - info: Sip10CryptoAssetInfo; - }[], - filter: Sip10CryptoAssetFilter -) { - return tokens.filter(token => { - const principal = getPrincipalFromContractId(token.info.contractId); - if (filter === 'supported') { - return swapAssets.some(swapAsset => swapAsset.principal === principal); - } - if (filter === 'unsupported') { - return !swapAssets.some(swapAsset => swapAsset.principal === principal); - } - return true; - }); -} diff --git a/src/app/query/stacks/stacks-client.ts b/src/app/query/stacks/stacks-client.ts deleted file mode 100644 index 802a6d2ce3f..00000000000 --- a/src/app/query/stacks/stacks-client.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { TokensApi } from '@hirosystems/token-metadata-api-client'; -import { - AccountsApi, - BlocksApi, - Configuration, - type ConfigurationParameters, - FaucetsApi, - FeesApi, - FungibleTokensApi, - InfoApi, - NamesApi, - NonFungibleTokensApi, - RosettaApi, - SearchApi, - SmartContractsApi, - TransactionsApi, -} from '@stacks/blockchain-api-client'; -import axios from 'axios'; - -import { STX20_API_BASE_URL_MAINNET } from '@shared/constants'; - -export interface Stx20Balance { - ticker: string; - balance: string; - updateDate: string; -} - -interface Stx20BalanceResponse { - address: string; - balances: Stx20Balance[]; -} - -class Stx20Api { - url = STX20_API_BASE_URL_MAINNET; - - async getStx20Balances(address: string) { - const resp = await axios.get(`${this.url}/balance/${address}`); - return resp.data.balances; - } -} - -export class StacksClient { - configuration: Configuration; - smartContractsApi: SmartContractsApi; - accountsApi: AccountsApi; - infoApi: InfoApi; - transactionsApi: TransactionsApi; - blocksApi: BlocksApi; - faucetsApi: FaucetsApi; - namesApi: NamesApi; - feesApi: FeesApi; - searchApi: SearchApi; - rosettaApi: RosettaApi; - fungibleTokensApi: FungibleTokensApi; - nonFungibleTokensApi: NonFungibleTokensApi; - tokensApi: TokensApi; - stx20Api: Stx20Api; - - constructor(config: ConfigurationParameters) { - this.configuration = new Configuration(config); - this.smartContractsApi = new SmartContractsApi(this.configuration); - this.accountsApi = new AccountsApi(this.configuration); - this.infoApi = new InfoApi(this.configuration); - this.transactionsApi = new TransactionsApi(this.configuration); - this.blocksApi = new BlocksApi(this.configuration); - this.faucetsApi = new FaucetsApi(this.configuration); - this.namesApi = new NamesApi(this.configuration); - this.feesApi = new FeesApi(this.configuration); - this.searchApi = new SearchApi(this.configuration); - this.rosettaApi = new RosettaApi(this.configuration); - this.fungibleTokensApi = new FungibleTokensApi(this.configuration); - this.nonFungibleTokensApi = new NonFungibleTokensApi(this.configuration); - this.tokensApi = new TokensApi({ basePath: config.basePath }); - this.stx20Api = new Stx20Api(); - } -} diff --git a/src/app/query/stacks/stx20/stx20-tokens.hooks.ts b/src/app/query/stacks/stx20/stx20-tokens.hooks.ts deleted file mode 100644 index 3728b65e0a8..00000000000 --- a/src/app/query/stacks/stx20/stx20-tokens.hooks.ts +++ /dev/null @@ -1,28 +0,0 @@ -import BigNumber from 'bignumber.js'; - -import type { Stx20CryptoAssetInfo } from '@leather-wallet/models'; -import { createMoney } from '@leather-wallet/utils'; - -import { createCryptoAssetBalance } from '@app/query/common/models'; - -import type { Stx20Balance } from '../stacks-client'; -import { useStx20BalancesQuery } from './stx20-tokens.query'; - -function createStx20CryptoAssetInfo(stx20Balance: Stx20Balance): Stx20CryptoAssetInfo { - return { - name: 'stx-20', - symbol: stx20Balance.ticker, - }; -} - -export function useStx20Tokens(address: string) { - return useStx20BalancesQuery(address, { - select: resp => - resp.map(stx20Balance => ({ - balance: createCryptoAssetBalance( - createMoney(new BigNumber(stx20Balance.balance), stx20Balance.ticker, 0) - ), - info: createStx20CryptoAssetInfo(stx20Balance), - })), - }); -} diff --git a/src/app/query/stacks/stx20/stx20-tokens.query.ts b/src/app/query/stacks/stx20/stx20-tokens.query.ts deleted file mode 100644 index 529922547fb..00000000000 --- a/src/app/query/stacks/stx20/stx20-tokens.query.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ChainID } from '@stacks/transactions'; -import { useQuery } from '@tanstack/react-query'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -import type { Stx20Balance } from '../stacks-client'; - -export function useStx20BalancesQuery( - address: string, - options?: AppUseQueryConfig -) { - const client = useStacksClient(); - const network = useCurrentNetwork(); - - return useQuery({ - enabled: network.chain.stacks.chainId === ChainID.Mainnet, - queryKey: ['stx20-balances', address], - queryFn: () => client.stx20Api.getStx20Balances(address), - ...options, - }); -} diff --git a/src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query.ts b/src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query.ts deleted file mode 100644 index 49d6438fa45..00000000000 --- a/src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { useQueries, useQuery } from '@tanstack/react-query'; -import BigNumber from 'bignumber.js'; -import PQueue from 'p-queue'; - -import { createMoney } from '@leather-wallet/utils'; - -import type { AddressBalanceResponse } from '@shared/models/account.model'; - -import { getStacksContractIdStringParts } from '@app/common/stacks-utils'; -import { getPrincipalFromContractId, getTicker } from '@app/common/utils'; -import { createCryptoAssetBalance } from '@app/query/common/models'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -import { useHiroApiRateLimiter } from '../../hiro-rate-limiter'; -import { createSip10CryptoAssetInfo } from '../../sip10/sip10-tokens.utils'; -import type { StacksClient } from '../../stacks-client'; -import { FtAssetResponse, isFtAsset } from '../token-metadata.utils'; - -const staleTime = 12 * 60 * 60 * 1000; - -const queryOptions = { - keepPreviousData: true, - cacheTime: staleTime, - staleTime: staleTime, - refetchOnMount: false, - refetchInterval: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, - retry: 0, -} as const; - -function fetchFungibleTokenMetadata(client: StacksClient, limiter: PQueue) { - return (principal: string) => async () => { - return limiter.add(() => client.tokensApi.getFtMetadata(principal), { - throwOnTimeout: true, - }) as unknown as FtAssetResponse; - }; -} - -export function useGetFungibleTokenMetadataQuery(principal: string) { - const client = useStacksClient(); - const network = useCurrentNetworkState(); - const limiter = useHiroApiRateLimiter(); - - return useQuery({ - queryKey: ['get-ft-metadata', principal, network.chain.stacks.url], - queryFn: fetchFungibleTokenMetadata(client, limiter)(principal), - ...queryOptions, - }); -} - -export function useGetFungibleTokensBalanceMetadataQuery( - ftBalances: AddressBalanceResponse['fungible_tokens'] -) { - const client = useStacksClient(); - const network = useCurrentNetworkState(); - const limiter = useHiroApiRateLimiter(); - - return useQueries({ - queries: Object.entries(ftBalances).map(([key, value]) => { - const principal = getPrincipalFromContractId(key); - return { - enabled: !!principal, - queryKey: ['get-ft-metadata', principal, network.chain.stacks.url], - queryFn: fetchFungibleTokenMetadata(client, limiter)(principal), - select: (resp: FtAssetResponse) => { - if (!(resp && isFtAsset(resp))) return; - const { contractAssetName } = getStacksContractIdStringParts(key); - const name = resp.name || contractAssetName; - const symbol = resp.symbol || getTicker(name); - return { - contractId: key, - balance: createCryptoAssetBalance( - createMoney(new BigNumber(value.balance), symbol, resp.decimals ?? 0) - ), - }; - }, - ...queryOptions, - }; - }), - }); -} - -export function useGetFungibleTokensMetadataQuery(keys: string[]) { - const client = useStacksClient(); - const network = useCurrentNetworkState(); - const limiter = useHiroApiRateLimiter(); - - return useQueries({ - queries: keys.map(key => { - const principal = getPrincipalFromContractId(key); - return { - enabled: !!principal, - queryKey: ['get-ft-metadata', principal, network.chain.stacks.url], - queryFn: fetchFungibleTokenMetadata(client, limiter)(principal), - select: (resp: FtAssetResponse) => { - if (!(resp && isFtAsset(resp))) return; - return createSip10CryptoAssetInfo(key, resp); - }, - ...queryOptions, - }; - }), - }); -} diff --git a/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-holdings.query.ts b/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-holdings.query.ts deleted file mode 100644 index edb127a54bf..00000000000 --- a/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-holdings.query.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import PQueue from 'p-queue'; - -import { logger } from '@shared/logger'; -import { Paginated } from '@shared/models/api-types'; - -import { QueryPrefixes } from '@app/query/query-prefixes'; -import { StacksClient } from '@app/query/stacks/stacks-client'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -import { useHiroApiRateLimiter } from '../../hiro-rate-limiter'; - -const staleTime = 15 * 60 * 1000; // 15 min - -interface NonFungibleTokenHoldingListResult { - asset_identifier: string; - value: { - hex: string; - repr: string; - }; - block_height: number; - tx_id: string; -} - -const queryOptions = { cacheTime: staleTime, staleTime, refetchhOnFocus: false } as const; - -type FetchNonFungibleTokenHoldingsResp = Paginated; - -function fetchNonFungibleTokenHoldings(client: StacksClient, limiter: PQueue) { - return async (address: string) => { - if (!address) return; - return limiter.add( - () => - client.nonFungibleTokensApi.getNftHoldings({ - principal: address, - limit: 50, - }) as unknown as Promise, - { - throwOnTimeout: true, - } - ); - }; -} - -function makeNonFungibleTokenHoldingsQuery( - address: string, - network: string, - client: StacksClient, - limiter: PQueue -) { - if (address === '') logger.warn('No address passed to ' + QueryPrefixes.GetNftHoldings); - return { - enabled: !!address, - queryKey: [QueryPrefixes.GetNftHoldings, address, network], - queryFn: () => fetchNonFungibleTokenHoldings(client, limiter)(address), - ...queryOptions, - }; -} - -export function useGetNonFungibleTokenHoldingsQuery(address: string) { - const client = useStacksClient(); - const network = useCurrentNetworkState(); - const limiter = useHiroApiRateLimiter(); - - return useQuery( - makeNonFungibleTokenHoldingsQuery(address, network.chain.stacks.url, client, limiter) - ); -} diff --git a/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.hooks.ts b/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.hooks.ts deleted file mode 100644 index 694755de2bf..00000000000 --- a/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.hooks.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useMemo } from 'react'; - -import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; - -import { isNftAsset } from '../token-metadata.utils'; -import { useGetNonFungibleTokenMetadataListQuery } from './non-fungible-token-metadata.query'; - -export function useStacksNonFungibleTokensMetadata(account: StacksAccount) { - const respList = useGetNonFungibleTokenMetadataListQuery(account); - - return useMemo( - () => - respList - .filter(resp => resp.status === 'success') - .map(resp => { - if (resp.data && isNftAsset(resp.data)) return resp.data; - return; - }), - [respList] - ); -} diff --git a/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.query.ts b/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.query.ts deleted file mode 100644 index 6709965471e..00000000000 --- a/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.query.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { hexToCV } from '@stacks/transactions'; -import { UseQueryResult, useQueries } from '@tanstack/react-query'; - -import { getPrincipalFromContractId } from '@app/common/utils'; -import { QueryPrefixes } from '@app/query/query-prefixes'; -import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; - -import { useHiroApiRateLimiter } from '../../hiro-rate-limiter'; -import { NftAssetResponse } from '../token-metadata.utils'; -import { useGetNonFungibleTokenHoldingsQuery } from './non-fungible-token-holdings.query'; - -const queryOptions = { - refetchOnWindowFocus: false, - refetchOnMount: false, - staleTime: 10 * 1000, -}; - -function getTokenId(hex: string) { - const clarityValue = hexToCV(hex); - return clarityValue.type === 1 ? Number(clarityValue.value) : 0; -} - -function statusCodeNotFoundOrNotProcessable(status: number) { - return status === 404 || status === 422; -} - -export function useGetNonFungibleTokenMetadataListQuery( - account: StacksAccount -): UseQueryResult[] { - const client = useStacksClient(); - const limiter = useHiroApiRateLimiter(); - const nftHoldings = useGetNonFungibleTokenHoldingsQuery(account.address); - - return useQueries({ - queries: (nftHoldings.data?.results ?? []).map(nft => { - const principal = getPrincipalFromContractId(nft.asset_identifier); - const tokenId = getTokenId(nft.value.hex); - - return { - enabled: !!tokenId, - queryKey: [QueryPrefixes.GetNftMetadata, principal, tokenId], - queryFn: async () => { - return limiter.add(() => client.tokensApi.getNftMetadata(principal, tokenId), { - throwOnTimeout: true, - }); - }, - retry(_count: number, error: Response) { - if (statusCodeNotFoundOrNotProcessable(error.status)) return false; - return true; - }, - ...queryOptions, - }; - }), - }); -} diff --git a/src/app/query/stacks/token-metadata/token-metadata.utils.ts b/src/app/query/stacks/token-metadata/token-metadata.utils.ts deleted file mode 100644 index 9d8858f8923..00000000000 --- a/src/app/query/stacks/token-metadata/token-metadata.utils.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - FtMetadataResponse, - NftMetadataResponse, - NotFoundErrorResponse, -} from '@hirosystems/token-metadata-api-client'; - -export type FtAssetResponse = FtMetadataResponse | NotFoundErrorResponse; -export type NftAssetResponse = NftMetadataResponse | NotFoundErrorResponse; - -function isAssetMetadataNotFoundResponse( - resp: FtAssetResponse | NftAssetResponse -): resp is NotFoundErrorResponse { - return 'error' in resp; -} - -export function isFtAsset(resp: FtAssetResponse): resp is FtMetadataResponse { - return !isAssetMetadataNotFoundResponse(resp); -} - -export function isNftAsset(resp: NftAssetResponse): resp is NftMetadataResponse { - return !isAssetMetadataNotFoundResponse(resp); -} diff --git a/src/app/query/stacks/transactions/transactions-by-id.query.ts b/src/app/query/stacks/transactions/transactions-by-id.query.ts deleted file mode 100644 index 705b7b60fbf..00000000000 --- a/src/app/query/stacks/transactions/transactions-by-id.query.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { MempoolTransaction, Transaction } from '@stacks/stacks-blockchain-api-types/generated'; -import { useQueries, useQuery } from '@tanstack/react-query'; - -import { useStacksClient } from '@app/store/common/api-clients.hooks'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; - -const options = { - staleTime: 30 * 1000, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: true, -} as const; - -export function useTransactionsById(txids: string[]) { - const client = useStacksClient(); - const limiter = useHiroApiRateLimiter(); - - async function transactionByIdFetcher(txId: string) { - return limiter.add( - () => client.transactionsApi.getTransactionById({ txId }) as unknown as MempoolTransaction, - { - throwOnTimeout: true, - } - ); - } - - return useQueries({ - queries: txids.map(txid => { - return { - queryKey: ['transaction-by-id', txid], - queryFn: () => transactionByIdFetcher(txid), - ...options, - }; - }), - }); -} - -export function useTransactionById(txid: string) { - const client = useStacksClient(); - const limiter = useHiroApiRateLimiter(); - async function transactionByIdFetcher(txId: string) { - return limiter.add( - () => - client.transactionsApi.getTransactionById({ txId }) as unknown as - | Transaction - | MempoolTransaction, - { - throwOnTimeout: true, - } - ); - } - - return useQuery({ - queryKey: ['transaction-by-id', txid], - queryFn: () => transactionByIdFetcher(txid), - ...options, - }); -} diff --git a/src/app/query/stacks/transactions/transactions-with-transfers.hooks.ts b/src/app/query/stacks/transactions/transactions-with-transfers.hooks.ts deleted file mode 100644 index a1987cbed62..00000000000 --- a/src/app/query/stacks/transactions/transactions-with-transfers.hooks.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { useMemo } from 'react'; - -import { useGetAccountTransactionsWithTransfersQuery } from './transactions-with-transfers.query'; - -export function useStacksConfirmedTransactions() { - const txs = useGetAccountTransactionsWithTransfersQuery().data?.results; - return useMemo(() => txs?.map(txWithTransfers => txWithTransfers.tx), [txs]) ?? []; -} diff --git a/src/app/query/stacks/transactions/transactions-with-transfers.query.ts b/src/app/query/stacks/transactions/transactions-with-transfers.query.ts deleted file mode 100644 index a999bbdd02b..00000000000 --- a/src/app/query/stacks/transactions/transactions-with-transfers.query.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AddressTransactionsWithTransfersListResponse } from '@stacks/stacks-blockchain-api-types'; -import { UseQueryOptions, UseQueryResult, useQuery } from '@tanstack/react-query'; - -import { DEFAULT_LIST_LIMIT } from '@shared/constants'; - -import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -import { useHiroApiRateLimiter } from '../hiro-rate-limiter'; - -const queryOptions: UseQueryOptions = { - staleTime: 60 * 1000, - refetchInterval: 30_000, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: true, -}; - -export function useGetAccountTransactionsWithTransfersQuery() { - const principal = useCurrentStacksAccountAddress(); - const { chain } = useCurrentNetworkState(); - const client = useStacksClient(); - const limiter = useHiroApiRateLimiter(); - - async function fetchAccountTxsWithTransfers(signal?: AbortSignal) { - if (!principal) return; - return limiter.add( - () => - client.accountsApi.getAccountTransactionsWithTransfers({ - principal, - limit: DEFAULT_LIST_LIMIT, - }), - { - signal, - throwOnTimeout: true, - } - ); - } - - return useQuery({ - queryKey: ['account-txs-with-transfers', principal, chain.stacks.url], - queryFn: ({ signal }) => fetchAccountTxsWithTransfers(signal), - enabled: !!principal && !!chain.stacks.url, - ...queryOptions, - }) as UseQueryResult; -} diff --git a/src/app/store/accounts/blockchain/stacks/stacks-account.models.ts b/src/app/store/accounts/blockchain/stacks/stacks-account.models.ts index b4961251af3..02c5ab3f58a 100644 --- a/src/app/store/accounts/blockchain/stacks/stacks-account.models.ts +++ b/src/app/store/accounts/blockchain/stacks/stacks-account.models.ts @@ -1,7 +1,5 @@ import type { Account } from '@stacks/wallet-sdk'; -import type { AccountBalanceStxKeys } from '@shared/models/account.model'; - // Extending the `Account` type from `@stacks/wallet-sdk` export type SoftwareStacksAccount = Account & { type: 'software'; @@ -20,12 +18,3 @@ export interface HardwareStacksAccount { } export type StacksAccount = SoftwareStacksAccount | HardwareStacksAccount; - -export const accountBalanceStxKeys: AccountBalanceStxKeys[] = [ - 'balance', - 'total_sent', - 'total_received', - 'total_fees_sent', - 'total_miner_rewards_received', - 'locked', -]; diff --git a/src/app/store/common/api-clients.hooks.ts b/src/app/store/common/api-clients.hooks.ts index 6c401e486d4..60ad3f53a20 100644 --- a/src/app/store/common/api-clients.hooks.ts +++ b/src/app/store/common/api-clients.hooks.ts @@ -1,9 +1,6 @@ import { useMemo } from 'react'; -import { bitcoinClient } from '@leather-wallet/query'; - -import { wrappedFetch as fetchApi } from '@app/common/api/fetch-wrapper'; -import { StacksClient } from '@app/query/stacks/stacks-client'; +import { bitcoinClient, stacksClient } from '@leather-wallet/query'; import { useCurrentNetworkState } from '../networks/networks.hooks'; @@ -16,9 +13,6 @@ export function useStacksClient() { const network = useCurrentNetworkState(); return useMemo(() => { - return new StacksClient({ - basePath: network.chain.stacks.url, - fetchApi, - }); + return stacksClient(network.chain.stacks.url); }, [network.chain.stacks.url]); } diff --git a/src/app/store/software-keys/software-key.actions.ts b/src/app/store/software-keys/software-key.actions.ts index a5701d252f5..4e5127f4aae 100644 --- a/src/app/store/software-keys/software-key.actions.ts +++ b/src/app/store/software-keys/software-key.actions.ts @@ -1,6 +1,6 @@ import { AddressVersion } from '@stacks/transactions'; -import type { BitcoinClient } from '@leather-wallet/query'; +import { type BitcoinClient, type StacksClient, fetchNamesForAddress } from '@leather-wallet/query'; import { decryptMnemonic, encryptMnemonic } from '@shared/crypto/mnemonic-encryption'; import { logger } from '@shared/logger'; @@ -10,8 +10,6 @@ import { identifyUser } from '@shared/utils/analytics'; import { recurseAccountsForActivity } from '@app/common/account-restoration/account-restore'; import { checkForLegacyGaiaConfigWithKnownGeneratedAccountIndex } from '@app/common/account-restoration/legacy-gaia-config-lookup'; import { mnemonicToRootNode } from '@app/common/keychain/keychain'; -import { fetchNamesForAddress } from '@app/query/stacks/bns/bns.utils'; -import { StacksClient } from '@app/query/stacks/stacks-client'; import { AppThunk } from '@app/store'; import { initalizeWalletSession } from '@app/store/session-restore'; @@ -45,7 +43,7 @@ function setWalletEncryptionPassword(args: { await checkForLegacyGaiaConfigWithKnownGeneratedAccountIndex(secretKey); async function doesStacksAddressHaveBalance(address: string) { - const resp = await stxClient.accountsApi.getAccountBalance({ principal: address }); + const resp = await stxClient.getAccountBalance(address); return Number(resp.stx.balance) > 0; } diff --git a/src/app/store/submitted-transactions/submitted-transactions.hooks.ts b/src/app/store/submitted-transactions/submitted-transactions.hooks.ts index cfb130f52a7..e7f08068cf6 100644 --- a/src/app/store/submitted-transactions/submitted-transactions.hooks.ts +++ b/src/app/store/submitted-transactions/submitted-transactions.hooks.ts @@ -1,25 +1,45 @@ -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; +import type { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; + +import { safelyFormatHexTxid } from '@app/common/utils/safe-handle-txid'; import { useAppDispatch } from '@app/store'; import { submittedTransactionsActions } from './submitted-transactions.actions'; +import { useSubmittedTransactions } from './submitted-transactions.selectors'; import { SubmittedTransaction } from './submitted-transactions.slice'; +export function useUpdateSubmittedTransactions() { + const submittedTransactions = useSubmittedTransactions(); + const submittedTransactionsActions = useSubmittedTransactionsActions(); + return useCallback( + (data: MempoolTransaction[]) => { + const pendingTxids = data.map(tx => tx.tx_id); + submittedTransactions.map(tx => { + if (pendingTxids.includes(safelyFormatHexTxid(tx.txid))) + return submittedTransactionsActions.transactionEnteredMempool(tx.txid); + return; + }); + }, + [submittedTransactions, submittedTransactionsActions] + ); +} + export function useSubmittedTransactionsActions() { const dispatch = useAppDispatch(); return useMemo( () => ({ - newTransactionSubmitted({ rawTx, txId }: SubmittedTransaction) { - return dispatch(submittedTransactionsActions.newTransactionSubmitted({ rawTx, txId })); + newTransactionSubmitted({ rawTx, txid }: SubmittedTransaction) { + return dispatch(submittedTransactionsActions.newTransactionSubmitted({ rawTx, txid })); }, - transactionEnteredMempool(txId: string) { - return dispatch(submittedTransactionsActions.transactionEnteredMempool(txId)); + transactionEnteredMempool(txid: string) { + return dispatch(submittedTransactionsActions.transactionEnteredMempool(txid)); }, - transactionReplacedByFee(txId: string) { - return dispatch(submittedTransactionsActions.transactionReplacedByFee(txId)); + transactionReplacedByFee(txid: string) { + return dispatch(submittedTransactionsActions.transactionReplacedByFee(txid)); }, }), [dispatch] diff --git a/src/app/store/submitted-transactions/submitted-transactions.slice.ts b/src/app/store/submitted-transactions/submitted-transactions.slice.ts index dc74d2e4c27..d783daacd09 100644 --- a/src/app/store/submitted-transactions/submitted-transactions.slice.ts +++ b/src/app/store/submitted-transactions/submitted-transactions.slice.ts @@ -2,11 +2,11 @@ import { PayloadAction, createEntityAdapter, createSlice } from '@reduxjs/toolki export interface SubmittedTransaction { rawTx: string; - txId: string; + txid: string; } export const submittedTransactionsAdapter = createEntityAdapter({ - selectId: submittedTransaction => submittedTransaction.txId, + selectId: submittedTransaction => submittedTransaction.txid, }); const initialSubmittedTransactionsState = submittedTransactionsAdapter.getInitialState(); diff --git a/src/app/store/transactions/contract-call.hooks.ts b/src/app/store/transactions/contract-call.hooks.ts index c29e3974ab3..13799b552c4 100644 --- a/src/app/store/transactions/contract-call.hooks.ts +++ b/src/app/store/transactions/contract-call.hooks.ts @@ -2,22 +2,24 @@ import { useCallback } from 'react'; import { ContractCallPayload } from '@stacks/connect'; +import { useNextNonce } from '@leather-wallet/query'; + import { StacksTransactionFormValues } from '@shared/models/form.model'; import { GenerateUnsignedTransactionOptions, generateUnsignedTransaction, } from '@app/common/transactions/stacks/generate-unsigned-txs'; -import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useCurrentStacksAccount } from '../accounts/blockchain/stacks/stacks-account.hooks'; import { useCurrentStacksNetworkState } from '../networks/networks.hooks'; export function useGenerateStacksContractCallUnsignedTx() { - const { data: nextNonce } = useNextNonce(); - const network = useCurrentStacksNetworkState(); const account = useCurrentStacksAccount(); + const { data: nextNonce } = useNextNonce(account?.address ?? ''); + const network = useCurrentStacksNetworkState(); + return useCallback( async (payload: ContractCallPayload, values: StacksTransactionFormValues) => { if (!account) return; diff --git a/src/app/store/transactions/post-conditions.hooks.ts b/src/app/store/transactions/post-conditions.hooks.ts index 9162e91c76b..a1797fcf87f 100644 --- a/src/app/store/transactions/post-conditions.hooks.ts +++ b/src/app/store/transactions/post-conditions.hooks.ts @@ -3,12 +3,12 @@ import { useMemo } from 'react'; import { ContractCallPayload, ContractDeployPayload, STXTransferPayload } from '@stacks/connect'; import { FungiblePostCondition, addressToString } from '@stacks/transactions'; +import { isFtAsset, useGetFungibleTokenMetadataQuery } from '@leather-wallet/query'; + import { getPostCondition, handlePostConditions, } from '@app/common/transactions/stacks/post-condition.utils'; -import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query'; -import { isFtAsset } from '@app/query/stacks/token-metadata/token-metadata.utils'; import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useTransactionRequestState } from './requests.hooks'; diff --git a/src/app/store/transactions/raw.hooks.ts b/src/app/store/transactions/raw.hooks.ts deleted file mode 100644 index eaa5efa80d6..00000000000 --- a/src/app/store/transactions/raw.hooks.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useMemo } from 'react'; -import { useAsync } from 'react-async-hook'; - -import { deserializeTransaction } from '@stacks/transactions'; -import { useAtom } from 'jotai'; - -import { useStacksClient } from '@app/store/common/api-clients.hooks'; -import { rawTxIdState } from '@app/store/transactions/raw'; - -export function useRawTxIdState() { - return useAtom(rawTxIdState); -} - -const rawTxCache = new Map(); - -function useRawTxState() { - const [txId] = useRawTxIdState(); - const { transactionsApi } = useStacksClient(); - - return useAsync(async () => { - if (!txId) return; - const match = rawTxCache.get(txId); - // No need to fetch again - if (match) return match; - return transactionsApi.getRawTransactionById({ txId }).then(result => { - const rawTx = result.raw_tx; - rawTxCache.set(txId, rawTx); - return rawTx; - }); - }, [transactionsApi, txId]).result; -} - -export function useRawDeserializedTxState() { - const rawTx = useRawTxState(); - return useMemo(() => { - if (!rawTx) return; - return deserializeTransaction(rawTx); - }, [rawTx]); -} diff --git a/src/app/store/transactions/raw.ts b/src/app/store/transactions/raw.ts deleted file mode 100644 index e651f9d4751..00000000000 --- a/src/app/store/transactions/raw.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { atom } from 'jotai'; - -export const rawTxIdState = atom(null); diff --git a/src/app/store/transactions/token-transfer.hooks.ts b/src/app/store/transactions/token-transfer.hooks.ts index c29e37e51f6..93ae9ebd9be 100644 --- a/src/app/store/transactions/token-transfer.hooks.ts +++ b/src/app/store/transactions/token-transfer.hooks.ts @@ -17,6 +17,7 @@ import { } from '@stacks/transactions'; import type { Sip10CryptoAssetInfo } from '@leather-wallet/models'; +import { useNextNonce } from '@leather-wallet/query'; import { stxToMicroStx } from '@leather-wallet/utils'; import { logger } from '@shared/logger'; @@ -27,16 +28,15 @@ import { GenerateUnsignedTransactionOptions, generateUnsignedTransaction, } from '@app/common/transactions/stacks/generate-unsigned-txs'; -import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useCurrentStacksNetworkState } from '@app/store/networks/networks.hooks'; import { makePostCondition } from '@app/store/transactions/transaction.hooks'; import { useCurrentStacksAccount } from '../accounts/blockchain/stacks/stacks-account.hooks'; export function useGenerateStxTokenTransferUnsignedTx() { - const { data: nextNonce } = useNextNonce(); - const network = useCurrentStacksNetworkState(); const account = useCurrentStacksAccount(); + const { data: nextNonce } = useNextNonce(account?.address ?? ''); + const network = useCurrentStacksNetworkState(); return useCallback( async (values?: StacksSendFormValues) => { @@ -68,9 +68,9 @@ export function useGenerateStxTokenTransferUnsignedTx() { export function useStxTokenTransferUnsignedTxState(values?: StacksSendFormValues) { const generateTx = useGenerateStxTokenTransferUnsignedTx(); - const { data: nextNonce } = useNextNonce(); - const network = useCurrentStacksNetworkState(); const account = useCurrentStacksAccount(); + const { data: nextNonce } = useNextNonce(account?.address ?? ''); + const network = useCurrentStacksNetworkState(); const tx = useAsync( async () => generateTx(values ?? undefined), @@ -81,8 +81,8 @@ export function useStxTokenTransferUnsignedTxState(values?: StacksSendFormValues } export function useGenerateFtTokenTransferUnsignedTx(info: Sip10CryptoAssetInfo) { - const { data: nextNonce } = useNextNonce(); const account = useCurrentStacksAccount(); + const { data: nextNonce } = useNextNonce(account?.address ?? ''); const network = useCurrentStacksNetworkState(); const { contractId } = info; const { contractAddress, contractAssetName, contractName } = diff --git a/src/app/store/transactions/transaction.hooks.ts b/src/app/store/transactions/transaction.hooks.ts index 2ba0ba2b1a9..8091aa47b1f 100644 --- a/src/app/store/transactions/transaction.hooks.ts +++ b/src/app/store/transactions/transaction.hooks.ts @@ -14,6 +14,7 @@ import { } from '@stacks/transactions'; import BN from 'bn.js'; +import { useNextNonce } from '@leather-wallet/query'; import { isUndefined, stxToMicroStx } from '@leather-wallet/utils'; import { logger } from '@shared/logger'; @@ -28,7 +29,6 @@ import { useWalletType } from '@app/common/use-wallet-type'; import { listenForStacksTxLedgerSigning } from '@app/features/ledger/flows/stacks-tx-signing/stacks-tx-signing-event-listeners'; import { useLedgerNavigate } from '@app/features/ledger/hooks/use-ledger-navigate'; import { useToast } from '@app/features/toasts/use-toast'; -import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useCurrentStacksAccount, useCurrentStacksAccountAddress, @@ -44,8 +44,8 @@ export function useTransactionPostConditions() { export function useUnsignedStacksTransactionBaseState() { const network = useCurrentStacksNetworkState(); - const { data: nextNonce } = useNextNonce(); const stxAddress = useCurrentStacksAccountAddress(); + const { data: nextNonce } = useNextNonce(stxAddress); const payload = useTransactionRequestState(); const postConditions = useTransactionPostConditions(); const account = useCurrentStacksAccount(); @@ -104,7 +104,8 @@ export function makePostCondition(options: PostConditionsOptions): PostCondition export function useGenerateUnsignedStacksTransaction() { const stacksTxBaseState = useUnsignedStacksTransactionBaseState(); - const { data: nextNonce } = useNextNonce(); + const stxAddress = useCurrentStacksAccountAddress(); + const { data: nextNonce } = useNextNonce(stxAddress); return useCallback( (values: StacksTransactionFormValues) => { diff --git a/src/shared/constants.ts b/src/shared/constants.ts index f8ba725704a..7f29bcdc611 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -8,8 +8,6 @@ export const HIGH_FEE_AMOUNT_STX = 5; export const HIGH_FEE_WARNING_LEARN_MORE_URL_BTC = 'https://bitcoinfees.earn.com/'; export const HIGH_FEE_WARNING_LEARN_MORE_URL_STX = 'https://hiro.so/questions/fee-estimates'; -export const DEFAULT_FEE_RATE = 400; - export const PERSISTENCE_CACHE_TIME = 1000 * 60 * 60 * 12; // 12 hours export const BTC_DECIMALS = 8; @@ -75,8 +73,6 @@ export interface NetworkConfiguration { }; } -export const STX20_API_BASE_URL_MAINNET = 'https://api.stx20.com/api/v1'; - export const HIRO_EXPLORER_URL = 'https://explorer.hiro.so'; export const HIRO_API_BASE_URL_MAINNET = 'https://api.hiro.so'; export const HIRO_API_BASE_URL_TESTNET = 'https://api.old.testnet.hiro.so'; @@ -186,6 +182,4 @@ export const defaultNetworksKeyedById: Record< [WalletDefaultNetworkConfigurationIds.devnet]: networkDevnet, }; -export const DEFAULT_LIST_LIMIT = 50; - export const TOKEN_NAME_LENGTH = 4; diff --git a/src/shared/models/account.model.ts b/src/shared/models/account.model.ts deleted file mode 100644 index 96f12d39e57..00000000000 --- a/src/shared/models/account.model.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AddressTokenOfferingLocked } from '@stacks/stacks-blockchain-api-types/generated'; - -type SelectedKeys = - | 'balance' - | 'total_sent' - | 'total_received' - | 'total_fees_sent' - | 'total_miner_rewards_received' - | 'locked'; - -export type AccountBalanceStxKeys = keyof Pick; - -/** - * This is a duplicated type from the types lib/generated API client - * We define it client side, as the library-returned types are not accurate - */ -export interface AddressBalanceResponse { - stx: { - balance: string; - total_sent: string; - total_received: string; - total_fees_sent: string; - total_miner_rewards_received: string; - lock_tx_id: string; - locked: string; - lock_height: number; - burnchain_lock_height: number; - burnchain_unlock_height: number; - }; - fungible_tokens: Record< - string, - { - balance: string; - total_sent: string; - total_received: string; - } - >; - non_fungible_tokens: Record< - string, - { - count: string; - total_sent: string; - total_received: string; - } - >; - token_offering_locked?: AddressTokenOfferingLocked; -} diff --git a/src/shared/models/api-types.ts b/src/shared/models/api-types.ts deleted file mode 100644 index d01ea2e555e..00000000000 --- a/src/shared/models/api-types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Paginated { - limit: number; - offset: number; - total: number; - results: T; -} diff --git a/src/shared/models/contract-types.ts b/src/shared/models/contract-types.ts deleted file mode 100644 index 84eb7fce5b3..00000000000 --- a/src/shared/models/contract-types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ContractInterfaceFunction } from '@stacks/rpc-client'; -import { ContractInterfaceResponse } from '@stacks/stacks-blockchain-api-types'; - -export type ContractInterfaceResponseWithFunctions = Omit< - ContractInterfaceResponse, - 'functions' -> & { - functions: ContractInterfaceFunction[]; -}; diff --git a/src/shared/route-urls.ts b/src/shared/route-urls.ts index 293f0a6e1de..41d749ec38a 100644 --- a/src/shared/route-urls.ts +++ b/src/shared/route-urls.ts @@ -25,7 +25,7 @@ export enum RouteUrls { AddNetwork = '/add-network', Fund = '/fund/:currency', FundChooseCurrency = '/fund-choose-currency', - IncreaseStxFee = '/increase-fee/stx', + IncreaseStxFee = '/increase-fee/stx/:txid', IncreaseBtcFee = '/increase-fee/btc', Send = '/send-transaction', ViewSecretKey = '/view-secret-key', diff --git a/tests/mocks/mock-apis.ts b/tests/mocks/mock-apis.ts index 5732c55f76c..e67f65ef604 100644 --- a/tests/mocks/mock-apis.ts +++ b/tests/mocks/mock-apis.ts @@ -1,7 +1,7 @@ import { Page } from '@playwright/test'; import { json } from '@tests/utils'; -import { mockStacksFeeRequests } from '@app/query/stacks/fees/fee.query.mocks'; +import { mockStacksFeeRequests } from './mock-stacks-fees'; export async function setupMockApis(page: Page) { await page.route(/chrome-extension/, route => route.continue()); diff --git a/src/app/query/stacks/fees/fee.query.mocks.ts b/tests/mocks/mock-stacks-fees.ts similarity index 100% rename from src/app/query/stacks/fees/fee.query.mocks.ts rename to tests/mocks/mock-stacks-fees.ts diff --git a/tests/specs/send/send-btc.spec.ts b/tests/specs/send/send-btc.spec.ts index eb7d8f9713f..0f6fca99426 100644 --- a/tests/specs/send/send-btc.spec.ts +++ b/tests/specs/send/send-btc.spec.ts @@ -1,11 +1,10 @@ +import { BtcFeeType } from '@leather-wallet/models'; import { TEST_TESTNET_ACCOUNT_2_BTC_ADDRESS } from '@tests/mocks/constants'; import { mockOrdinalsComApiHtmlResponse } from '@tests/mocks/mock-ordinalscom-api'; import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors'; import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors'; import { getDisplayerAddress } from '@tests/utils'; -import { BtcFeeType } from '@leather-wallet/models'; - import { test } from '../../fixtures/fixtures'; test.describe('send btc', () => { @@ -24,7 +23,7 @@ test.describe('send btc', () => { await sendPage.recipientInput.fill(TEST_TESTNET_ACCOUNT_2_BTC_ADDRESS); await sendPage.previewSendTxButton.click(); - await sendPage.feesListItem.filter({ hasText: BtcFeeType.High }).click(); + await sendPage.feesListItem.filter({ hasText: BtcFeeType.Low }).click(); const details = await sendPage.confirmationDetails.allInnerTexts(); test.expect(details).toBeTruthy(); @@ -36,7 +35,7 @@ test.describe('send btc', () => { await sendPage.recipientInput.blur(); await sendPage.page.waitForTimeout(1000); await sendPage.previewSendTxButton.click(); - await sendPage.feesListItem.filter({ hasText: BtcFeeType.High }).click(); + await sendPage.feesListItem.filter({ hasText: BtcFeeType.Low }).click(); const displayerAddress = await getDisplayerAddress(sendPage.confirmationDetailsRecipient); test.expect(displayerAddress).toEqual(TEST_TESTNET_ACCOUNT_2_BTC_ADDRESS); @@ -52,7 +51,7 @@ test.describe('send btc', () => { await sendPage.page.waitForTimeout(1000); await sendPage.previewSendTxButton.click(); - await sendPage.feesListItem.filter({ hasText: BtcFeeType.High }).click(); + await sendPage.feesListItem.filter({ hasText: BtcFeeType.Low }).click(); const displayerAddress = await getDisplayerAddress(sendPage.confirmationDetailsRecipient); test.expect(displayerAddress).toEqual(TEST_TESTNET_ACCOUNT_2_BTC_ADDRESS); @@ -97,7 +96,7 @@ test.describe('send btc', () => { await sendPage.recipientInput.fill(TEST_TESTNET_ACCOUNT_2_BTC_ADDRESS); await sendPage.previewSendTxButton.click(); - await sendPage.feesListItem.filter({ hasText: BtcFeeType.High }).click(); + await sendPage.feesListItem.filter({ hasText: BtcFeeType.Low }).click(); await sendPage.clickInfoCardButton(); @@ -155,7 +154,7 @@ test.describe('send btc', () => { await sendPage.recipientInput.fill(TEST_TESTNET_ACCOUNT_2_BTC_ADDRESS); await sendPage.previewSendTxButton.click(); - await sendPage.feesListItem.filter({ hasText: BtcFeeType.High }).click(); + await sendPage.feesListItem.filter({ hasText: BtcFeeType.Low }).click(); await sendPage.clickInfoCardButton();