From 832434e6b02df6948c14d4c1a0b7088b38c4493f Mon Sep 17 00:00:00 2001 From: Edgar Khanzadian Date: Fri, 24 May 2024 13:47:03 +0400 Subject: [PATCH] feat: use bitcoin queries from monorepo + inscriptions from monorepo --- .storybook/main.ts | 9 +- package.json | 7 +- pnpm-lock.yaml | 552 ++++++++---------- src/app/app.tsx | 43 +- .../hooks/balance/use-total-balance.tsx | 3 +- src/app/common/hooks/use-bitcoin-contracts.ts | 10 +- .../hooks/use-convert-to-fiat-amount.ts | 9 +- .../coinselect/local-coin-selection.ts | 2 +- .../bitcoin/fees/bitcoin-fees.spec.ts | 3 +- .../fees/calculate-max-bitcoin-spend.ts | 2 +- .../bitcoin/use-generate-bitcoin-tx.ts | 2 +- src/app/common/transactions/bitcoin/utils.ts | 2 +- src/app/common/utils.ts | 16 - .../validation/forms/amount-validators.ts | 2 +- .../hooks/use-bitcoin-custom-fee.tsx | 3 +- ...e-bitcoin-fees-list-multiple-recipients.ts | 9 +- .../use-bitcoin-fees-list.ts | 9 +- .../bitcoin-transaction-inscription-icon.tsx | 7 +- .../bitcoin-transaction-item.tsx | 18 +- .../components/inscription-preview.tsx | 9 +- .../components/inscription-text.tsx | 2 +- src/app/components/loaders/runes-loader.tsx | 3 +- .../loaders/src20-tokens-loader.tsx | 3 +- .../features/activity-list/activity-list.tsx | 6 +- .../btc-crypto-asset-item.tsx | 2 +- .../stx-crypto-asset-item.tsx | 2 +- .../components/bitcoin/inscription-text.tsx | 3 +- .../components/bitcoin/inscription.tsx | 13 +- .../components/bitcoin/ordinals.tsx | 14 +- .../collectibles/components/bitcoin/stamp.tsx | 3 +- .../components/bitcoin/stamps.tsx | 3 +- .../components/taproot-balance-displayer.tsx | 16 +- .../hooks/use-btc-increase-fee.ts | 2 +- .../pending-brc-20-transfers.tsx | 9 +- .../psbt-address-receive-totals.tsx | 3 +- .../psbt-address-transfer-totals.tsx | 3 +- .../components/psbt-inscription.tsx | 10 +- .../components/psbt-request-fee.tsx | 2 +- .../psbt-signer/hooks/use-parsed-inputs.tsx | 8 +- .../psbt-signer/psbt-signer.context.ts | 3 +- .../retrieve-taproot-to-native-segwit.tsx | 31 +- ...use-generate-retrieve-taproot-funds-tx.tsx | 32 +- .../hooks/use-stacks-transaction-summary.ts | 5 +- .../bitcoin-contract-list-item-layout.tsx | 2 +- .../bitcoin-contract-offer-input.tsx | 2 +- .../rpc-send-transfer-choose-fee.tsx | 3 +- .../rpc-send-transfer-confirmation.tsx | 6 +- .../pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx | 10 +- .../coinselect/select-inscription-coins.ts | 3 +- .../create-utxo-from-inscription.ts | 26 +- .../components/send-inscription-container.tsx | 17 +- .../components/send-inscription-loader.tsx | 4 +- .../hooks/use-generate-ordinal-tx.ts | 2 +- .../hooks/use-send-inscription-fees-list.ts | 13 +- .../hooks/use-send-inscription-form.tsx | 12 +- .../hooks/use-send-inscription-route-state.ts | 5 +- .../send-inscription-choose-fee.tsx | 6 +- .../send-inscription-review.tsx | 4 +- .../sent-inscription-summary.tsx | 4 +- .../bitcoin/hooks/use-calculate-max-spend.ts | 4 +- .../form/brc20/brc20-choose-fee.tsx | 2 +- .../brc20/brc20-send-form-confirmation.tsx | 2 +- .../form/btc/btc-choose-fee.tsx | 3 +- .../form/btc/btc-send-form-confirmation.tsx | 6 +- .../form/btc/btc-send-form.tsx | 2 +- .../form/stx/stx-send-form.tsx | 5 +- .../hooks/use-send-form-navigate.ts | 3 +- .../query/bitcoin/address/address.utils.ts | 7 - .../address/transactions-by-address.hooks.ts | 52 -- .../address/transactions-by-address.query.ts | 48 -- .../address/transactions-by-address.spec.ts | 26 - .../bitcoin/address/utxos-by-address.hooks.ts | 156 +---- .../bitcoin/address/utxos-by-address.query.ts | 100 ---- .../bitcoin/address/utxos-by-address.spec.tsx | 35 -- .../bitcoin-contracts-balance.hooks.ts | 2 +- .../btc-balance-native-segwit.hooks.ts | 4 +- .../balance/btc-balance-taproot.hooks.ts | 37 -- src/app/query/bitcoin/bitcoin-client.ts | 413 ------------- .../query/bitcoin/blockstream-rate-limiter.ts | 18 - .../send-accepted-bitcoin-contract-offer.ts | 21 - .../query/bitcoin/fees/fee-estimates.hooks.ts | 33 -- .../query/bitcoin/fees/fee-estimates.query.ts | 38 -- .../ordinals/brc20/brc20-tokens.hooks.ts | 29 +- .../ordinals/brc20/brc20-tokens.query.ts | 117 ---- .../ordinals/brc20/brc20-tokens.utils.spec.ts | 16 - .../ordinals/brc20/brc20-tokens.utils.ts | 51 -- .../ordinals/brc20/use-check-order-status.ts | 4 +- .../ordinals/inscription-by-id.query.ts | 8 - .../inscription-text-content.query.ts | 26 - .../bitcoin/ordinals/inscription.hooks.ts | 83 --- .../bitcoin/ordinals/inscription.query.ts | 38 -- .../ordinals/inscriptions-by-param.query.ts | 65 --- .../bitcoin/ordinals/inscriptions.hooks.ts | 32 - .../bitcoin/ordinals/inscriptions.query.ts | 279 --------- .../bitcoin/ordinals/inscriptions.spec.ts | 14 - src/app/query/bitcoin/ordinalsbot-client.ts | 164 ------ .../runes/runes-outputs-by-address.query.ts | 32 - .../bitcoin/runes/runes-ticker-info.query.ts | 34 -- .../runes/runes-wallet-balances.query.ts | 35 -- src/app/query/bitcoin/runes/runes.hooks.ts | 38 -- src/app/query/bitcoin/runes/runes.utils.ts | 29 - .../bitcoin/stamps/stamps-by-address.hooks.ts | 36 -- .../bitcoin/stamps/stamps-by-address.query.ts | 100 ---- .../bitcoin/transaction/transaction.query.ts | 54 -- .../use-bitcoin-broadcast-transaction.ts | 77 --- .../bitcoin/transaction/use-check-utxos.ts | 174 ------ .../common/market-data/market-data.hooks.ts | 64 -- .../common/market-data/market-data.query.ts | 10 - .../vendors/binance-market-data.query.ts | 25 - .../vendors/coincap-market-data.query.ts | 27 - .../vendors/coingecko-market-data.query.ts | 30 - .../remote-config/remote-config.query.ts | 178 +----- src/app/store/common/api-clients.hooks.ts | 5 +- .../software-keys/software-key.actions.ts | 2 +- src/shared/constants.ts | 4 - src/shared/environment.ts | 1 + src/shared/models/form.model.ts | 5 +- src/shared/models/inscription.model.ts | 155 ----- src/shared/models/money.model.ts | 2 +- src/shared/utils.ts | 4 - tests/mocks/mock-inscriptions.ts | 15 +- webpack/webpack.config.base.js | 6 + 122 files changed, 592 insertions(+), 3507 deletions(-) delete mode 100644 src/app/query/bitcoin/address/address.utils.ts delete mode 100644 src/app/query/bitcoin/address/transactions-by-address.hooks.ts delete mode 100644 src/app/query/bitcoin/address/transactions-by-address.query.ts delete mode 100644 src/app/query/bitcoin/address/transactions-by-address.spec.ts delete mode 100644 src/app/query/bitcoin/address/utxos-by-address.query.ts delete mode 100644 src/app/query/bitcoin/address/utxos-by-address.spec.tsx delete mode 100644 src/app/query/bitcoin/balance/btc-balance-taproot.hooks.ts delete mode 100644 src/app/query/bitcoin/bitcoin-client.ts delete mode 100644 src/app/query/bitcoin/blockstream-rate-limiter.ts delete mode 100644 src/app/query/bitcoin/contract/send-accepted-bitcoin-contract-offer.ts delete mode 100644 src/app/query/bitcoin/fees/fee-estimates.hooks.ts delete mode 100644 src/app/query/bitcoin/fees/fee-estimates.query.ts delete mode 100644 src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts delete mode 100644 src/app/query/bitcoin/ordinals/brc20/brc20-tokens.utils.spec.ts delete mode 100644 src/app/query/bitcoin/ordinals/brc20/brc20-tokens.utils.ts delete mode 100644 src/app/query/bitcoin/ordinals/inscription-by-id.query.ts delete mode 100644 src/app/query/bitcoin/ordinals/inscription-text-content.query.ts delete mode 100644 src/app/query/bitcoin/ordinals/inscription.hooks.ts delete mode 100644 src/app/query/bitcoin/ordinals/inscription.query.ts delete mode 100644 src/app/query/bitcoin/ordinals/inscriptions-by-param.query.ts delete mode 100644 src/app/query/bitcoin/ordinals/inscriptions.hooks.ts delete mode 100644 src/app/query/bitcoin/ordinals/inscriptions.query.ts delete mode 100644 src/app/query/bitcoin/ordinals/inscriptions.spec.ts delete mode 100644 src/app/query/bitcoin/ordinalsbot-client.ts delete mode 100644 src/app/query/bitcoin/runes/runes-outputs-by-address.query.ts delete mode 100644 src/app/query/bitcoin/runes/runes-ticker-info.query.ts delete mode 100644 src/app/query/bitcoin/runes/runes-wallet-balances.query.ts delete mode 100644 src/app/query/bitcoin/runes/runes.hooks.ts delete mode 100644 src/app/query/bitcoin/runes/runes.utils.ts delete mode 100644 src/app/query/bitcoin/stamps/stamps-by-address.hooks.ts delete mode 100644 src/app/query/bitcoin/stamps/stamps-by-address.query.ts delete mode 100644 src/app/query/bitcoin/transaction/transaction.query.ts delete mode 100644 src/app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction.ts delete mode 100644 src/app/query/bitcoin/transaction/use-check-utxos.ts delete mode 100644 src/app/query/common/market-data/market-data.hooks.ts delete mode 100644 src/app/query/common/market-data/market-data.query.ts delete mode 100644 src/app/query/common/market-data/vendors/binance-market-data.query.ts delete mode 100644 src/app/query/common/market-data/vendors/coincap-market-data.query.ts delete mode 100644 src/app/query/common/market-data/vendors/coingecko-market-data.query.ts delete mode 100644 src/shared/models/inscription.model.ts diff --git a/.storybook/main.ts b/.storybook/main.ts index 6a182aa2387..ff281124586 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -1,5 +1,6 @@ import type { StorybookConfig } from '@storybook/react-webpack5'; import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'; +import Webpack from 'webpack'; const config: StorybookConfig = { addons: [ @@ -45,7 +46,7 @@ const config: StorybookConfig = { }, '@storybook/addon-mdx-gfm', '@storybook/addon-webpack5-compiler-swc', - '@chromatic-com/storybook' + '@chromatic-com/storybook', ], docs: { autodocs: 'tag', @@ -79,6 +80,12 @@ const config: StorybookConfig = { extensions: config.resolve.extensions, }) ); + config.plugins ??= []; + config.plugins.push( + new Webpack.ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + }) + ); return config; }, }; diff --git a/package.json b/package.json index 2f4b6341da8..f873a8af726 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,8 @@ "@dlc-link/dlc-tools": "1.1.1", "@fungible-systems/zone-file": "2.0.0", "@hirosystems/token-metadata-api-client": "1.2.0", - "@leather-wallet/models": "0.6.4", + "@leather-wallet/models": "0.6.5", + "@leather-wallet/query": "0.6.6", "@leather-wallet/tokens": "0.0.15", "@ledgerhq/hw-transport-webusb": "6.27.19", "@noble/hashes": "1.3.2", @@ -252,8 +253,8 @@ "@actions/core": "1.10.1", "@btckit/types": "0.0.19", "@chromatic-com/storybook": "1.2.23", - "@leather-wallet/prettier-config": "0.0.5", - "@leather-wallet/rpc": "0.3.0", + "@leather-wallet/prettier-config": "0.4.0", + "@leather-wallet/rpc": "0.3.9", "@ls-lint/ls-lint": "2.2.3", "@mdx-js/loader": "3.0.0", "@pandacss/dev": "0.32.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 707b243a083..60e7867011a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,8 +30,11 @@ dependencies: specifier: 1.2.0 version: 1.2.0 '@leather-wallet/models': - specifier: 0.6.4 - version: 0.6.4 + specifier: 0.6.5 + version: 0.6.5 + '@leather-wallet/query': + specifier: 0.6.6 + version: 0.6.6(react-dom@18.2.0)(react@18.2.0) '@leather-wallet/tokens': specifier: 0.0.15 version: 0.0.15 @@ -166,7 +169,7 @@ dependencies: version: 4.7.7 '@typescript-eslint/eslint-plugin': specifier: 7.5.0 - version: 7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.4) + version: 7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.56.0)(typescript@5.4.4) '@zondax/ledger-stacks': specifier: 1.0.4 version: 1.0.4 @@ -377,11 +380,11 @@ devDependencies: specifier: 1.2.23 version: 1.2.23(react@18.2.0) '@leather-wallet/prettier-config': - specifier: 0.0.5 - version: 0.0.5 + specifier: 0.4.0 + version: 0.4.0 '@leather-wallet/rpc': - specifier: 0.3.0 - version: 0.3.0 + specifier: 0.3.9 + version: 0.3.9 '@ls-lint/ls-lint': specifier: 2.2.3 version: 2.2.3 @@ -399,10 +402,10 @@ devDependencies: version: 0.5.11(@types/webpack@5.28.5)(react-refresh@0.14.0)(webpack-dev-server@4.15.1)(webpack-hot-middleware@2.26.1)(webpack@5.91.0) '@redux-devtools/cli': specifier: 4.0.0 - version: 4.0.0(@babel/core@7.24.5)(@types/react-dom@18.2.19)(@types/styled-components@5.1.34) + version: 4.0.0(@babel/core@7.24.4)(@types/react-dom@18.2.19)(@types/styled-components@5.1.34) '@redux-devtools/remote': specifier: 0.9.3 - version: 0.9.3(@redux-devtools/core@4.0.0)(immutable@4.3.5)(redux@5.0.1) + version: 0.9.3(@redux-devtools/core@4.0.0)(immutable@4.3.4)(redux@5.0.1) '@schemastore/web-manifest': specifier: 0.0.6 version: 0.0.6 @@ -522,7 +525,7 @@ devDependencies: version: 4.4.4 '@typescript-eslint/parser': specifier: 7.5.0 - version: 7.5.0(eslint@8.57.0)(typescript@5.4.4) + version: 7.5.0(eslint@8.56.0)(typescript@5.4.4) '@vitest/coverage-istanbul': specifier: 1.4.0 version: 1.4.0(vitest@1.4.0) @@ -582,19 +585,19 @@ devDependencies: version: 4.1.0(webpack@5.91.0) eslint-plugin-deprecation: specifier: 2.0.0 - version: 2.0.0(eslint@8.57.0)(typescript@5.4.4) + version: 2.0.0(eslint@8.56.0)(typescript@5.4.4) eslint-plugin-mdx: specifier: 3.1.5 - version: 3.1.5(eslint@8.57.0) + version: 3.1.5(eslint@8.56.0) eslint-plugin-react: specifier: 7.34.1 - version: 7.34.1(eslint@8.57.0) + version: 7.34.1(eslint@8.56.0) eslint-plugin-react-hooks: specifier: 4.6.0 - version: 4.6.0(eslint@8.57.0) + version: 4.6.0(eslint@8.56.0) eslint-plugin-storybook: specifier: 0.8.0 - version: 0.8.0(eslint@8.57.0)(typescript@5.4.4) + version: 0.8.0(eslint@8.56.0)(typescript@5.4.4) file-loader: specifier: 6.2.0 version: 6.2.0(webpack@5.91.0) @@ -624,7 +627,7 @@ devDependencies: version: 2.1.0(webpack@5.91.0) react-dev-utils: specifier: 12.0.1 - version: 12.0.1(eslint@8.57.0)(typescript@5.4.4)(webpack@5.91.0) + version: 12.0.1(eslint@8.56.0)(typescript@5.4.4)(webpack@5.91.0) react-refresh: specifier: 0.14.0 version: 0.14.0 @@ -691,7 +694,6 @@ packages: /@aashutoshrathi/word-wrap@1.2.6: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} - dev: true /@actions/core@1.10.1: resolution: {integrity: sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==} @@ -988,29 +990,6 @@ packages: - supports-color dev: true - /@babel/core@7.24.5: - resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.5 - '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5) - '@babel/helpers': 7.24.5 - '@babel/parser': 7.24.5 - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.5 - '@babel/types': 7.24.5 - convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@5.5.0) - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/generator@7.17.7: resolution: {integrity: sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==} engines: {node: '>=6.9.0'} @@ -1040,16 +1019,6 @@ packages: jsesc: 2.5.2 dev: true - /@babel/generator@7.24.5: - resolution: {integrity: sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.5 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - dev: true - /@babel/helper-annotate-as-pure@7.22.5: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} engines: {node: '>=6.9.0'} @@ -1154,13 +1123,6 @@ packages: '@babel/types': 7.24.0 dev: true - /@babel/helper-module-imports@7.24.3: - resolution: {integrity: sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.5 - dev: true - /@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} engines: {node: '>=6.9.0'} @@ -1189,20 +1151,6 @@ packages: '@babel/helper-validator-identifier': 7.22.20 dev: true - /@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5): - resolution: {integrity: sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.24.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.24.3 - '@babel/helper-simple-access': 7.24.5 - '@babel/helper-split-export-declaration': 7.24.5 - '@babel/helper-validator-identifier': 7.24.5 - dev: true - /@babel/helper-optimise-call-expression@7.22.5: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} @@ -1246,13 +1194,6 @@ packages: '@babel/types': 7.24.0 dev: true - /@babel/helper-simple-access@7.24.5: - resolution: {integrity: sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.5 - dev: true - /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} @@ -1267,33 +1208,16 @@ packages: '@babel/types': 7.24.0 dev: true - /@babel/helper-split-export-declaration@7.24.5: - resolution: {integrity: sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.24.5 - dev: true - /@babel/helper-string-parser@7.23.4: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-string-parser@7.24.1: - resolution: {integrity: sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-validator-identifier@7.24.5: - resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-validator-identifier@8.0.0-alpha.5: resolution: {integrity: sha512-kcoGwdxi58Npdfof0ghzycZQ1n7USE0DGNDCvR6f2pYxmAQpxrauuJnGIgOqCyOAT+zLQAnjl/kYgLCWYTs6QA==} engines: {node: ^16.20.0 || ^18.16.0 || >=20.0.0} @@ -1335,17 +1259,6 @@ packages: - supports-color dev: true - /@babel/helpers@7.24.5: - resolution: {integrity: sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.24.0 - '@babel/traverse': 7.24.5 - '@babel/types': 7.24.5 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/highlight@7.23.4: resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} engines: {node: '>=6.9.0'} @@ -1390,14 +1303,6 @@ packages: '@babel/types': 7.24.0 dev: true - /@babel/parser@7.24.5: - resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} - engines: {node: '>=6.0.0'} - hasBin: true - dependencies: - '@babel/types': 7.24.5 - dev: true - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.24.0): resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==} engines: {node: '>=6.9.0'} @@ -1544,13 +1449,13 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.5): + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.4): resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.5 + '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -2384,13 +2289,13 @@ packages: resolution: {integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.23.5 - '@babel/generator': 7.23.6 + '@babel/code-frame': 7.24.2 + '@babel/generator': 7.24.4 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@babel/types': 7.24.0 debug: 4.3.4(supports-color@5.5.0) globals: 11.12.0 @@ -2434,24 +2339,6 @@ packages: - supports-color dev: true - /@babel/traverse@7.24.5: - resolution: {integrity: sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.24.5 - '@babel/parser': 7.24.5 - '@babel/types': 7.24.5 - debug: 4.3.4(supports-color@5.5.0) - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/types@7.17.0: resolution: {integrity: sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==} engines: {node: '>=6.9.0'} @@ -2469,15 +2356,6 @@ packages: to-fast-properties: 2.0.0 dev: true - /@babel/types@7.24.5: - resolution: {integrity: sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.24.1 - '@babel/helper-validator-identifier': 7.24.5 - to-fast-properties: 2.0.0 - dev: true - /@base2/pretty-print-object@1.0.1: resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} dev: true @@ -3449,16 +3327,6 @@ packages: dependencies: eslint: 8.56.0 eslint-visitor-keys: 3.4.3 - dev: true - - /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 /@eslint-community/regexpp@4.10.0: resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} @@ -3488,11 +3356,6 @@ packages: /@eslint/js@8.56.0: resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dev: true - - /@eslint/js@8.57.0: - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} /@fal-works/esbuild-plugin-global-externals@2.1.2: resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==} @@ -3696,20 +3559,40 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true - /@leather-wallet/models@0.4.0: - resolution: {integrity: sha512-6uwhdJxEhIpCI/29TPQIXAzfcz4niVIIujkGuwRFSyUiT/E8IEvjM30A3p9pMtEPCzO2UnpVERGLiLSIk48fgg==} + /@leather-wallet/bitcoin@0.6.5: + resolution: {integrity: sha512-zJPLe4qCjex/qnyyi1mR1D3Dza3eec6p+VRQ5l/CJq6vwVmpQ86ISYZ6EKQW9BSoXy5l7RWsXXOx4LPZr1OGwA==} dependencies: - bignumber.js: 9.1.2 - dev: true + '@bitcoinerlab/secp256k1': 1.0.2 + '@leather-wallet/constants': 0.6.1 + '@leather-wallet/models': 0.6.5 + '@leather-wallet/utils': 0.6.5 + '@noble/hashes': 1.3.3 + '@noble/secp256k1': 2.0.0 + '@scure/base': 1.1.3 + '@scure/bip32': 1.3.3 + '@scure/bip39': 1.2.1 + '@scure/btc-signer': 1.2.1 + '@stacks/common': 6.8.1 + '@stacks/transactions': 6.9.0 + bip32: 4.0.0 + bitcoinjs-lib: 6.1.3 + ecpair: 2.1.0 + varuint-bitcoin: 1.1.2 + transitivePeerDependencies: + - encoding + dev: false + + /@leather-wallet/constants@0.6.1: + resolution: {integrity: sha512-hchHbLTEDueM1vFe1xmYeJLx2hj4cQaDQX5DmgDZ/W1haBqfQ1meWxkX1FPXk8af9Dtm0ZfF957LkUWbkXnSTg==} + dev: false - /@leather-wallet/models@0.6.4: - resolution: {integrity: sha512-VPtMoeUeulHaRk5ECo1OCtwzV3QLh9uihPjr5fd1dmWPCiiy2sVHOM2g5+VU/NRaUJU7aBB5Pjmj3pyatWvSqg==} + /@leather-wallet/models@0.6.5: + resolution: {integrity: sha512-9ULJLp9PgxEF13/V9avNyibIEqlAXlnPOXSmdhoJ0Tupa7zwEkG8RZhtuhdxV1dHm3HELPJYfJo8z2DqM+YjHQ==} dependencies: bignumber.js: 9.1.2 - dev: false - /@leather-wallet/prettier-config@0.0.5: - resolution: {integrity: sha512-cK44e4XqYghcbd5ow8AKzK0NyNQ0r0V/PGVm940PN17tDxZ7JykBcEmSI1iGRkCnTCy/RE/qHkgGBW5gYXhM7g==} + /@leather-wallet/prettier-config@0.4.0: + resolution: {integrity: sha512-fKLW8t2DqT0H23iKY0pCXRJpynKOz/GS6V91qtFpgrFUgAEFJLf40+gK7Y9OyEgNqCP0h7zYwjFhDbqMIbac+A==} dependencies: '@trivago/prettier-plugin-sort-imports': 4.3.0(prettier@3.2.5) prettier: 3.2.5 @@ -3718,16 +3601,60 @@ packages: - supports-color dev: true - /@leather-wallet/rpc@0.3.0: - resolution: {integrity: sha512-jRexusk82Bax0CMIC9T2ApvnxA5rvd1yWjRyOxeImHdfQUK140MMij/Za8OcC8TYcFGMZbm3DEK1Xh9/NY6oIg==} + /@leather-wallet/query@0.6.6(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-rwNLLQDRKA5LJ3UESltkbgy8IuyzrV1QDse7tL/3QwM6y7L0IcN1mMx/IDwjVGPutvsCVJPed1/j2ZMG+pDifw==} + peerDependencies: + react: '*' dependencies: - '@leather-wallet/models': 0.4.0 - dev: true + '@leather-wallet/bitcoin': 0.6.5 + '@leather-wallet/constants': 0.6.1 + '@leather-wallet/models': 0.6.5 + '@leather-wallet/rpc': 0.3.9 + '@leather-wallet/utils': 0.6.5 + '@noble/hashes': 1.3.3 + '@scure/base': 1.1.3 + '@scure/bip32': 1.3.3 + '@scure/btc-signer': 1.2.1 + '@stacks/common': 6.8.1 + '@stacks/rpc-client': 0.8.18 + '@stacks/stacks-blockchain-api-types': 7.8.2 + '@tanstack/query-sync-storage-persister': 4.35.7 + '@tanstack/react-query': 4.35.7(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-query-devtools': 4.35.7(@tanstack/react-query@4.35.7)(react-dom@18.2.0)(react@18.2.0) + '@tanstack/react-query-persist-client': 4.35.7(@tanstack/react-query@4.35.7) + axios: 1.6.7 + bignumber.js: 9.1.2 + lodash.get: 4.4.2 + p-queue: 8.0.1 + react: 18.2.0 + url-join: 5.0.0 + yup: 1.3.3 + zod: 3.23.6 + transitivePeerDependencies: + - debug + - encoding + - react-dom + - react-native + dev: false + + /@leather-wallet/rpc@0.3.9: + resolution: {integrity: sha512-q0uKPqrSLv9/hj5gairpEm5hG1JdJ9sFOa6fapL9a4EsQzGbbNS6bOF7pNknMo/7cRGzTfDJmFlxLreLsGB1Gg==} + dependencies: + '@leather-wallet/models': 0.6.5 /@leather-wallet/tokens@0.0.15: resolution: {integrity: sha512-/jG36yCB8YRtyn1/sUgSeFwQjHhf6OfT0vsf384kbIXCzuaGhJpNl7GN0qWQkrxOKB5ahVuvqRwqaKFgwg1lUg==} dev: false + /@leather-wallet/utils@0.6.5: + resolution: {integrity: sha512-fVVpuqcfW8k5kvp7pQZiYC3HQ6GSmjk+M3PB/FGYVRBUrzHYDaSxiQi/L1d7g/dGGuspz0nD7kG3fMhli6iNyA==} + dependencies: + '@leather-wallet/constants': 0.6.1 + '@leather-wallet/models': 0.6.5 + '@leather-wallet/rpc': 0.3.9 + bignumber.js: 9.1.2 + dev: false + /@ledgerhq/devices@8.2.0: resolution: {integrity: sha512-XROTW2gTmmuy+YPPDjdtKKTQ3mfxrPtKtV+a9QFbj8f5MnjVMV0Zpy1BIB4CyIMsVVi4z6+nI67auT7IlsM3SQ==} dependencies: @@ -5513,7 +5440,7 @@ packages: redux: 4.2.1 redux-persist: 6.0.0(react@18.2.0)(redux@4.2.1) socketcluster-client: 17.2.2 - styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) transitivePeerDependencies: - '@types/react-dom' - bufferutil @@ -5540,7 +5467,7 @@ packages: redux-devtools-themes: 1.0.0 dev: true - /@redux-devtools/cli@4.0.0(@babel/core@7.24.5)(@types/react-dom@18.2.19)(@types/styled-components@5.1.34): + /@redux-devtools/cli@4.0.0(@babel/core@7.24.4)(@types/react-dom@18.2.19)(@types/styled-components@5.1.34): resolution: {integrity: sha512-MACJmhnNS8Q0P8ldYgH+p7//U6QdetQqnIS5KZRzd8sp4rYSegzTqsQuf4eGZ4PSzrVOoVezj9eH6ESZFm9/9Q==} engines: {node: '>= 18.12.0'} hasBin: true @@ -5568,7 +5495,7 @@ packages: semver: 7.6.0 socketcluster-server: 19.0.1 sqlite3: 5.1.6 - styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) uuid: 9.0.1 transitivePeerDependencies: - '@babel/core' @@ -5645,7 +5572,7 @@ packages: react-icons: 4.12.0(react@18.2.0) redux: 4.2.1 simple-diff: 1.7.2 - styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) dev: true /@redux-devtools/inspector-monitor-trace-tab@4.0.0(@emotion/react@11.11.3)(@redux-devtools/inspector-monitor@6.0.0)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1): @@ -5748,14 +5675,14 @@ packages: redux-devtools-themes: 1.0.0 dev: true - /@redux-devtools/remote@0.9.3(@redux-devtools/core@4.0.0)(immutable@4.3.5)(redux@5.0.1): + /@redux-devtools/remote@0.9.3(@redux-devtools/core@4.0.0)(immutable@4.3.4)(redux@5.0.1): resolution: {integrity: sha512-M6sG/ekZiFjzVmm8tNra1c5asz6GfitdDnsCTt+Sg2hzK2HdR2rhHSbA0UyVk3/WNjor/wmY6wISmn1Om0T4KA==} peerDependencies: redux: ^3.5.2 || ^4.0.0 || ^5.0.0 dependencies: '@babel/runtime': 7.24.4 '@redux-devtools/instrument': 2.2.0(redux@5.0.1) - '@redux-devtools/utils': 3.0.0(@redux-devtools/core@4.0.0)(immutable@4.3.5)(redux@5.0.1) + '@redux-devtools/utils': 3.0.0(@redux-devtools/core@4.0.0)(immutable@4.3.4)(redux@5.0.1) jsan: 3.1.14 querystring: 0.2.1 redux: 5.0.1 @@ -5797,18 +5724,18 @@ packages: react-json-tree: 0.18.0(@types/react@18.2.57)(react@18.2.0) redux: 4.2.1 redux-devtools-themes: 1.0.0 - styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) transitivePeerDependencies: - react-dom dev: true - /@redux-devtools/serialize@0.4.2(immutable@4.3.5): + /@redux-devtools/serialize@0.4.2(immutable@4.3.4): resolution: {integrity: sha512-YVqZCChJld5l3Ni2psEZ5loe9x5xpf9J4ckz+7OJdzCNsplC7vzjnkQbFxE6+ULZbywRVp+nSBslTXmaXqAw4A==} peerDependencies: immutable: ^4.0.0 dependencies: '@babel/runtime': 7.24.4 - immutable: 4.3.5 + immutable: 4.3.4 jsan: 3.1.14 dev: true @@ -5831,7 +5758,7 @@ packages: react: 18.2.0 redux: 4.2.1 redux-devtools-themes: 1.0.0 - styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) transitivePeerDependencies: - react-dom dev: true @@ -5863,12 +5790,12 @@ packages: react-select: 5.8.0(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) redux-devtools-themes: 1.0.0 simple-element-resize-detector: 1.3.0 - styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) transitivePeerDependencies: - react-dom dev: true - /@redux-devtools/utils@3.0.0(@redux-devtools/core@4.0.0)(immutable@4.3.5)(redux@5.0.1): + /@redux-devtools/utils@3.0.0(@redux-devtools/core@4.0.0)(immutable@4.3.4)(redux@5.0.1): resolution: {integrity: sha512-m1AJoxQffm1/6m0qrkb7gW0FkmaAoi1/HJzmdkchAeA8sAJhzGOnXJEpsjmXPt5BIHxg0zsglA+5FsgGWXa97A==} peerDependencies: '@redux-devtools/core': ^4.0.0 @@ -5877,10 +5804,10 @@ packages: dependencies: '@babel/runtime': 7.24.4 '@redux-devtools/core': 4.0.0(react-redux@9.1.0)(react@18.2.0)(redux@5.0.1) - '@redux-devtools/serialize': 0.4.2(immutable@4.3.5) + '@redux-devtools/serialize': 0.4.2(immutable@4.3.4) '@types/get-params': 0.1.2 get-params: 0.1.2 - immutable: 4.3.5 + immutable: 4.3.4 jsan: 3.1.14 lodash: 4.17.21 nanoid: 3.3.4 @@ -6073,6 +6000,10 @@ packages: resolution: {integrity: sha512-mhMEg7qUdpPuqdTAs2xh2GBAISAE++74uFX3y84G0GPwkBF1kdiDVypKbHIbizYTEFty+Ieurxoagq9KRkgEiw==} dev: true + /@scure/base@1.1.3: + resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} + dev: false + /@scure/base@1.1.5: resolution: {integrity: sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==} @@ -6102,6 +6033,13 @@ packages: '@noble/hashes': 1.1.5 '@scure/base': 1.1.5 + /@scure/bip39@1.2.1: + resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} + dependencies: + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.5 + dev: false + /@scure/bip39@1.3.0: resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} dependencies: @@ -6428,14 +6366,14 @@ packages: resolution: {integrity: sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==} dev: false - /@socket.io/component-emitter@3.1.0: - resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==} + /@socket.io/component-emitter@3.1.2: + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} dev: false /@stacks/auth@6.11.0: resolution: {integrity: sha512-I4oWBoTu57lg3dL+PC/tpRdAU/1ETYtMw4g7f76a8tauiFDDvk4Dve5MNl5fknrH+rhtvqkwLILc3UBFIMfu0Q==} dependencies: - '@stacks/common': 6.10.0 + '@stacks/common': 6.13.0 '@stacks/encryption': 6.13.1 '@stacks/network': 6.10.0 '@stacks/profile': 6.11.0 @@ -6460,7 +6398,7 @@ packages: /@stacks/blockchain-api-client@6.3.4: resolution: {integrity: sha512-4O9qe7m2XKG8PNZ9n5cvhji95IDZ29WO1X2ICgeBPdGc5Y0WGmo0wgIFAROh37pGSkBJsuJjy15ICdP47iy8+w==} dependencies: - '@stacks/stacks-blockchain-api-types': 6.3.4 + '@stacks/stacks-blockchain-api-types': 7.3.6 '@types/ws': 7.4.7 cross-fetch: 3.1.5 eventemitter3: 4.0.7 @@ -6487,6 +6425,7 @@ packages: dependencies: '@types/bn.js': 5.1.5 '@types/node': 18.19.29 + dev: false /@stacks/common@6.13.0: resolution: {integrity: sha512-wwzyihjaSdmL6NxKvDeayy3dqM0L0Q2sawmdNtzJDi0FnXuJGm5PeapJj7bEfcI9XwI7Bw5jZoC6mCn9nc5YIw==} @@ -6557,7 +6496,7 @@ packages: /@stacks/network@6.10.0: resolution: {integrity: sha512-mbiZ8nlsyy77ndmBdaqhHXii22IFdK4ThRcOQs9j/O00DkAr04jCM4GV5Q+VLUnZ9OBoJq7yOV7Pf6jglh+0hw==} dependencies: - '@stacks/common': 6.10.0 + '@stacks/common': 6.13.0 cross-fetch: 3.1.8 transitivePeerDependencies: - encoding @@ -6574,7 +6513,7 @@ packages: /@stacks/profile@6.11.0: resolution: {integrity: sha512-GXheCbu2g5X5qCAj1rSWCeZEbzHTW/IhPMOInPA15FH4VUBu09AonWtDs9w+sPUhbbiG9Zx4A0TLDaO52hHpBg==} dependencies: - '@stacks/common': 6.10.0 + '@stacks/common': 6.13.0 '@stacks/network': 6.10.0 '@stacks/transactions': 6.11.0 jsontokens: 4.0.1 @@ -6596,6 +6535,15 @@ packages: - encoding dev: false + /@stacks/rpc-client@0.8.18: + resolution: {integrity: sha512-DV898sXOBKHJGEtth6L4gLvd4iC2/sevwZrs5UySDrHK9x2pMz+GdDnUQ20Bsg56mwQDhJXqvJIybHNI3udDQg==} + dependencies: + '@blockstack/stacks-transactions': 0.7.0 + cross-fetch: 3.1.8 + transitivePeerDependencies: + - encoding + dev: false + /@stacks/rpc-client@1.0.3: resolution: {integrity: sha512-lao7MKCq39VA86v2rJzmgjHKG5bg9LWdLSzvktuEy3lfatVki/hRm6sitkmNhYVcdUVp3YV9gyW6mvu7U9weWw==} dependencies: @@ -6607,16 +6555,21 @@ packages: /@stacks/stacks-blockchain-api-types@6.3.4: resolution: {integrity: sha512-zrjKPGJN4p1azzmh8j0Yj+ZjQ0L9F01qJjAxOtBpapmFbGr1NUuPT1GthIg76y+dobdjSDPN39LpoJG/FbWFLw==} + dev: true + + /@stacks/stacks-blockchain-api-types@7.3.6: + resolution: {integrity: sha512-Y8/knKsXKVTtCO46aETAzkBGDrilMyi9cPcuW20UQSPZISD/MApxdEJUOml+OOgvkywO844TSdYqYmtG5D1xWQ==} + dev: false - /@stacks/stacks-blockchain-api-types@7.10.0: - resolution: {integrity: sha512-LfDishvEsmDJ6OXfgohkOIIsLTwvKVn3NKELaWTaZndN2Pucsk3Uz8NPDhVM5Ij1rGjFGT2bwcHNoGE7k3GhxQ==} + /@stacks/stacks-blockchain-api-types@7.8.2: + resolution: {integrity: sha512-wcDSdgIZx/ttQfUTPtGJOIyEkTOjmCsC79TaIyxTIiihSgrGppqTuzkwHD/DyuQkcJtUZvDTxMsAXkBKShE1kw==} dev: false /@stacks/storage@6.11.0: resolution: {integrity: sha512-tZ39pntKzNaJQiDmG32AMU5HKZ3kuU4jd0t9/9A1SHVfqXDWxO1Kuu81MZk4ujcXfJM5zZtrMAMOj2Tgc988GA==} dependencies: '@stacks/auth': 6.11.0 - '@stacks/common': 6.10.0 + '@stacks/common': 6.13.0 '@stacks/encryption': 6.13.1 '@stacks/network': 6.10.0 base64-js: 1.5.1 @@ -6661,7 +6614,7 @@ packages: dependencies: '@noble/hashes': 1.1.5 '@noble/secp256k1': 1.7.1 - '@stacks/common': 6.10.0 + '@stacks/common': 6.13.0 '@stacks/network': 6.10.0 c32check: 2.0.0 lodash.clonedeep: 4.5.0 @@ -9002,6 +8955,12 @@ packages: '@tanstack/query-core': 4.35.7 dev: false + /@tanstack/query-sync-storage-persister@4.35.7: + resolution: {integrity: sha512-LwOV4FfbRQmCH3MUXU2xngnws5AzRJ8mtc9JlDogRVwutlDEeY2bYSiu7xV6NDRLPf0/+0KCMzWeBx6Tlgnkgw==} + dependencies: + '@tanstack/query-persist-client-core': 4.35.7 + dev: false + /@tanstack/react-query-devtools@4.35.7(@tanstack/react-query@4.35.7)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-oe3reHNvXBTUvNb9jwLY8EYOXyp8Oq8/c40iwpXBnEkAtJI+RryrCXaGKFTivg72roPcYHzKILQHR9jbX8sn1Q==} peerDependencies: @@ -9121,7 +9080,7 @@ packages: optional: true dependencies: '@babel/generator': 7.17.7 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@babel/traverse': 7.23.2 '@babel/types': 7.17.0 javascript-natural-sort: 0.7.1 @@ -9173,7 +9132,7 @@ packages: /@types/babel__core@7.20.5: resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} dependencies: - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@babel/types': 7.24.0 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 @@ -9189,7 +9148,7 @@ packages: /@types/babel__template@7.4.4: resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} dependencies: - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@babel/types': 7.24.0 dev: true @@ -10043,7 +10002,7 @@ packages: resolution: {integrity: sha512-Tuk4q7q0DnpzyJDI4aMeghGuFu2iS1QAdKpabn8JfbtfGmVDUgvZv1I7mEjP61Bvnp3ljKCC8BE6YYSTNxmvRQ==} dev: true - /@typescript-eslint/eslint-plugin@7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.57.0)(typescript@5.4.4): + /@typescript-eslint/eslint-plugin@7.5.0(@typescript-eslint/parser@7.5.0)(eslint@8.56.0)(typescript@5.4.4): resolution: {integrity: sha512-HpqNTH8Du34nLxbKgVMGljZMG0rJd2O9ecvr2QLYp+7512ty1j42KnsFwspPXg1Vh8an9YImf6CokUBltisZFQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: @@ -10055,13 +10014,13 @@ packages: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.5.0(eslint@8.57.0)(typescript@5.4.4) + '@typescript-eslint/parser': 7.5.0(eslint@8.56.0)(typescript@5.4.4) '@typescript-eslint/scope-manager': 7.5.0 - '@typescript-eslint/type-utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4) - '@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4) + '@typescript-eslint/type-utils': 7.5.0(eslint@8.56.0)(typescript@5.4.4) + '@typescript-eslint/utils': 7.5.0(eslint@8.56.0)(typescript@5.4.4) '@typescript-eslint/visitor-keys': 7.5.0 debug: 4.3.4(supports-color@5.5.0) - eslint: 8.57.0 + eslint: 8.56.0 graphemer: 1.4.0 ignore: 5.3.1 natural-compare: 1.4.0 @@ -10072,7 +10031,7 @@ packages: - supports-color dev: false - /@typescript-eslint/parser@7.5.0(eslint@8.57.0)(typescript@5.4.4): + /@typescript-eslint/parser@7.5.0(eslint@8.56.0)(typescript@5.4.4): resolution: {integrity: sha512-cj+XGhNujfD2/wzR1tabNsidnYRaFfEkcULdcIyVBYcXjBvBKOes+mpMBP7hMpOyk+gBcfXsrg4NBGAStQyxjQ==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: @@ -10087,7 +10046,7 @@ packages: '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4) '@typescript-eslint/visitor-keys': 7.5.0 debug: 4.3.4(supports-color@5.5.0) - eslint: 8.57.0 + eslint: 8.56.0 typescript: 5.4.4 transitivePeerDependencies: - supports-color @@ -10115,7 +10074,7 @@ packages: '@typescript-eslint/types': 7.5.0 '@typescript-eslint/visitor-keys': 7.5.0 - /@typescript-eslint/type-utils@7.5.0(eslint@8.57.0)(typescript@5.4.4): + /@typescript-eslint/type-utils@7.5.0(eslint@8.56.0)(typescript@5.4.4): resolution: {integrity: sha512-A021Rj33+G8mx2Dqh0nMO9GyjjIBK3MqgVgZ2qlKf6CJy51wY/lkkFqq3TqqnH34XyAHUkq27IjlUkWlQRpLHw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: @@ -10126,9 +10085,9 @@ packages: optional: true dependencies: '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4) - '@typescript-eslint/utils': 7.5.0(eslint@8.57.0)(typescript@5.4.4) + '@typescript-eslint/utils': 7.5.0(eslint@8.56.0)(typescript@5.4.4) debug: 4.3.4(supports-color@5.5.0) - eslint: 8.57.0 + eslint: 8.56.0 ts-api-utils: 1.3.0(typescript@5.4.4) typescript: 5.4.4 transitivePeerDependencies: @@ -10213,19 +10172,19 @@ packages: transitivePeerDependencies: - supports-color - /@typescript-eslint/utils@5.62.0(eslint@8.57.0)(typescript@5.4.4): + /@typescript-eslint/utils@5.62.0(eslint@8.56.0)(typescript@5.4.4): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.7 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.4.4) - eslint: 8.57.0 + eslint: 8.56.0 eslint-scope: 5.1.1 semver: 7.6.0 transitivePeerDependencies: @@ -10233,38 +10192,38 @@ packages: - typescript dev: true - /@typescript-eslint/utils@6.17.0(eslint@8.57.0)(typescript@5.4.4): + /@typescript-eslint/utils@6.17.0(eslint@8.56.0)(typescript@5.4.4): resolution: {integrity: sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==} engines: {node: ^16.0.0 || >=18.0.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.7 '@typescript-eslint/scope-manager': 6.17.0 '@typescript-eslint/types': 6.17.0 '@typescript-eslint/typescript-estree': 6.17.0(typescript@5.4.4) - eslint: 8.57.0 + eslint: 8.56.0 semver: 7.6.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@7.5.0(eslint@8.57.0)(typescript@5.4.4): + /@typescript-eslint/utils@7.5.0(eslint@8.56.0)(typescript@5.4.4): resolution: {integrity: sha512-3vZl9u0R+/FLQcpy2EHyRGNqAS/ofJ3Ji8aebilfJe+fobK8+LbIFmrHciLVDxjDoONmufDcnVSF38KwMEOjzw==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) '@types/json-schema': 7.0.15 '@types/semver': 7.5.8 '@typescript-eslint/scope-manager': 7.5.0 '@typescript-eslint/types': 7.5.0 '@typescript-eslint/typescript-estree': 7.5.0(typescript@5.4.4) - eslint: 8.57.0 + eslint: 8.56.0 semver: 7.6.0 transitivePeerDependencies: - supports-color @@ -10381,7 +10340,7 @@ packages: /@vue/compiler-core@3.4.19: resolution: {integrity: sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==} dependencies: - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@vue/shared': 3.4.19 entities: 4.5.0 estree-walker: 2.0.2 @@ -10398,7 +10357,7 @@ packages: /@vue/compiler-sfc@3.4.19: resolution: {integrity: sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==} dependencies: - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@vue/compiler-core': 3.4.19 '@vue/compiler-dom': 3.4.19 '@vue/compiler-ssr': 3.4.19 @@ -11342,6 +11301,16 @@ packages: resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==} dev: true + /axios@1.6.7: + resolution: {integrity: sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==} + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + dev: false + /axios@1.6.8: resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} dependencies: @@ -11405,17 +11374,17 @@ packages: - supports-color dev: true - /babel-plugin-styled-components@2.1.4(@babel/core@7.24.5)(styled-components@5.3.11): + /babel-plugin-styled-components@2.1.4(@babel/core@7.24.4)(styled-components@5.3.11): resolution: {integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==} peerDependencies: styled-components: '>= 2' dependencies: '@babel/helper-annotate-as-pure': 7.22.5 '@babel/helper-module-imports': 7.22.15 - '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.5) + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.4) lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) + styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0) transitivePeerDependencies: - '@babel/core' dev: true @@ -11546,6 +11515,18 @@ packages: sha256-uint8array: 0.10.7 dev: false + /bitcoinjs-lib@6.1.3: + resolution: {integrity: sha512-TYXs/Qf+GNk2nnsB9HrXWqzFuEgCg0Gx+v3UW3B8VuceFHXVvhT+7hRnTSvwkX0i8rz2rtujeU6gFaDcFqYFDw==} + engines: {node: '>=8.0.0'} + dependencies: + '@noble/hashes': 1.3.2 + bech32: 2.0.0 + bip174: 2.1.1 + bs58check: 3.0.1 + typeforce: 1.18.0 + varuint-bitcoin: 1.1.2 + dev: false + /bitcoinjs-lib@6.1.5: resolution: {integrity: sha512-yuf6xs9QX/E8LWE2aMJPNd0IxGofwfuVOiYdNUESkc+2bHHVKjhJd8qewqapeoolh9fihzHGoDCB5Vkr57RZCQ==} engines: {node: '>=8.0.0'} @@ -12216,7 +12197,7 @@ packages: peerDependencies: '@stacks/transactions': '*' dependencies: - '@stacks/stacks-blockchain-api-types': 7.10.0 + '@stacks/stacks-blockchain-api-types': 7.8.2 '@stacks/transactions': 6.9.0 axios: 1.6.8 lodash: 4.17.21 @@ -14297,7 +14278,7 @@ packages: source-map: 0.6.1 dev: true - /eslint-mdx@3.1.5(eslint@8.57.0): + /eslint-mdx@3.1.5(eslint@8.56.0): resolution: {integrity: sha512-ynztX0k7CQ3iDL7fDEIeg3g0O/d6QPv7IBI9fdYLhXp5fAp0fi8X22xF/D3+Pk0f90R27uwqa1clHpay6t0l8Q==} engines: {node: '>=18.0.0'} peerDependencies: @@ -14305,7 +14286,7 @@ packages: dependencies: acorn: 8.11.3 acorn-jsx: 5.3.2(acorn@8.11.3) - eslint: 8.57.0 + eslint: 8.56.0 espree: 9.6.1 estree-util-visit: 2.0.0 remark-mdx: 3.0.0 @@ -14322,14 +14303,14 @@ packages: - supports-color dev: true - /eslint-plugin-deprecation@2.0.0(eslint@8.57.0)(typescript@5.4.4): + /eslint-plugin-deprecation@2.0.0(eslint@8.56.0)(typescript@5.4.4): resolution: {integrity: sha512-OAm9Ohzbj11/ZFyICyR5N6LbOIvQMp7ZU2zI7Ej0jIc8kiGUERXPNMfw2QqqHD1ZHtjMub3yPZILovYEYucgoQ==} peerDependencies: eslint: ^7.0.0 || ^8.0.0 typescript: ^4.2.4 || ^5.0.0 dependencies: - '@typescript-eslint/utils': 6.17.0(eslint@8.57.0)(typescript@5.4.4) - eslint: 8.57.0 + '@typescript-eslint/utils': 6.17.0(eslint@8.56.0)(typescript@5.4.4) + eslint: 8.56.0 tslib: 2.6.2 tsutils: 3.21.0(typescript@5.4.4) typescript: 5.4.4 @@ -14337,27 +14318,27 @@ packages: - supports-color dev: true - /eslint-plugin-markdown@3.0.1(eslint@8.57.0): + /eslint-plugin-markdown@3.0.1(eslint@8.56.0): resolution: {integrity: sha512-8rqoc148DWdGdmYF6WSQFT3uQ6PO7zXYgeBpHAOAakX/zpq+NvFYbDA/H7PYzHajwtmaOzAwfxyl++x0g1/N9A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.57.0 + eslint: 8.56.0 mdast-util-from-markdown: 0.8.5 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-mdx@3.1.5(eslint@8.57.0): + /eslint-plugin-mdx@3.1.5(eslint@8.56.0): resolution: {integrity: sha512-lUE7tP7IrIRHU3gTtASDe5u4YM2SvQveYVJfuo82yn3MLh/B/v05FNySURCK4aIxIYF1QYo3IRemQG/lyQzpAg==} engines: {node: '>=18.0.0'} peerDependencies: eslint: '>=8.0.0' dependencies: - eslint: 8.57.0 - eslint-mdx: 3.1.5(eslint@8.57.0) - eslint-plugin-markdown: 3.0.1(eslint@8.57.0) + eslint: 8.56.0 + eslint-mdx: 3.1.5(eslint@8.56.0) + eslint-plugin-markdown: 3.0.1(eslint@8.56.0) remark-mdx: 3.0.0 remark-parse: 11.0.0 remark-stringify: 11.0.0 @@ -14384,16 +14365,16 @@ packages: eslint: 8.56.0 dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.57.0): + /eslint-plugin-react-hooks@4.6.0(eslint@8.56.0): resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 dependencies: - eslint: 8.57.0 + eslint: 8.56.0 dev: true - /eslint-plugin-react@7.34.1(eslint@8.57.0): + /eslint-plugin-react@7.34.1(eslint@8.56.0): resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} engines: {node: '>=4'} peerDependencies: @@ -14406,7 +14387,7 @@ packages: array.prototype.tosorted: 1.1.3 doctrine: 2.1.0 es-iterator-helpers: 1.0.18 - eslint: 8.57.0 + eslint: 8.56.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 @@ -14420,15 +14401,15 @@ packages: string.prototype.matchall: 4.0.11 dev: true - /eslint-plugin-storybook@0.8.0(eslint@8.57.0)(typescript@5.4.4): + /eslint-plugin-storybook@0.8.0(eslint@8.56.0)(typescript@5.4.4): resolution: {integrity: sha512-CZeVO5EzmPY7qghO2t64oaFM+8FTaD4uzOEjHKp516exyTKo+skKAL9GI3QALS2BXhyALJjNtwbmr1XinGE8bA==} engines: {node: '>= 18'} peerDependencies: eslint: '>=6' dependencies: '@storybook/csf': 0.0.1 - '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.4.4) - eslint: 8.57.0 + '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@5.4.4) + eslint: 8.56.0 requireindex: 1.2.0 ts-dedent: 2.2.0 transitivePeerDependencies: @@ -14545,53 +14526,6 @@ packages: text-table: 0.2.0 transitivePeerDependencies: - supports-color - dev: true - - /eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - hasBin: true - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@eslint-community/regexpp': 4.10.0 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.57.0 - '@humanwhocodes/config-array': 0.11.14 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@5.5.0) - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.5.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.1 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.0 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color /esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} @@ -15090,7 +15024,7 @@ packages: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} dev: true - /fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.0)(typescript@5.4.4)(webpack@5.91.0): + /fork-ts-checker-webpack-plugin@6.5.3(eslint@8.56.0)(typescript@5.4.4)(webpack@5.91.0): resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} engines: {node: '>=10', yarn: '>=1.0.0'} peerDependencies: @@ -15110,7 +15044,7 @@ packages: chokidar: 3.6.0 cosmiconfig: 6.0.0 deepmerge: 4.3.1 - eslint: 8.57.0 + eslint: 8.56.0 fs-extra: 9.1.0 glob: 7.2.3 memfs: 3.6.0 @@ -16252,10 +16186,6 @@ packages: resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} dev: true - /immutable@4.3.5: - resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==} - dev: true - /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -17082,7 +17012,7 @@ packages: optional: true dependencies: '@babel/core': 7.24.0 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.4 '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.0) '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.0) '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.0) @@ -19378,18 +19308,6 @@ packages: levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 - dev: true - - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 /ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} @@ -20854,7 +20772,7 @@ packages: tween-functions: 1.2.0 dev: true - /react-dev-utils@12.0.1(eslint@8.57.0)(typescript@5.4.4)(webpack@5.91.0): + /react-dev-utils@12.0.1(eslint@8.56.0)(typescript@5.4.4)(webpack@5.91.0): resolution: {integrity: sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==} engines: {node: '>=14'} peerDependencies: @@ -20873,7 +20791,7 @@ packages: escape-string-regexp: 4.0.0 filesize: 8.0.7 find-up: 5.0.0 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.0)(typescript@5.4.4)(webpack@5.91.0) + fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.56.0)(typescript@5.4.4)(webpack@5.91.0) global-modules: 2.0.0 globby: 11.1.0 gzip-size: 6.0.0 @@ -22286,7 +22204,7 @@ packages: resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} engines: {node: '>=10.0.0'} dependencies: - '@socket.io/component-emitter': 3.1.0 + '@socket.io/component-emitter': 3.1.2 debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -22839,7 +22757,7 @@ packages: inline-style-parser: 0.2.2 dev: true - /styled-components@5.3.11(@babel/core@7.24.5)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): + /styled-components@5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.0): resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} engines: {node: '>=10'} peerDependencies: @@ -22852,7 +22770,7 @@ packages: '@emotion/is-prop-valid': 1.2.1 '@emotion/stylis': 0.8.5 '@emotion/unitless': 0.7.5 - babel-plugin-styled-components: 2.1.4(@babel/core@7.24.5)(styled-components@5.3.11) + babel-plugin-styled-components: 2.1.4(@babel/core@7.24.4)(styled-components@5.3.11) css-to-react-native: 3.2.0 hoist-non-react-statics: 3.3.2 react: 18.2.0 @@ -24731,10 +24649,6 @@ packages: resolution: {integrity: sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==} dev: true - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - /wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: true diff --git a/src/app/app.tsx b/src/app/app.tsx index 528b162aab0..4ac6a2d8277 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -1,9 +1,13 @@ import { Suspense } from 'react'; import { Provider as ReduxProvider } from 'react-redux'; +import { LeatherQueryProvider } from '@leather-wallet/query'; import { QueryClientProvider } from '@tanstack/react-query'; import { PersistGate } from 'redux-persist/integration/react'; +import { GITHUB_ORG, GITHUB_REPO } from '@shared/constants'; +import { BRANCH_NAME, WALLET_ENVIRONMENT } from '@shared/environment'; + import { queryClient } from '@app/common/persistence'; import { ThemeSwitcherProvider } from '@app/common/theme-provider'; import { FullPageLoadingSpinner } from '@app/components/loading-spinner'; @@ -14,10 +18,40 @@ import { ToastsProvider } from '@app/features/toasts/toasts-provider'; import { AppRoutes } from '@app/routes/app-routes'; import { persistor, store } from '@app/store'; +import localConfig from '../../config/wallet-config.json'; import './index.css'; +import { useCurrentNetwork } from './store/networks/networks.selectors'; const reactQueryDevToolsEnabled = process.env.REACT_QUERY_DEVTOOLS_ENABLED === 'true'; +function ConnectedApp() { + const network = useCurrentNetwork(); + return ( + + + }> + + + + {reactQueryDevToolsEnabled && } + + + + ); +} + export function App() { return ( @@ -25,14 +59,7 @@ export function App() { - - }> - - - - {reactQueryDevToolsEnabled && } - - + diff --git a/src/app/common/hooks/balance/use-total-balance.tsx b/src/app/common/hooks/balance/use-total-balance.tsx index 60031645bf9..2cbd6fa2cab 100644 --- a/src/app/common/hooks/balance/use-total-balance.tsx +++ b/src/app/common/hooks/balance/use-total-balance.tsx @@ -1,11 +1,12 @@ import { useMemo } from 'react'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; + import { createMoney } from '@shared/models/money.model'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; interface UseTotalBalanceArgs { diff --git a/src/app/common/hooks/use-bitcoin-contracts.ts b/src/app/common/hooks/use-bitcoin-contracts.ts index 5a75f6bd570..4cb0f50aaa4 100644 --- a/src/app/common/hooks/use-bitcoin-contracts.ts +++ b/src/app/common/hooks/use-bitcoin-contracts.ts @@ -2,6 +2,11 @@ import { useNavigate } from 'react-router-dom'; import { RpcErrorCode } from '@btckit/types'; import { JsDLCInterface } from '@dlc-link/dlc-tools'; +import { + sendAcceptedBitcoinContractOfferToProtocolWallet, + useCalculateBitcoinFiatValue, + useCryptoCurrencyMarketDataMeanAverage, +} from '@leather-wallet/query'; import { bytesToHex } from '@stacks/common'; import { @@ -13,11 +18,6 @@ import { RouteUrls } from '@shared/route-urls'; import { BitcoinContractResponseStatus } from '@shared/rpc/methods/accept-bitcoin-contract'; import { makeRpcErrorResponse, makeRpcSuccessResponse } from '@shared/rpc/rpc-methods'; -import { sendAcceptedBitcoinContractOfferToProtocolWallet } from '@app/query/bitcoin/contract/send-accepted-bitcoin-contract-offer'; -import { - useCalculateBitcoinFiatValue, - useCryptoCurrencyMarketDataMeanAverage, -} from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountIndex } from '@app/store/accounts/account'; import { useCurrentAccountNativeSegwitIndexZeroSigner, diff --git a/src/app/common/hooks/use-convert-to-fiat-amount.ts b/src/app/common/hooks/use-convert-to-fiat-amount.ts index 5d3cc82e6f3..82120dc5eea 100644 --- a/src/app/common/hooks/use-convert-to-fiat-amount.ts +++ b/src/app/common/hooks/use-convert-to-fiat-amount.ts @@ -1,14 +1,17 @@ import { useCallback } from 'react'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; + import { CryptoCurrencies } from '@shared/models/currencies.model'; import { type Money } from '@shared/models/money.model'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; - import { baseCurrencyAmountInQuote } from '../money/calculate-money'; export function useConvertCryptoCurrencyToFiatAmount(currency: CryptoCurrencies) { - const cryptoCurrencyMarketData = useCryptoCurrencyMarketDataMeanAverage(currency); + // TODO: unsafe type assumption + const cryptoCurrencyMarketData = useCryptoCurrencyMarketDataMeanAverage( + currency as 'BTC' | 'STX' + ); return useCallback( (value: Money) => baseCurrencyAmountInQuote(value, cryptoCurrencyMarketData), diff --git a/src/app/common/transactions/bitcoin/coinselect/local-coin-selection.ts b/src/app/common/transactions/bitcoin/coinselect/local-coin-selection.ts index 0f6ae5349fe..03bb507cca9 100644 --- a/src/app/common/transactions/bitcoin/coinselect/local-coin-selection.ts +++ b/src/app/common/transactions/bitcoin/coinselect/local-coin-selection.ts @@ -1,3 +1,4 @@ +import type { UtxoResponseItem } from '@leather-wallet/query'; import BigNumber from 'bignumber.js'; import { validate } from 'bitcoin-address-validation'; @@ -5,7 +6,6 @@ import type { TransferRecipient } from '@shared/models/form.model'; import { sumNumbers } from '@app/common/math/helpers'; import { sumMoney } from '@app/common/money/calculate-money'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { filterUneconomicalUtxos, getSizeInfo } from '../utils'; diff --git a/src/app/common/transactions/bitcoin/fees/bitcoin-fees.spec.ts b/src/app/common/transactions/bitcoin/fees/bitcoin-fees.spec.ts index 4420164cbaa..d0a6584e8d8 100644 --- a/src/app/common/transactions/bitcoin/fees/bitcoin-fees.spec.ts +++ b/src/app/common/transactions/bitcoin/fees/bitcoin-fees.spec.ts @@ -1,10 +1,9 @@ +import type { UtxoResponseItem } from '@leather-wallet/query'; import BigNumber from 'bignumber.js'; import { sha256 } from 'bitcoinjs-lib/src/crypto'; import { createMoney } from '@shared/models/money.model'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; - import { filterUneconomicalUtxos } from '../utils'; import { calculateMaxBitcoinSpend } from './calculate-max-bitcoin-spend'; diff --git a/src/app/common/transactions/bitcoin/fees/calculate-max-bitcoin-spend.ts b/src/app/common/transactions/bitcoin/fees/calculate-max-bitcoin-spend.ts index fc714572184..713320d9f8f 100644 --- a/src/app/common/transactions/bitcoin/fees/calculate-max-bitcoin-spend.ts +++ b/src/app/common/transactions/bitcoin/fees/calculate-max-bitcoin-spend.ts @@ -1,10 +1,10 @@ +import type { UtxoResponseItem } from '@leather-wallet/query'; import BigNumber from 'bignumber.js'; import { AverageBitcoinFeeRates } from '@shared/models/fees/bitcoin-fees.model'; import { createMoney } from '@shared/models/money.model'; import { satToBtc } from '@app/common/money/unit-conversion'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { filterUneconomicalUtxos, getSpendableAmount } from '../utils'; diff --git a/src/app/common/transactions/bitcoin/use-generate-bitcoin-tx.ts b/src/app/common/transactions/bitcoin/use-generate-bitcoin-tx.ts index a22ce17a555..ac3f9a27808 100644 --- a/src/app/common/transactions/bitcoin/use-generate-bitcoin-tx.ts +++ b/src/app/common/transactions/bitcoin/use-generate-bitcoin-tx.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react'; +import type { UtxoResponseItem } from '@leather-wallet/query'; import * as btc from '@scure/btc-signer'; import { logger } from '@shared/logger'; @@ -10,7 +11,6 @@ import { determineUtxosForSpend, determineUtxosForSpendAll, } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { useBitcoinScureLibNetworkConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin-keychain'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; diff --git a/src/app/common/transactions/bitcoin/utils.ts b/src/app/common/transactions/bitcoin/utils.ts index 8fe55aa93d1..85278ffa110 100644 --- a/src/app/common/transactions/bitcoin/utils.ts +++ b/src/app/common/transactions/bitcoin/utils.ts @@ -1,3 +1,4 @@ +import type { UtxoResponseItem } from '@leather-wallet/query'; import BigNumber from 'bignumber.js'; import { type AddressInfo, @@ -15,7 +16,6 @@ import { import { sumNumbers } from '@app/common/math/helpers'; import { satToBtc } from '@app/common/money/unit-conversion'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; import { BtcSizeFeeEstimator } from './fees/btc-size-fee-estimator'; diff --git a/src/app/common/utils.ts b/src/app/common/utils.ts index 9f9e31198dc..0f612c31de3 100644 --- a/src/app/common/utils.ts +++ b/src/app/common/utils.ts @@ -15,14 +15,6 @@ export function createNullArrayOfLength(length: number) { return new Array(length).fill(null); } -export function createNumArrayOfRange(fromIndex: number, toIndex: number) { - const result = []; - for (let i = fromIndex; i <= toIndex; i++) { - result.push(i); - } - return result; -} - function kebabCase(str: string) { return str.replace(KEBAB_REGEX, match => '-' + match.toLowerCase()); } @@ -299,14 +291,6 @@ export function capitalize(val: string) { return val.charAt(0).toUpperCase() + val.slice(1); } -export function isFulfilled(p: PromiseSettledResult): p is PromiseFulfilledResult { - return p.status === 'fulfilled'; -} - -export function isRejected(p: PromiseSettledResult): p is PromiseRejectedResult { - return p.status === 'rejected'; -} - interface LinearInterpolation { start: number; end: number; diff --git a/src/app/common/validation/forms/amount-validators.ts b/src/app/common/validation/forms/amount-validators.ts index c062a47cac6..1b50085c6c3 100644 --- a/src/app/common/validation/forms/amount-validators.ts +++ b/src/app/common/validation/forms/amount-validators.ts @@ -1,3 +1,4 @@ +import type { UtxoResponseItem } from '@leather-wallet/query'; import BigNumber from 'bignumber.js'; import * as yup from 'yup'; @@ -13,7 +14,6 @@ import { satToBtc, stxToMicroStx, } from '@app/common/money/unit-conversion'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { FormErrorMessages } from '../../../../shared/error-messages'; import { formatInsufficientBalanceError, formatPrecisionError } from '../../error-formatters'; diff --git a/src/app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee.tsx b/src/app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee.tsx index 3bcf528f288..b097c90c307 100644 --- a/src/app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee.tsx +++ b/src/app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee.tsx @@ -1,5 +1,7 @@ import { useCallback } from 'react'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; + import type { TransferRecipient } from '@shared/models/form.model'; import { Money, createMoney } from '@shared/models/money.model'; @@ -11,7 +13,6 @@ import { } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { useCurrentBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; export const MAX_FEE_RATE_MULTIPLIER = 50; diff --git a/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list-multiple-recipients.ts b/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list-multiple-recipients.ts index 06891d2ce02..7e4e2508120 100644 --- a/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list-multiple-recipients.ts +++ b/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list-multiple-recipients.ts @@ -1,5 +1,11 @@ import { useMemo } from 'react'; +import { + type UtxoResponseItem, + useAverageBitcoinFeeRates, + useCryptoCurrencyMarketDataMeanAverage, +} from '@leather-wallet/query'; + import { BtcFeeType, btcTxTimeMap } from '@shared/models/fees/bitcoin-fees.model'; import type { TransferRecipient } from '@shared/models/form.model'; import { Money, createMoney } from '@shared/models/money.model'; @@ -11,9 +17,6 @@ import { determineUtxosForSpend, determineUtxosForSpendAll, } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; -import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { FeesListItem } from './bitcoin-fees-list'; diff --git a/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts b/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts index 3cd9b45741e..43e9987461d 100644 --- a/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts +++ b/src/app/components/bitcoin-fees-list/use-bitcoin-fees-list.ts @@ -1,5 +1,11 @@ import { useMemo } from 'react'; +import { + type UtxoResponseItem, + useAverageBitcoinFeeRates, + useCryptoCurrencyMarketDataMeanAverage, +} from '@leather-wallet/query'; + import { BtcFeeType, btcTxTimeMap } from '@shared/models/fees/bitcoin-fees.model'; import { Money, createMoney } from '@shared/models/money.model'; @@ -11,9 +17,6 @@ import { determineUtxosForSpendAll, } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; import { useCurrentBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; -import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { FeesListItem } from './bitcoin-fees-list'; diff --git a/src/app/components/bitcoin-transaction-item/bitcoin-transaction-inscription-icon.tsx b/src/app/components/bitcoin-transaction-item/bitcoin-transaction-inscription-icon.tsx index b3ce5a376bb..5cd1ace91e7 100644 --- a/src/app/components/bitcoin-transaction-item/bitcoin-transaction-inscription-icon.tsx +++ b/src/app/components/bitcoin-transaction-item/bitcoin-transaction-inscription-icon.tsx @@ -1,11 +1,10 @@ +import type { Inscription } from '@leather-wallet/models'; import { Circle } from 'leather-styles/jsx'; -import { SupportedInscription } from '@shared/models/inscription.model'; - import { OrdinalAvatarIcon } from '@app/ui/components/avatar/ordinal-avatar-icon'; -export function InscriptionIcon({ inscription, ...rest }: { inscription: SupportedInscription }) { - switch (inscription.type) { +export function InscriptionIcon({ inscription, ...rest }: { inscription: Inscription }) { + switch (inscription.mimeType) { case 'image': return ( { void analytics.track('view_bitcoin_transaction'); if (inscriptionData) { - openInNewTab(createInscriptionInfoUrl(inscriptionData.id)); + openInNewTab(inscriptionData.id); return; } handleOpenTxLink({ txid: transaction?.txid || '' }); @@ -79,7 +69,7 @@ export function BitcoinTransactionItem({ transaction }: BitcoinTransactionItemPr {caption} - {inscriptionData ? {inscriptionData.mime_type} : null} + {inscriptionData ? {inscriptionData.mimeType} : null} ); diff --git a/src/app/components/inscription-preview-card/components/inscription-preview.tsx b/src/app/components/inscription-preview-card/components/inscription-preview.tsx index a009524afc6..79220f6b199 100644 --- a/src/app/components/inscription-preview-card/components/inscription-preview.tsx +++ b/src/app/components/inscription-preview-card/components/inscription-preview.tsx @@ -1,7 +1,6 @@ +import type { Inscription } from '@leather-wallet/models'; import { BoxProps, Flex } from 'leather-styles/jsx'; -import { SupportedInscription } from '@shared/models/inscription.model'; - import { OrdinalAvatarIcon } from '@app/ui/components/avatar/ordinal-avatar-icon'; import { InscriptionImage } from './inscription-image'; @@ -9,10 +8,10 @@ import { InscriptionPreviewContainer } from './inscription-preview-container'; import { InscriptionText } from './inscription-text'; interface InscriptionPreviewProps extends BoxProps { - inscription: SupportedInscription; + inscription: Inscription; } export function InscriptionPreview({ inscription, ...props }: InscriptionPreviewProps) { - switch (inscription.type) { + switch (inscription.mimeType) { case 'image': { return ( @@ -23,7 +22,7 @@ export function InscriptionPreview({ inscription, ...props }: InscriptionPreview case 'text': { return ( - + ); } diff --git a/src/app/components/inscription-preview-card/components/inscription-text.tsx b/src/app/components/inscription-preview-card/components/inscription-text.tsx index a9f657cee47..b5ebeedad35 100644 --- a/src/app/components/inscription-preview-card/components/inscription-text.tsx +++ b/src/app/components/inscription-preview-card/components/inscription-text.tsx @@ -1,9 +1,9 @@ +import { useInscriptionTextContentQuery } from '@leather-wallet/query'; import { sanitize } from 'dompurify'; import { Box } from 'leather-styles/jsx'; import { parseJson } from '@app/components/json'; import { LoadingSpinner } from '@app/components/loading-spinner'; -import { useInscriptionTextContentQuery } from '@app/query/bitcoin/ordinals/inscription-text-content.query'; interface InscriptionTextProps { contentSrc: string; diff --git a/src/app/components/loaders/runes-loader.tsx b/src/app/components/loaders/runes-loader.tsx index 9b4eb8ae036..97ec54b439b 100644 --- a/src/app/components/loaders/runes-loader.tsx +++ b/src/app/components/loaders/runes-loader.tsx @@ -1,6 +1,5 @@ import type { CryptoAssetBalance, RuneCryptoAssetInfo } from '@leather-wallet/models'; - -import { useRuneTokens } from '@app/query/bitcoin/runes/runes.hooks'; +import { useRuneTokens } from '@leather-wallet/query'; interface RunesLoaderProps { addresses: string[]; diff --git a/src/app/components/loaders/src20-tokens-loader.tsx b/src/app/components/loaders/src20-tokens-loader.tsx index 47f568f0089..4633e6be09f 100644 --- a/src/app/components/loaders/src20-tokens-loader.tsx +++ b/src/app/components/loaders/src20-tokens-loader.tsx @@ -1,6 +1,5 @@ import type { CryptoAssetBalance, Src20CryptoAssetInfo } from '@leather-wallet/models'; - -import { useSrc20TokensByAddress } from '@app/query/bitcoin/stamps/stamps-by-address.hooks'; +import { useSrc20TokensByAddress } from '@leather-wallet/query'; export interface Src20TokenAssetDetails { balance: CryptoAssetBalance; diff --git a/src/app/features/activity-list/activity-list.tsx b/src/app/features/activity-list/activity-list.tsx index 46d8f62b865..8f4367d4830 100644 --- a/src/app/features/activity-list/activity-list.tsx +++ b/src/app/features/activity-list/activity-list.tsx @@ -1,11 +1,13 @@ import { useMemo } from 'react'; import { Outlet } from 'react-router-dom'; +import { + useBitcoinPendingTransactions, + useGetBitcoinTransactionsByAddressesQuery, +} from '@leather-wallet/query'; import uniqby from 'lodash.uniqby'; import { LoadingSpinner } from '@app/components/loading-spinner'; -import { useBitcoinPendingTransactions } from '@app/query/bitcoin/address/transactions-by-address.hooks'; -import { useGetBitcoinTransactionsByAddressesQuery } from '@app/query/bitcoin/address/transactions-by-address.query'; 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'; diff --git a/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx index c3b2e25dc60..9dbb92e5b11 100644 --- a/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx +++ b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx @@ -1,9 +1,9 @@ import type { BtcCryptoAssetBalance } from '@leather-wallet/models'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; interface BtcCryptoAssetItemProps { diff --git a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx index 1473ad0cf0d..130cd842d6f 100644 --- a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx +++ b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx @@ -1,10 +1,10 @@ import type { StxCryptoAssetBalance } from '@leather-wallet/models'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; import { styled } from 'leather-styles/jsx'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { formatMoneyWithoutSymbol, i18nFormatCurrency } from '@app/common/money/format-money'; import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; import { Caption } from '@app/ui/components/typography/caption'; diff --git a/src/app/features/collectibles/components/bitcoin/inscription-text.tsx b/src/app/features/collectibles/components/bitcoin/inscription-text.tsx index af99655db66..15fc469ba58 100644 --- a/src/app/features/collectibles/components/bitcoin/inscription-text.tsx +++ b/src/app/features/collectibles/components/bitcoin/inscription-text.tsx @@ -1,5 +1,6 @@ +import { useInscriptionTextContentQuery } from '@leather-wallet/query'; + import { parseJson } from '@app/components/json'; -import { useInscriptionTextContentQuery } from '@app/query/bitcoin/ordinals/inscription-text-content.query'; import { OrdinalAvatarIcon } from '@app/ui/components/avatar/ordinal-avatar-icon'; import { CollectibleText } from '../_collectible-types/collectible-text'; diff --git a/src/app/features/collectibles/components/bitcoin/inscription.tsx b/src/app/features/collectibles/components/bitcoin/inscription.tsx index d4f426f1a67..fd5db492fbc 100644 --- a/src/app/features/collectibles/components/bitcoin/inscription.tsx +++ b/src/app/features/collectibles/components/bitcoin/inscription.tsx @@ -1,11 +1,11 @@ import { useLocation, useNavigate } from 'react-router-dom'; +import { type Inscription } from '@leather-wallet/models'; + import { ORD_IO_URL } from '@shared/constants'; -import { Inscription as InscriptionType } from '@shared/models/inscription.model'; import { RouteUrls } from '@shared/route-urls'; import { openInNewTab } from '@app/common/utils/open-in-new-tab'; -import { convertInscriptionToSupportedInscriptionType } from '@app/query/bitcoin/ordinals/inscription.hooks'; import { OrdinalAvatarIcon } from '@app/ui/components/avatar/ordinal-avatar-icon'; import { CollectibleAudio } from '../_collectible-types/collectible-audio'; @@ -15,15 +15,14 @@ import { CollectibleOther } from '../_collectible-types/collectible-other'; import { InscriptionText } from './inscription-text'; interface InscriptionProps { - rawInscription: InscriptionType; + inscription: Inscription; } function openInscriptionUrl(num: number) { return openInNewTab(`${ORD_IO_URL}/${num}`); } -export function Inscription({ rawInscription }: InscriptionProps) { - const inscription = convertInscriptionToSupportedInscriptionType(rawInscription); +export function Inscription({ inscription }: InscriptionProps) { const navigate = useNavigate(); const location = useLocation(); @@ -33,7 +32,7 @@ export function Inscription({ rawInscription }: InscriptionProps) { }); } - switch (inscription.type) { + switch (inscription.mimeType) { case 'audio': return ( openInscriptionUrl(inscription.number)} onClickSend={() => openSendInscriptionModal()} diff --git a/src/app/features/collectibles/components/bitcoin/ordinals.tsx b/src/app/features/collectibles/components/bitcoin/ordinals.tsx index 427b6d48df1..213e7c2baec 100644 --- a/src/app/features/collectibles/components/bitcoin/ordinals.tsx +++ b/src/app/features/collectibles/components/bitcoin/ordinals.tsx @@ -1,10 +1,12 @@ import { useEffect } from 'react'; import { useInView } from 'react-intersection-observer'; +import { useInscriptions } from '@leather-wallet/query'; import { Box } from 'leather-styles/jsx'; import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; -import { useGetInscriptionsInfiniteQuery } from '@app/query/bitcoin/ordinals/inscriptions.query'; +import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentTaprootAccount } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; import { Inscription } from './inscription'; @@ -12,7 +14,13 @@ interface OrdinalsProps { setIsLoadingMore(isLoading: boolean): void; } export function Ordinals({ setIsLoadingMore }: OrdinalsProps) { - const query = useGetInscriptionsInfiniteQuery(); + const account = useCurrentTaprootAccount(); + const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); + + const query = useInscriptions({ + taprootKeychain: account?.keychain, + nativeSegwitAddress: nativeSegwitSigner.address, + }); const pages = query.data?.pages; const analytics = useAnalytics(); const { ref: intersectionSentinel, inView } = useInView({ @@ -53,7 +61,7 @@ export function Ordinals({ setIsLoadingMore }: OrdinalsProps) { <> {pages.map(page => page.inscriptions.map(inscription => ( - + )) )} diff --git a/src/app/features/collectibles/components/bitcoin/stamp.tsx b/src/app/features/collectibles/components/bitcoin/stamp.tsx index aca13fe961b..758aaca3980 100644 --- a/src/app/features/collectibles/components/bitcoin/stamp.tsx +++ b/src/app/features/collectibles/components/bitcoin/stamp.tsx @@ -1,5 +1,6 @@ +import { Stamp as BitcoinStamp } from '@leather-wallet/query'; + import { openInNewTab } from '@app/common/utils/open-in-new-tab'; -import { Stamp as BitcoinStamp } from '@app/query/bitcoin/stamps/stamps-by-address.query'; import { StampsAvatarIcon } from '@app/ui/components/avatar/stamps-avatar-icon'; import { CollectibleImage } from '../_collectible-types/collectible-image'; diff --git a/src/app/features/collectibles/components/bitcoin/stamps.tsx b/src/app/features/collectibles/components/bitcoin/stamps.tsx index 4b062dc1e20..2667249f1e8 100644 --- a/src/app/features/collectibles/components/bitcoin/stamps.tsx +++ b/src/app/features/collectibles/components/bitcoin/stamps.tsx @@ -1,7 +1,8 @@ import { useEffect } from 'react'; +import { useStampsByAddress } from '@leather-wallet/query'; + import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; -import { useStampsByAddress } from '@app/query/bitcoin/stamps/stamps-by-address.hooks'; import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { Stamp } from './stamp'; diff --git a/src/app/features/collectibles/components/taproot-balance-displayer.tsx b/src/app/features/collectibles/components/taproot-balance-displayer.tsx index 02bf8a181cf..941678f1af5 100644 --- a/src/app/features/collectibles/components/taproot-balance-displayer.tsx +++ b/src/app/features/collectibles/components/taproot-balance-displayer.tsx @@ -1,6 +1,10 @@ +import { useCurrentTaprootAccountBalance } from '@leather-wallet/query'; + import { formatMoney } from '@app/common/money/format-money'; -import { useCurrentTaprootAccountBalance } from '@app/query/bitcoin/balance/btc-balance-taproot.hooks'; import { useRecoverUninscribedTaprootUtxosFeatureEnabled } from '@app/query/common/remote-config/remote-config.query'; +import { useCurrentAccountIndex } from '@app/store/accounts/account'; +import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentTaprootAccount } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; import { Link } from '@app/ui/components/link/link'; import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; @@ -13,7 +17,15 @@ interface TaprootBalanceDisplayerProps { onSelectRetrieveBalance(): void; } export function TaprootBalanceDisplayer({ onSelectRetrieveBalance }: TaprootBalanceDisplayerProps) { - const balance = useCurrentTaprootAccountBalance(); + const currentAccountIndex = useCurrentAccountIndex(); + const account = useCurrentTaprootAccount(); + const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); + + const balance = useCurrentTaprootAccountBalance({ + currentAccountIndex, + taprootKeychain: account?.keychain, + nativeSegwitAddress: nativeSegwitSigner.address, + }); const isRecoverFeatureEnabled = useRecoverUninscribedTaprootUtxosFeatureEnabled(); if (!isRecoverFeatureEnabled) return null; if (balance.amount.isLessThanOrEqualTo(0)) return null; diff --git a/src/app/features/dialogs/increase-fee-dialog/hooks/use-btc-increase-fee.ts b/src/app/features/dialogs/increase-fee-dialog/hooks/use-btc-increase-fee.ts index 1505ee9f863..15de7e9d3ed 100644 --- a/src/app/features/dialogs/increase-fee-dialog/hooks/use-btc-increase-fee.ts +++ b/src/app/features/dialogs/increase-fee-dialog/hooks/use-btc-increase-fee.ts @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useBitcoinBroadcastTransaction } from '@leather-wallet/query'; import * as btc from '@scure/btc-signer'; import BigNumber from 'bignumber.js'; import * as yup from 'yup'; @@ -23,7 +24,6 @@ import { useBitcoinFeesList } from '@app/components/bitcoin-fees-list/use-bitcoi import { useToast } from '@app/features/toasts/use-toast'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; -import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; import { useBitcoinScureLibNetworkConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin-keychain'; import { useSignBitcoinTx } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; diff --git a/src/app/features/pending-brc-20-transfers/pending-brc-20-transfers.tsx b/src/app/features/pending-brc-20-transfers/pending-brc-20-transfers.tsx index 7602638ec3c..4874e301b28 100644 --- a/src/app/features/pending-brc-20-transfers/pending-brc-20-transfers.tsx +++ b/src/app/features/pending-brc-20-transfers/pending-brc-20-transfers.tsx @@ -1,5 +1,6 @@ import { useNavigate } from 'react-router-dom'; +import { fetchInscripionById, useOrdinalsbotClient } from '@leather-wallet/query'; import { Box, Flex, HStack, Stack } from 'leather-styles/jsx'; import { RouteUrls } from '@shared/route-urls'; @@ -10,9 +11,6 @@ import { StatusPending } from '@app/components/status-pending'; import { StatusReady } from '@app/components/status-ready'; import { useCurrentBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { useCheckOrderStatuses } from '@app/query/bitcoin/ordinals/brc20/use-check-order-status'; -import { fetchInscripionById } from '@app/query/bitcoin/ordinals/inscription-by-id.query'; -import { convertInscriptionToSupportedInscriptionType } from '@app/query/bitcoin/ordinals/inscription.hooks'; -import { useOrdinalsbotClient } from '@app/query/bitcoin/ordinalsbot-client'; import { OrdinalsbotInscriptionStatus, PendingBrc20Transfer, @@ -109,10 +107,7 @@ function PendingBrcTransfer({ order }: PendingBrcTransferProps) { ); navigate(RouteUrls.SendOrdinalInscription, { state: { - inscription: convertInscriptionToSupportedInscriptionType({ - ...inscription, - addressIndex: 0, - }), + inscription, }, }); } diff --git a/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-address-receive-totals.tsx b/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-address-receive-totals.tsx index 88d5ac0dea3..6b6b93403a3 100644 --- a/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-address-receive-totals.tsx +++ b/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-address-receive-totals.tsx @@ -1,7 +1,8 @@ +import { useCalculateBitcoinFiatValue } from '@leather-wallet/query'; + import { formatMoney, i18nFormatCurrency } from '@app/common/money/format-money'; import { removeMinusSign } from '@app/common/utils'; import { usePsbtSignerContext } from '@app/features/psbt-signer/psbt-signer.context'; -import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; import { PsbtAddressTotalItem } from './psbt-address-total-item'; diff --git a/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-address-transfer-totals.tsx b/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-address-transfer-totals.tsx index 66314965e76..205faff2e97 100644 --- a/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-address-transfer-totals.tsx +++ b/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-address-transfer-totals.tsx @@ -1,6 +1,7 @@ +import { useCalculateBitcoinFiatValue } from '@leather-wallet/query'; + import { formatMoney, i18nFormatCurrency } from '@app/common/money/format-money'; import { usePsbtSignerContext } from '@app/features/psbt-signer/psbt-signer.context'; -import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; import { PsbtAddressTotalItem } from './psbt-address-total-item'; diff --git a/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-inscription.tsx b/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-inscription.tsx index 0f97f8bb35c..5e800042a02 100644 --- a/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-inscription.tsx +++ b/src/app/features/psbt-signer/components/psbt-inputs-outputs-totals/components/psbt-inscription.tsx @@ -1,12 +1,10 @@ -import { Inscription } from '@shared/models/inscription.model'; +import type { Inscription } from '@leather-wallet/models'; +import { useInscription } from '@leather-wallet/query'; + import { isUndefined } from '@shared/utils'; import { openInNewTab } from '@app/common/utils/open-in-new-tab'; import { InscriptionPreview } from '@app/components/inscription-preview-card/components/inscription-preview'; -import { - createInscriptionInfoUrl, - useInscription, -} from '@app/query/bitcoin/ordinals/inscription.hooks'; import { OrdinalAvatarIcon } from '@app/ui/components/avatar/ordinal-avatar-icon'; import { PsbtAddressTotalItem } from './psbt-address-total-item'; @@ -32,7 +30,7 @@ export function PsbtInscription({ inscription }: PsbtInscriptionProps) { image={} title="Inscription" value={`#${inscription?.number}`} - valueAction={() => openInNewTab(createInscriptionInfoUrl(inscription?.id))} + valueAction={() => openInNewTab(inscription.preview)} /> ); } diff --git a/src/app/features/psbt-signer/components/psbt-request-fee.tsx b/src/app/features/psbt-signer/components/psbt-request-fee.tsx index ba81fc77ec2..43fecaed59a 100644 --- a/src/app/features/psbt-signer/components/psbt-request-fee.tsx +++ b/src/app/features/psbt-signer/components/psbt-request-fee.tsx @@ -1,9 +1,9 @@ +import { useCalculateBitcoinFiatValue } from '@leather-wallet/query'; import { HStack, Stack, styled } from 'leather-styles/jsx'; import { Money } from '@shared/models/money.model'; import { formatMoney, i18nFormatCurrency } from '@app/common/money/format-money'; -import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks'; import { PsbtRequestDetailsSectionLayout } from './psbt-request-details-section.layout'; diff --git a/src/app/features/psbt-signer/hooks/use-parsed-inputs.tsx b/src/app/features/psbt-signer/hooks/use-parsed-inputs.tsx index 5dab2f18d02..4f7b869cd5c 100644 --- a/src/app/features/psbt-signer/hooks/use-parsed-inputs.tsx +++ b/src/app/features/psbt-signer/hooks/use-parsed-inputs.tsx @@ -1,14 +1,14 @@ import { useMemo } from 'react'; +import type { Inscription } from '@leather-wallet/models'; +import { useInscriptionsByOutputs } from '@leather-wallet/query'; import * as btc from '@scure/btc-signer'; import { bytesToHex } from '@stacks/common'; import { getBtcSignerLibNetworkConfigByMode } from '@shared/crypto/bitcoin/bitcoin.network'; import { getBitcoinInputAddress, getBitcoinInputValue } from '@shared/crypto/bitcoin/bitcoin.utils'; -import { Inscription } from '@shared/models/inscription.model'; import { isDefined, isUndefined } from '@shared/utils'; -import { useGetInscriptionsByOutputQueries } from '@app/query/bitcoin/ordinals/inscriptions-by-param.query'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentAccountTaprootIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; @@ -32,9 +32,7 @@ export function useParsedInputs({ inputs, indexesToSign }: UseParsedInputsArgs) const bitcoinNetwork = getBtcSignerLibNetworkConfigByMode(network.chain.bitcoin.bitcoinNetwork); const bitcoinAddressNativeSegwit = useCurrentAccountNativeSegwitIndexZeroSigner().address; const { address: bitcoinAddressTaproot } = useCurrentAccountTaprootIndexZeroSigner(); - const inscriptions = useGetInscriptionsByOutputQueries(inputs).map( - query => query.data?.results[0] - ); + const inscriptions = useInscriptionsByOutputs(inputs); const signAll = isUndefined(indexesToSign); const psbtInputs = useMemo( diff --git a/src/app/features/psbt-signer/psbt-signer.context.ts b/src/app/features/psbt-signer/psbt-signer.context.ts index 5e58fd0c552..94a9e3283c5 100644 --- a/src/app/features/psbt-signer/psbt-signer.context.ts +++ b/src/app/features/psbt-signer/psbt-signer.context.ts @@ -1,6 +1,7 @@ import { createContext, useContext } from 'react'; -import { Inscription } from '@shared/models/inscription.model'; +import type { Inscription } from '@leather-wallet/models'; + import { Money } from '@shared/models/money.model'; import { PsbtInput } from './hooks/use-parsed-inputs'; diff --git a/src/app/features/retrieve-taproot-to-native-segwit/retrieve-taproot-to-native-segwit.tsx b/src/app/features/retrieve-taproot-to-native-segwit/retrieve-taproot-to-native-segwit.tsx index 05ba1136a5b..624b34dd7b5 100644 --- a/src/app/features/retrieve-taproot-to-native-segwit/retrieve-taproot-to-native-segwit.tsx +++ b/src/app/features/retrieve-taproot-to-native-segwit/retrieve-taproot-to-native-segwit.tsx @@ -1,5 +1,10 @@ import { useNavigate } from 'react-router-dom'; +import { + useBitcoinBroadcastTransaction, + useCurrentTaprootAccountBalance, + useCurrentTaprootAccountUninscribedUtxos, +} from '@leather-wallet/query'; import { Stack } from 'leather-styles/jsx'; import { RouteUrls } from '@shared/route-urls'; @@ -10,12 +15,12 @@ import { formatMoneyPadded } from '@app/common/money/format-money'; import { FormAddressDisplayer } from '@app/components/address-displayer/form-address-displayer'; import { InfoCardRow, InfoCardSeparator } from '@app/components/info-card/info-card'; import { useToast } from '@app/features/toasts/use-toast'; +import { useCurrentAccountIndex } from '@app/store/accounts/account'; import { - useCurrentTaprootAccountBalance, - useCurrentTaprootAccountUninscribedUtxos, -} from '@app/query/bitcoin/balance/btc-balance-taproot.hooks'; -import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; -import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; + useCurrentAccountNativeSegwitAddressIndexZero, + useCurrentAccountNativeSegwitIndexZeroSigner, +} from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentTaprootAccount } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; import { Link } from '@app/ui/components/link/link'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; @@ -25,9 +30,21 @@ import { useGenerateRetrieveTaprootFundsTx } from './use-generate-retrieve-tapro export function RetrieveTaprootToNativeSegwit() { const toast = useToast(); const navigate = useNavigate(); - const balance = useCurrentTaprootAccountBalance(); + + const currentAccountIndex = useCurrentAccountIndex(); + const account = useCurrentTaprootAccount(); + const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); + const balance = useCurrentTaprootAccountBalance({ + currentAccountIndex, + taprootKeychain: account?.keychain, + nativeSegwitAddress: nativeSegwitSigner.address, + }); const recipient = useCurrentAccountNativeSegwitAddressIndexZero(); - const uninscribedUtxos = useCurrentTaprootAccountUninscribedUtxos(); + const uninscribedUtxos = useCurrentTaprootAccountUninscribedUtxos({ + taprootKeychain: account?.keychain, + nativeSegwitAddress: nativeSegwitSigner.address, + currentAccountIndex, + }); const analytics = useAnalytics(); const { generateRetrieveTaprootFundsTx, fee } = useGenerateRetrieveTaprootFundsTx(); const { broadcastTx, isBroadcasting } = useBitcoinBroadcastTransaction(); diff --git a/src/app/features/retrieve-taproot-to-native-segwit/use-generate-retrieve-taproot-funds-tx.tsx b/src/app/features/retrieve-taproot-to-native-segwit/use-generate-retrieve-taproot-funds-tx.tsx index e3351b64d5a..4ba4084779d 100644 --- a/src/app/features/retrieve-taproot-to-native-segwit/use-generate-retrieve-taproot-funds-tx.tsx +++ b/src/app/features/retrieve-taproot-to-native-segwit/use-generate-retrieve-taproot-funds-tx.tsx @@ -1,5 +1,10 @@ import { useCallback, useMemo } from 'react'; +import { + useAverageBitcoinFeeRates, + useCurrentTaprootAccountUninscribedUtxos, + useNumberOfInscriptionsOnUtxo, +} from '@leather-wallet/query'; import * as btc from '@scure/btc-signer'; import { extractAddressIndexFromPath } from '@shared/crypto/bitcoin/bitcoin.utils'; @@ -7,18 +12,33 @@ import { Money, createMoney } from '@shared/models/money.model'; import { sumNumbers } from '@app/common/math/helpers'; import { BtcSizeFeeEstimator } from '@app/common/transactions/bitcoin/fees/btc-size-fee-estimator'; -import { useCurrentTaprootAccountUninscribedUtxos } from '@app/query/bitcoin/balance/btc-balance-taproot.hooks'; -import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; -import { useNumberOfInscriptionsOnUtxo } from '@app/query/bitcoin/ordinals/inscriptions.hooks'; +import { useCurrentAccountIndex } from '@app/store/accounts/account'; import { useBitcoinScureLibNetworkConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin-keychain'; -import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; +import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { + useCurrentAccountTaprootSigner, + useCurrentTaprootAccount, +} from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; export function useGenerateRetrieveTaprootFundsTx() { const networkMode = useBitcoinScureLibNetworkConfig(); - const uninscribedUtxos = useCurrentTaprootAccountUninscribedUtxos(); + + const currentAccountIndex = useCurrentAccountIndex(); + const account = useCurrentTaprootAccount(); + const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); + + const uninscribedUtxos = useCurrentTaprootAccountUninscribedUtxos({ + taprootKeychain: account?.keychain, + nativeSegwitAddress: nativeSegwitSigner.address, + currentAccountIndex, + }); + const createSigner = useCurrentAccountTaprootSigner(); const { data: feeRates } = useAverageBitcoinFeeRates(); - const getNumberOfInscriptionOnUtxo = useNumberOfInscriptionsOnUtxo(); + const getNumberOfInscriptionOnUtxo = useNumberOfInscriptionsOnUtxo({ + taprootKeychain: account?.keychain, + nativeSegwitAddress: nativeSegwitSigner.address, + }); const fee = useMemo(() => { if (!feeRates) return createMoney(0, 'BTC'); 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 054472e0f3b..a4b4b7a3fdc 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 @@ -1,3 +1,4 @@ +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; import { bytesToUtf8 } from '@stacks/common'; import { ClarityType, @@ -22,13 +23,13 @@ import { import { formatMoney, i18nFormatCurrency } from '@app/common/money/format-money'; import { getEstimatedConfirmationTime } from '@app/common/transactions/stacks/transaction.utils'; import { removeTrailingNullCharacters } from '@app/common/utils'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useStacksBlockTime } from '@app/query/stacks/info/info.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; import { microStxToStx } from '@app/ui/utils/micro-stx-to-stx'; export function useStacksTransactionSummary(token: CryptoCurrencies) { - const tokenMarketData = useCryptoCurrencyMarketDataMeanAverage(token); + // TODO: unsafe type assumption + const tokenMarketData = useCryptoCurrencyMarketDataMeanAverage(token as 'BTC' | 'STX'); const { isTestnet } = useCurrentNetworkState(); const { data: blockTime } = useStacksBlockTime(); diff --git a/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx b/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx index b7549ec79cd..554040eb53a 100644 --- a/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx +++ b/src/app/pages/bitcoin-contract-list/components/bitcoin-contract-list-item-layout.tsx @@ -1,5 +1,6 @@ import { useCallback } from 'react'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; import { Flex, HStack, styled } from 'leather-styles/jsx'; import { createMoneyFromDecimal } from '@shared/models/money.model'; @@ -8,7 +9,6 @@ import { useBitcoinExplorerLink } from '@app/common/hooks/use-bitcoin-explorer-l import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; import { satToBtc } from '@app/common/money/unit-conversion'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { Flag } from '@app/ui/components/flag/flag'; import { Caption } from '@app/ui/components/typography/caption'; import { BitcoinContractIcon } from '@app/ui/icons/bitcoin-contract-icon'; diff --git a/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-offer/bitcoin-contract-offer-input.tsx b/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-offer/bitcoin-contract-offer-input.tsx index 8529b527585..b1c25519891 100644 --- a/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-offer/bitcoin-contract-offer-input.tsx +++ b/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-offer/bitcoin-contract-offer-input.tsx @@ -1,3 +1,4 @@ +import { useCalculateBitcoinFiatValue } from '@leather-wallet/query'; import { Stack, styled } from 'leather-styles/jsx'; import { createMoneyFromDecimal } from '@shared/models/money.model'; @@ -5,7 +6,6 @@ import { createMoneyFromDecimal } from '@shared/models/money.model'; import { SimplifiedBitcoinContract } from '@app/common/hooks/use-bitcoin-contracts'; import { formatMoney, i18nFormatCurrency } from '@app/common/money/format-money'; import { satToBtc } from '@app/common/money/unit-conversion'; -import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; import { BitcoinContractLockAmount } from './bitcoin-contract-lock-amount'; diff --git a/src/app/pages/rpc-send-transfer/rpc-send-transfer-choose-fee.tsx b/src/app/pages/rpc-send-transfer/rpc-send-transfer-choose-fee.tsx index c7a85e9a90e..0a8c8db57ff 100644 --- a/src/app/pages/rpc-send-transfer/rpc-send-transfer-choose-fee.tsx +++ b/src/app/pages/rpc-send-transfer/rpc-send-transfer-choose-fee.tsx @@ -1,5 +1,7 @@ import { Outlet, useNavigate } from 'react-router-dom'; +import type { UtxoResponseItem } from '@leather-wallet/query'; + import { logger } from '@shared/logger'; import { BtcFeeType } from '@shared/models/fees/bitcoin-fees.model'; import type { TransferRecipient } from '@shared/models/form.model'; @@ -15,7 +17,6 @@ import { import { useBitcoinFeesList } from '@app/components/bitcoin-fees-list/use-bitcoin-fees-list-multiple-recipients'; import { BitcoinChooseFee } from '@app/features/bitcoin-choose-fee/bitcoin-choose-fee'; import { useValidateBitcoinSpend } from '@app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { useSignBitcoinTx } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; import { formFeeRowValue } from '../../common/send/utils'; diff --git a/src/app/pages/rpc-send-transfer/rpc-send-transfer-confirmation.tsx b/src/app/pages/rpc-send-transfer/rpc-send-transfer-confirmation.tsx index 1f436e55bd4..c48f5e515b4 100644 --- a/src/app/pages/rpc-send-transfer/rpc-send-transfer-confirmation.tsx +++ b/src/app/pages/rpc-send-transfer/rpc-send-transfer-confirmation.tsx @@ -1,5 +1,9 @@ import { useLocation, useNavigate } from 'react-router-dom'; +import { + useBitcoinBroadcastTransaction, + useCryptoCurrencyMarketDataMeanAverage, +} from '@leather-wallet/query'; import { HStack, Stack, styled } from 'leather-styles/jsx'; import get from 'lodash.get'; @@ -21,8 +25,6 @@ import { } from '@app/common/money/format-money'; import { InfoCardFooter } from '@app/components/info-card/info-card'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { Button } from '@app/ui/components/button/button'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; diff --git a/src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx b/src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx index 7dbdcec1373..5090aec3240 100644 --- a/src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx +++ b/src/app/pages/rpc-sign-psbt/use-rpc-sign-psbt.tsx @@ -1,6 +1,11 @@ import { useNavigate } from 'react-router-dom'; import { RpcErrorCode } from '@btckit/types'; +import { + useBitcoinBroadcastTransaction, + useCalculateBitcoinFiatValue, + useCryptoCurrencyMarketDataMeanAverage, +} from '@leather-wallet/query'; import { hexToBytes } from '@noble/hashes/utils'; import { bytesToHex } from '@stacks/common'; @@ -16,11 +21,6 @@ import { SignPsbtArgs } from '@app/common/psbt/requests'; import { useRpcSignPsbtParams } from '@app/common/psbt/use-psbt-request-params'; import { usePsbtSigner } from '@app/features/psbt-signer/hooks/use-psbt-signer'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; -import { - useCalculateBitcoinFiatValue, - useCryptoCurrencyMarketDataMeanAverage, -} from '@app/query/common/market-data/market-data.hooks'; import { useGetAssumedZeroIndexSigningConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; interface BroadcastSignedPsbtTxArgs { diff --git a/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts b/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts index eab76a0ca44..d62cf44f6dd 100644 --- a/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts +++ b/src/app/pages/send/ordinal-inscription/coinselect/select-inscription-coins.ts @@ -1,10 +1,11 @@ +import type { UtxoResponseItem, UtxoWithDerivationPath } from '@leather-wallet/query'; + import { BTC_P2WPKH_DUST_AMOUNT } from '@shared/constants'; import { isDefined } from '@shared/utils'; import { sumNumbers } from '@app/common/math/helpers'; import { BtcSizeFeeEstimator } from '@app/common/transactions/bitcoin/fees/btc-size-fee-estimator'; import { createCounter } from '@app/common/utils/counter'; -import { UtxoResponseItem, UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; interface SelectInscriptionCoinSuccess { success: true; diff --git a/src/app/pages/send/ordinal-inscription/components/create-utxo-from-inscription.ts b/src/app/pages/send/ordinal-inscription/components/create-utxo-from-inscription.ts index 11a76ea009a..ec2a7de0b08 100644 --- a/src/app/pages/send/ordinal-inscription/components/create-utxo-from-inscription.ts +++ b/src/app/pages/send/ordinal-inscription/components/create-utxo-from-inscription.ts @@ -1,33 +1,37 @@ +import type { Inscription } from '@leather-wallet/models'; +import type { UtxoWithDerivationPath } from '@leather-wallet/query'; + import { BitcoinNetworkModes } from '@shared/constants'; import { getNativeSegwitAddressIndexDerivationPath } from '@shared/crypto/bitcoin/p2wpkh-address-gen'; -import { Inscription } from '@shared/models/inscription.model'; - -import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; interface CreateUtxoFromInscriptionArgs { inscription: Inscription; network: BitcoinNetworkModes; accountIndex: number; + inscriptionAddressIdx: number; } export function createUtxoFromInscription({ inscription, network, accountIndex, + inscriptionAddressIdx, }: CreateUtxoFromInscriptionArgs): UtxoWithDerivationPath { - const { genesis_block_hash, genesis_timestamp, genesis_block_height, value, addressIndex } = - inscription; - + const { genesisBlockHash, genesisTimestamp, genesisBlockHeight, value } = inscription; return { - txid: inscription.tx_id, + txid: inscription.txid, vout: Number(inscription.output.split(':')[1]), status: { confirmed: true, - block_height: genesis_block_height, - block_hash: genesis_block_hash, - block_time: genesis_timestamp, + block_height: genesisBlockHeight, + block_hash: genesisBlockHash, + block_time: genesisTimestamp, }, value: Number(value), - derivationPath: getNativeSegwitAddressIndexDerivationPath(network, accountIndex, addressIndex), + derivationPath: getNativeSegwitAddressIndexDerivationPath( + network, + accountIndex, + inscriptionAddressIdx + ), }; } diff --git a/src/app/pages/send/ordinal-inscription/components/send-inscription-container.tsx b/src/app/pages/send/ordinal-inscription/components/send-inscription-container.tsx index 98983fed764..fad5aec4123 100644 --- a/src/app/pages/send/ordinal-inscription/components/send-inscription-container.tsx +++ b/src/app/pages/send/ordinal-inscription/components/send-inscription-container.tsx @@ -1,14 +1,16 @@ import { useState } from 'react'; import { Outlet, useLocation, useOutletContext } from 'react-router-dom'; +import type { Inscription } from '@leather-wallet/models'; +import { type UtxoWithDerivationPath, useInscriptionsAddressesMap } from '@leather-wallet/query'; import get from 'lodash.get'; import { AverageBitcoinFeeRates, BtcFeeType } from '@shared/models/fees/bitcoin-fees.model'; -import { SupportedInscription } from '@shared/models/inscription.model'; import { useOnMount } from '@app/common/hooks/use-on-mount'; -import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; import { useCurrentAccountIndex } from '@app/store/accounts/account'; +import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentTaprootAccount } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; import { useSendInscriptionRouteState } from '../hooks/use-send-inscription-route-state'; @@ -17,7 +19,7 @@ import { SendInscriptionLoader } from './send-inscription-loader'; interface SendInscriptionContextState { feeRates: AverageBitcoinFeeRates; - inscription: SupportedInscription; + inscription: Inscription; selectedFeeType: BtcFeeType; setSelectedFeeType(value: BtcFeeType | null): void; utxo: UtxoWithDerivationPath; @@ -30,13 +32,19 @@ export function useSendInscriptionState() { export function SendInscriptionContainer() { const [selectedFeeType, setSelectedFeeType] = useState(null); - const [inscription, setInscription] = useState(null); + const [inscription, setInscription] = useState(null); const [utxo, setUtxo] = useState(null); const routeState = useSendInscriptionRouteState(); const network = useCurrentNetwork(); const currentAccountIndex = useCurrentAccountIndex(); + const account = useCurrentTaprootAccount(); + const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); + const addressesMap = useInscriptionsAddressesMap({ + taprootKeychain: account?.keychain, + nativeSegwitAddress: nativeSegwitSigner.address, + }); useOnMount(() => { if (!routeState.inscription) return; setInscription(routeState.inscription); @@ -45,6 +53,7 @@ export function SendInscriptionContainer() { inscription: routeState.inscription, network: network.chain.bitcoin.bitcoinNetwork, accountIndex: currentAccountIndex, + inscriptionAddressIdx: addressesMap[routeState.inscription.address], }) ); }); diff --git a/src/app/pages/send/ordinal-inscription/components/send-inscription-loader.tsx b/src/app/pages/send/ordinal-inscription/components/send-inscription-loader.tsx index efffb98fe70..3d7b97b7203 100644 --- a/src/app/pages/send/ordinal-inscription/components/send-inscription-loader.tsx +++ b/src/app/pages/send/ordinal-inscription/components/send-inscription-loader.tsx @@ -1,6 +1,6 @@ -import { AverageBitcoinFeeRates } from '@shared/models/fees/bitcoin-fees.model'; +import { useAverageBitcoinFeeRates } from '@leather-wallet/query'; -import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; +import { AverageBitcoinFeeRates } from '@shared/models/fees/bitcoin-fees.model'; interface SendInscriptionLoaderProps { children(data: { feeRates: AverageBitcoinFeeRates }): React.JSX.Element; diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts b/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts index 6097c44d18a..ec945a50417 100644 --- a/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts +++ b/src/app/pages/send/ordinal-inscription/hooks/use-generate-ordinal-tx.ts @@ -1,3 +1,4 @@ +import type { UtxoWithDerivationPath } from '@leather-wallet/query'; import * as btc from '@scure/btc-signer'; import { AddressType, getAddressInfo } from 'bitcoin-address-validation'; @@ -13,7 +14,6 @@ import { } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; import { createCounter } from '@app/common/utils/counter'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; import { useBitcoinScureLibNetworkConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin-keychain'; import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts index 4f077a65ddc..efaf2bb272b 100644 --- a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts +++ b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-fees-list.ts @@ -1,16 +1,19 @@ import { useCallback, useMemo } from 'react'; +import type { Inscription } from '@leather-wallet/models'; +import { + type UtxoWithDerivationPath, + useAverageBitcoinFeeRates, + useCryptoCurrencyMarketDataMeanAverage, +} from '@leather-wallet/query'; + import { BtcFeeType, btcTxTimeMap } from '@shared/models/fees/bitcoin-fees.model'; -import type { SupportedInscription } from '@shared/models/inscription.model'; import { createMoney } from '@shared/models/money.model'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { formatMoneyPadded, i18nFormatCurrency } from '@app/common/money/format-money'; import { FeesListItem } from '@app/components/bitcoin-fees-list/bitcoin-fees-list'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { UtxoWithDerivationPath } from '@app/query/bitcoin/bitcoin-client'; -import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useGenerateUnsignedOrdinalTx } from './use-generate-ordinal-tx'; @@ -18,7 +21,7 @@ import { useGenerateUnsignedOrdinalTx } from './use-generate-ordinal-tx'; interface UseSendInscriptionFeesListArgs { recipient: string; utxo: UtxoWithDerivationPath; - inscription: SupportedInscription; + inscription: Inscription; } export function useSendInscriptionFeesList({ diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-form.tsx b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-form.tsx index 8e5a89f1276..1256f704e95 100644 --- a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-form.tsx +++ b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-form.tsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import { useNumberOfInscriptionsOnUtxo } from '@leather-wallet/query'; import * as yup from 'yup'; import { bitcoinNetworkModeToCoreNetworkMode } from '@shared/crypto/bitcoin/bitcoin.utils'; @@ -18,8 +19,9 @@ import { btcAddressValidator, } from '@app/common/validation/forms/address-validators'; import { complianceValidator } from '@app/common/validation/forms/compliance-validators'; -import { useNumberOfInscriptionsOnUtxo } from '@app/query/bitcoin/ordinals/inscriptions.hooks'; import { useSignBitcoinTx } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; +import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentTaprootAccount } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; import { useSendInscriptionState } from '../components/send-inscription-container'; @@ -35,7 +37,13 @@ export function useSendInscriptionForm() { const { inscription, utxo } = useSendInscriptionState(); const currentNetwork = useCurrentNetwork(); - const getNumberOfInscriptionOnUtxo = useNumberOfInscriptionsOnUtxo(); + const account = useCurrentTaprootAccount(); + const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); + + const getNumberOfInscriptionOnUtxo = useNumberOfInscriptionsOnUtxo({ + taprootKeychain: account?.keychain, + nativeSegwitAddress: nativeSegwitSigner.address, + }); const { coverFeeFromAdditionalUtxos } = useGenerateUnsignedOrdinalTx(utxo); return { diff --git a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-route-state.ts b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-route-state.ts index 3dc8e49bad0..3f3207f0fac 100644 --- a/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-route-state.ts +++ b/src/app/pages/send/ordinal-inscription/hooks/use-send-inscription-route-state.ts @@ -1,12 +1,11 @@ import { useLocation } from 'react-router-dom'; +import type { Inscription } from '@leather-wallet/models'; import get from 'lodash.get'; -import { SupportedInscription } from '@shared/models/inscription.model'; - export function useSendInscriptionRouteState() { const location = useLocation(); return { - inscription: get(location.state, 'inscription', null) as SupportedInscription | null, + inscription: get(location.state, 'inscription', null) as Inscription | null, }; } diff --git a/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx b/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx index 219a68a5bed..6dc03a21b2a 100644 --- a/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx +++ b/src/app/pages/send/ordinal-inscription/send-inscription-choose-fee.tsx @@ -24,7 +24,11 @@ export function SendInscriptionChooseFee() { const navigate = useNavigate(); const { recipient, selectedFeeType, setSelectedFeeType, utxo, inscription } = useSendInscriptionState(); - const { feesList, isLoading } = useSendInscriptionFeesList({ recipient, utxo, inscription }); + const { feesList, isLoading } = useSendInscriptionFeesList({ + recipient, + utxo, + inscription, + }); const recommendedFeeRate = feesList[1]?.feeRate.toString() || ''; const { reviewTransaction } = useSendInscriptionForm(); diff --git a/src/app/pages/send/ordinal-inscription/send-inscription-review.tsx b/src/app/pages/send/ordinal-inscription/send-inscription-review.tsx index 049ce3c4b63..2ec902ab3c8 100644 --- a/src/app/pages/send/ordinal-inscription/send-inscription-review.tsx +++ b/src/app/pages/send/ordinal-inscription/send-inscription-review.tsx @@ -1,5 +1,6 @@ import { useLocation, useNavigate } from 'react-router-dom'; +import { useBitcoinBroadcastTransaction } from '@leather-wallet/query'; import { bytesToHex } from '@noble/hashes/utils'; import { Box, Flex, Stack } from 'leather-styles/jsx'; import get from 'lodash.get'; @@ -20,7 +21,6 @@ import { DialogHeader } from '@app/ui/components/containers/headers/dialog-heade import { Card } from '@app/ui/layout/card/card'; import { InscriptionPreviewCard } from '../../../components/inscription-preview-card/inscription-preview-card'; -import { useBitcoinBroadcastTransaction } from '../../../query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; import { useSendInscriptionState } from './components/send-inscription-container'; function useSendInscriptionReviewState() { @@ -45,7 +45,7 @@ export function SendInscriptionReview() { async function sendInscription() { await broadcastTx({ - skipSpendableCheckUtxoIds: [inscription.tx_id], + skipSpendableCheckUtxoIds: [inscription.txid], tx: bytesToHex(signedTx), async onSuccess(txid: string) { void analytics.track('broadcast_ordinal_transaction'); diff --git a/src/app/pages/send/ordinal-inscription/sent-inscription-summary.tsx b/src/app/pages/send/ordinal-inscription/sent-inscription-summary.tsx index 7d63b91d782..cdb13630d97 100644 --- a/src/app/pages/send/ordinal-inscription/sent-inscription-summary.tsx +++ b/src/app/pages/send/ordinal-inscription/sent-inscription-summary.tsx @@ -1,10 +1,10 @@ import { useLocation, useNavigate } from 'react-router-dom'; +import type { Inscription } from '@leather-wallet/models'; import { Box, Flex, HStack, Stack } from 'leather-styles/jsx'; import get from 'lodash.get'; import { Blockchains } from '@shared/models/blockchain.model'; -import { SupportedInscription } from '@shared/models/inscription.model'; import { RouteUrls } from '@shared/route-urls'; import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; @@ -30,7 +30,7 @@ function useSendInscriptionSummaryState() { txid: get(location.state, 'txid') as string, recipient: get(location.state, 'recipient', '') as string, arrivesIn: get(location.state, 'arrivesIn') as string, - inscription: get(location.state, 'inscription') as SupportedInscription, + inscription: get(location.state, 'inscription') as Inscription, feeRowValue: get(location.state, 'feeRowValue') as string, }; } diff --git a/src/app/pages/send/send-crypto-asset-form/family/bitcoin/hooks/use-calculate-max-spend.ts b/src/app/pages/send/send-crypto-asset-form/family/bitcoin/hooks/use-calculate-max-spend.ts index d83eef530f7..806d30cb8b0 100644 --- a/src/app/pages/send/send-crypto-asset-form/family/bitcoin/hooks/use-calculate-max-spend.ts +++ b/src/app/pages/send/send-crypto-asset-form/family/bitcoin/hooks/use-calculate-max-spend.ts @@ -1,8 +1,8 @@ import { useCallback } from 'react'; +import { type UtxoResponseItem, useAverageBitcoinFeeRates } from '@leather-wallet/query'; + import { calculateMaxBitcoinSpend } from '@app/common/transactions/bitcoin/fees/calculate-max-bitcoin-spend'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; -import { useAverageBitcoinFeeRates } from '@app/query/bitcoin/fees/fee-estimates.hooks'; export function useCalculateMaxBitcoinSpend() { const { data: feeRates } = useAverageBitcoinFeeRates(); diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx index bdba1fd81d9..9c4dd38e274 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { Outlet, useLocation, useNavigate } from 'react-router-dom'; +import type { UtxoResponseItem } from '@leather-wallet/query'; import { Stack } from 'leather-styles/jsx'; import get from 'lodash.get'; @@ -20,7 +21,6 @@ import { LoadingSpinner } from '@app/components/loading-spinner'; import { BitcoinChooseFee } from '@app/features/bitcoin-choose-fee/bitcoin-choose-fee'; import { useValidateBitcoinSpend } from '@app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend'; import { useToast } from '@app/features/toasts/use-toast'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { useBrc20Transfers } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks'; import { useSignBitcoinTx } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx index f589aa22c57..7372b141b6e 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx @@ -1,5 +1,6 @@ import { useLocation, useNavigate } from 'react-router-dom'; +import { useBitcoinBroadcastTransaction } from '@leather-wallet/query'; import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors'; import { Stack } from 'leather-styles/jsx'; import get from 'lodash.get'; @@ -18,7 +19,6 @@ import { } from '@app/components/info-card/info-card'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; import { useBrc20Transfers } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks'; -import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; import { Button } from '@app/ui/components/button/button'; import { Footer } from '@app/ui/components/containers/footers/footer'; import { Card } from '@app/ui/layout/card/card'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-choose-fee.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-choose-fee.tsx index 23a71a1827d..8942da0239b 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-choose-fee.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-choose-fee.tsx @@ -1,5 +1,7 @@ import { Outlet } from 'react-router-dom'; +import type { UtxoResponseItem } from '@leather-wallet/query'; + import { BtcFeeType } from '@shared/models/fees/bitcoin-fees.model'; import { BitcoinSendFormValues } from '@shared/models/form.model'; @@ -8,7 +10,6 @@ import { BitcoinFeesList } from '@app/components/bitcoin-fees-list/bitcoin-fees- import { useBitcoinFeesList } from '@app/components/bitcoin-fees-list/use-bitcoin-fees-list'; import { BitcoinChooseFee } from '@app/features/bitcoin-choose-fee/bitcoin-choose-fee'; import { useValidateBitcoinSpend } from '@app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; import { useSendBitcoinAssetContextState } from '../../family/bitcoin/components/send-bitcoin-asset-container'; import { useBtcChooseFee } from './use-btc-choose-fee'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx index b8fe3651a00..a8d75dc1683 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form-confirmation.tsx @@ -1,6 +1,10 @@ import { useMemo } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; +import { + useBitcoinBroadcastTransaction, + useCryptoCurrencyMarketDataMeanAverage, +} from '@leather-wallet/query'; import { hexToBytes } from '@noble/hashes/utils'; import * as btc from '@scure/btc-signer'; import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors'; @@ -25,8 +29,6 @@ import { InfoCardSeparator, } from '@app/components/info-card/info-card'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { useBitcoinBroadcastTransaction } from '@app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { Button } from '@app/ui/components/button/button'; import { Footer } from '@app/ui/components/containers/footers/footer'; import { Card } from '@app/ui/layout/card/card'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx index 298774c3371..456c1055be2 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/btc-send-form.tsx @@ -1,6 +1,7 @@ import { Outlet } from 'react-router-dom'; import type { CryptoCurrencies } from '@leather-wallet/models'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; import { SendCryptoAssetSelectors } from '@tests/selectors/send.selectors'; import { Form, Formik } from 'formik'; import { Box } from 'leather-styles/jsx'; @@ -9,7 +10,6 @@ import { HIGH_FEE_WARNING_LEARN_MORE_URL_BTC } from '@shared/constants'; import { formatMoney } from '@app/common/money/format-money'; import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; import { Button } from '@app/ui/components/button/button'; import { Callout } from '@app/ui/components/callout/callout'; diff --git a/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx index c13dfedc82a..9ab2744c76f 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stx/stx-send-form.tsx @@ -1,6 +1,5 @@ -import { CryptoCurrencies } from '@shared/models/currencies.model'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@leather-wallet/query'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; import { AmountField } from '../../components/amount-field'; @@ -10,7 +9,7 @@ import { SendMaxButton } from '../../components/send-max-button'; import { StacksCommonSendForm } from '../stacks/stacks-common-send-form'; import { useStxSendForm } from './use-stx-send-form'; -const symbol: CryptoCurrencies = 'STX'; +const symbol = 'STX'; export function StxSendForm() { const stxMarketData = useCryptoCurrencyMarketDataMeanAverage(symbol); diff --git a/src/app/pages/send/send-crypto-asset-form/hooks/use-send-form-navigate.ts b/src/app/pages/send/send-crypto-asset-form/hooks/use-send-form-navigate.ts index 1a761a11bf4..49f0bcd3112 100644 --- a/src/app/pages/send/send-crypto-asset-form/hooks/use-send-form-navigate.ts +++ b/src/app/pages/send/send-crypto-asset-form/hooks/use-send-form-navigate.ts @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; +import type { UtxoResponseItem } from '@leather-wallet/query'; import { bytesToHex } from '@stacks/common'; import { StacksTransaction } from '@stacks/transactions'; import { AxiosError } from 'axios'; @@ -8,8 +9,6 @@ import { AxiosError } from 'axios'; import { BitcoinSendFormValues } from '@shared/models/form.model'; import { RouteUrls } from '@shared/route-urls'; -import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client'; - interface ConfirmationRouteState { decimals?: number; token?: string; diff --git a/src/app/query/bitcoin/address/address.utils.ts b/src/app/query/bitcoin/address/address.utils.ts deleted file mode 100644 index 079ad6e3cec..00000000000 --- a/src/app/query/bitcoin/address/address.utils.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { isEmptyArray } from '@shared/utils'; - -import { UtxoResponseItem } from '../bitcoin-client'; - -export function hasInscriptions(utxos: UtxoResponseItem[]) { - return !isEmptyArray(utxos); -} diff --git a/src/app/query/bitcoin/address/transactions-by-address.hooks.ts b/src/app/query/bitcoin/address/transactions-by-address.hooks.ts deleted file mode 100644 index 05ded11c2a8..00000000000 --- a/src/app/query/bitcoin/address/transactions-by-address.hooks.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { useCallback } from 'react'; - -import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; - -import { sumNumbers } from '@app/common/math/helpers'; - -import { - useGetBitcoinTransactionsByAddressQuery, - useGetBitcoinTransactionsByAddressesQuery, -} from './transactions-by-address.query'; - -function useFilterAddressPendingTransactions() { - return useCallback((txs: BitcoinTx[]) => { - return txs.filter(tx => !tx.status.confirmed); - }, []); -} - -export function useBitcoinPendingTransactions(addresses: string[]) { - const filterPendingTransactions = useFilterAddressPendingTransactions(); - - return useGetBitcoinTransactionsByAddressesQuery(addresses, { - select(txs) { - return filterPendingTransactions(txs); - }, - }); -} - -export function useBitcoinPendingTransactionsInputs(address: string) { - const filterPendingTransactions = useFilterAddressPendingTransactions(); - - return useGetBitcoinTransactionsByAddressQuery(address, { - select(txs) { - return filterPendingTransactions(txs).flatMap(tx => tx.vin.map(input => input)); - }, - }); -} - -export function calculateOutboundPendingTxsValue(pendingTxs: BitcoinTx[], address: string) { - // sum all inputs - const sumInputs = sumNumbers(pendingTxs.flatMap(tx => tx.vin.map(input => input.prevout.value))); - - // get all outputs that are sent back to the address - const returnedOutputChangeValues = pendingTxs - .flatMap(tx => tx.vout.map(output => output)) - .filter(v => v.scriptpubkey_address === address) - .flatMap(output => output.value); - - // sum all filtered outputs - const sumOutputs = sumNumbers(returnedOutputChangeValues); - - return sumInputs.minus(sumOutputs).toNumber(); -} diff --git a/src/app/query/bitcoin/address/transactions-by-address.query.ts b/src/app/query/bitcoin/address/transactions-by-address.query.ts deleted file mode 100644 index 8f13efe7b6e..00000000000 --- a/src/app/query/bitcoin/address/transactions-by-address.query.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { type QueryFunctionContext, useQueries, useQuery } from '@tanstack/react-query'; - -import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; - -const staleTime = 10 * 1000; - -const queryOptions = { staleTime, cacheTime: Infinity, refetchInterval: staleTime }; - -export function useGetBitcoinTransactionsByAddressQuery( - address: string, - options?: AppUseQueryConfig -) { - const client = useBitcoinClient(); - - return useQuery({ - enabled: !!address, - queryKey: ['btc-txs-by-address', address], - queryFn: async ({ signal }) => { - return client.addressApi.getTransactionsByAddress(address, signal); - }, - ...queryOptions, - ...options, - }); -} - -export function useGetBitcoinTransactionsByAddressesQuery( - addresses: string[], - options?: AppUseQueryConfig -) { - const client = useBitcoinClient(); - - return useQueries({ - queries: addresses.map(address => { - return { - enabled: !!address, - queryKey: ['btc-txs-by-address', address], - queryFn: async ({ signal }: QueryFunctionContext) => { - return client.addressApi.getTransactionsByAddress(address, signal); - }, - ...queryOptions, - ...options, - }; - }), - }); -} diff --git a/src/app/query/bitcoin/address/transactions-by-address.spec.ts b/src/app/query/bitcoin/address/transactions-by-address.spec.ts deleted file mode 100644 index 94f3de555ea..00000000000 --- a/src/app/query/bitcoin/address/transactions-by-address.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - mockBitcoinTestnetAddress, - mockPendingTxs1, - mockPendingTxs2, - mockPendingTxs3, -} from '@tests/mocks/mock-btc-txs'; - -import { calculateOutboundPendingTxsValue } from './transactions-by-address.hooks'; - -describe(calculateOutboundPendingTxsValue.name, () => { - test('that it returns 0 if there are no pending txs', () => { - expect(calculateOutboundPendingTxsValue([], mockBitcoinTestnetAddress)).toEqual(0); - }); - - test('that it returns the correct total sum of pending txs', () => { - expect(calculateOutboundPendingTxsValue(mockPendingTxs1, mockBitcoinTestnetAddress)).toEqual( - 14165 - ); - expect(calculateOutboundPendingTxsValue(mockPendingTxs2, mockBitcoinTestnetAddress)).toEqual( - 28330 - ); - expect(calculateOutboundPendingTxsValue(mockPendingTxs3, mockBitcoinTestnetAddress)).toEqual( - 14165 - ); - }); -}); diff --git a/src/app/query/bitcoin/address/utxos-by-address.hooks.ts b/src/app/query/bitcoin/address/utxos-by-address.hooks.ts index 05a15ecf1a5..79471ec6d5d 100644 --- a/src/app/query/bitcoin/address/utxos-by-address.hooks.ts +++ b/src/app/query/bitcoin/address/utxos-by-address.hooks.ts @@ -1,41 +1,7 @@ -import { useCallback } from 'react'; - -import { InscriptionResponseItem } from '@shared/models/inscription.model'; +import { useNativeSegwitUtxosByAddress } from '@leather-wallet/query'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { - type RunesOutputsByAddress, - UtxoResponseItem, - UtxoWithDerivationPath, -} from '../bitcoin-client'; -import { useInscriptionsByAddressQuery } from '../ordinals/inscriptions.query'; -import { useRunesEnabled, useRunesOutputsByAddress } from '../runes/runes.hooks'; -import { useBitcoinPendingTransactionsInputs } from './transactions-by-address.hooks'; -import { useGetUtxosByAddressQuery } from './utxos-by-address.query'; - -export function filterUtxosWithInscriptions( - inscriptions: InscriptionResponseItem[], - utxos: UtxoWithDerivationPath[] | UtxoResponseItem[] -) { - return utxos.filter( - utxo => - !inscriptions?.some( - inscription => `${utxo.txid}:${utxo.vout.toString()}` === inscription.output - ) - ); -} - -export function filterUtxosWithRunes(runes: RunesOutputsByAddress[], utxos: UtxoResponseItem[]) { - return utxos.filter(utxo => { - const hasRuneOutput = runes.find(rune => { - return rune.output === `${utxo.txid}:${utxo.vout}`; - }); - - return !hasRuneOutput; - }); -} - const defaultArgs = { filterInscriptionUtxos: true, filterPendingTxsUtxos: true, @@ -59,123 +25,3 @@ export function useCurrentNativeSegwitUtxos(args = defaultArgs) { filterRunesUtxos, }); } - -interface UseFilterUtxosByAddressArgs { - address: string; - filterInscriptionUtxos: boolean; - filterPendingTxsUtxos: boolean; - filterRunesUtxos: boolean; -} - -type filterUtxoFunctionType = (utxos: UtxoResponseItem[]) => UtxoResponseItem[]; - -export function useNativeSegwitUtxosByAddress({ - address, - filterInscriptionUtxos, - filterPendingTxsUtxos, - filterRunesUtxos, -}: UseFilterUtxosByAddressArgs) { - const { filterOutInscriptions, isInitialLoadingInscriptions } = - useFilterInscriptionsByAddress(address); - const { filterOutPendingTxsUtxos, isInitialLoading } = useFilterPendingUtxosByAddress(address); - const { filterOutRunesUtxos, isInitialLoadingRunesData } = useFilterRuneUtxosByAddress(address); - - const utxosQuery = useGetUtxosByAddressQuery(address, { - select(utxos) { - const filters = []; - if (filterPendingTxsUtxos) { - filters.push(filterOutPendingTxsUtxos); - } - - if (filterInscriptionUtxos) { - filters.push(filterOutInscriptions); - } - - if (filterRunesUtxos) { - filters.push(filterOutRunesUtxos); - } - - return filters.reduce( - (filteredUtxos: UtxoResponseItem[], filterFunc: filterUtxoFunctionType) => - filterFunc(filteredUtxos), - utxos - ); - }, - }); - - return { - ...utxosQuery, - isInitialLoading: - utxosQuery.isInitialLoading || - isInitialLoading || - isInitialLoadingInscriptions || - isInitialLoadingRunesData, - }; -} - -function useFilterInscriptionsByAddress(address: string) { - const { - data: inscriptionsList, - hasNextPage: hasMoreInscriptionsToLoad, - isInitialLoading: isInitialLoadingInscriptions, - } = useInscriptionsByAddressQuery(address); - - const filterOutInscriptions = useCallback( - (utxos: UtxoResponseItem[]) => { - const inscriptions = inscriptionsList?.pages.flatMap(page => page.results) ?? []; - - return filterUtxosWithInscriptions(inscriptions, utxos); - }, - [inscriptionsList?.pages] - ); - - return { - filterOutInscriptions, - isInitialLoadingInscriptions: hasMoreInscriptionsToLoad || isInitialLoadingInscriptions, - }; -} - -function useFilterRuneUtxosByAddress(address: string) { - // TO-DO what if data is undefined? - const { data = [], isInitialLoading } = useRunesOutputsByAddress(address); - const runesEnabled = useRunesEnabled(); - - const filterOutRunesUtxos = useCallback( - (utxos: UtxoResponseItem[]) => { - // If Runes are not enabled, return all utxos - if (!runesEnabled) { - return utxos; - } - - return filterUtxosWithRunes(data, utxos); - }, - [data, runesEnabled] - ); - - return { - filterOutRunesUtxos, - isInitialLoadingRunesData: isInitialLoading, - }; -} - -function useFilterPendingUtxosByAddress(address: string) { - const { data: pendingInputs = [], isInitialLoading } = - useBitcoinPendingTransactionsInputs(address); - - const filterOutPendingTxsUtxos = useCallback( - (utxos: UtxoResponseItem[]) => { - return utxos.filter( - utxo => - !pendingInputs.find( - input => input.prevout.scriptpubkey_address === address && input.txid === utxo.txid - ) - ); - }, - [address, pendingInputs] - ); - - return { - filterOutPendingTxsUtxos, - isInitialLoading, - }; -} diff --git a/src/app/query/bitcoin/address/utxos-by-address.query.ts b/src/app/query/bitcoin/address/utxos-by-address.query.ts deleted file mode 100644 index 3ac6ca3ca13..00000000000 --- a/src/app/query/bitcoin/address/utxos-by-address.query.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { getTaprootAddress } from '@shared/crypto/bitcoin/bitcoin.utils'; -import { getNativeSegwitAddressIndexDerivationPath } from '@shared/crypto/bitcoin/p2wpkh-address-gen'; - -import { createCounter } from '@app/common/utils/counter'; -import { AppUseQueryConfig } from '@app/query/query-config'; -import { QueryPrefixes } from '@app/query/query-prefixes'; -import { useCurrentAccountIndex } from '@app/store/accounts/account'; -import { useCurrentTaprootAccount } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -import { UtxoResponseItem, UtxoWithDerivationPath } from '../bitcoin-client'; -import { hasInscriptions } from './address.utils'; - -const staleTime = 3 * 60 * 1000; - -const queryOptions = { staleTime, refetchOnWindowFocus: false }; - -export function useGetUtxosByAddressQuery( - address: string, - options?: AppUseQueryConfig -) { - const client = useBitcoinClient(); - - return useQuery({ - enabled: !!address, - queryKey: ['btc-utxos-by-address', address], - queryFn: async ({ signal }) => { - return client.addressApi.getUtxosByAddress(address, signal); - }, - ...queryOptions, - ...options, - }); -} - -const stopSearchAfterNumberAddressesWithoutUtxos = 5; - -/** - * Returns all utxos for the user's current taproot account. The search for - * utxos iterates through all addresses until a sufficiently large number of - * empty addresses is found. - */ -export function useTaprootAccountUtxosQuery() { - const network = useCurrentNetwork(); - const account = useCurrentTaprootAccount(); - const client = useBitcoinClient(); - - const currentAccountIndex = useCurrentAccountIndex(); - - return useQuery( - [QueryPrefixes.TaprootAddressUtxos, currentAccountIndex, network.id], - async () => { - let currentNumberOfAddressesWithoutUtxos = 0; - const addressIndexCounter = createCounter(0); - let foundUnspentTransactions: UtxoWithDerivationPath[] = []; - while (currentNumberOfAddressesWithoutUtxos < stopSearchAfterNumberAddressesWithoutUtxos) { - const address = getTaprootAddress({ - index: addressIndexCounter.getValue(), - keychain: account?.keychain, - network: network.chain.bitcoin.bitcoinNetwork, - }); - - const unspentTransactions = await client.addressApi.getUtxosByAddress(address); - - if (!hasInscriptions(unspentTransactions)) { - currentNumberOfAddressesWithoutUtxos += 1; - addressIndexCounter.increment(); - continue; - } - - foundUnspentTransactions = [ - ...unspentTransactions.map(utxo => { - const addressIndex = addressIndexCounter.getValue(); - return { - // adds addresss index of which utxo belongs - ...utxo, - addressIndex, - derivationPath: getNativeSegwitAddressIndexDerivationPath( - network.chain.bitcoin.bitcoinNetwork, - currentAccountIndex, - addressIndex - ), - }; - }), - ...foundUnspentTransactions, - ]; - - currentNumberOfAddressesWithoutUtxos = 0; - addressIndexCounter.increment(); - } - return foundUnspentTransactions; - }, - { - refetchInterval: 15000, - refetchOnWindowFocus: false, - } - ); -} diff --git a/src/app/query/bitcoin/address/utxos-by-address.spec.tsx b/src/app/query/bitcoin/address/utxos-by-address.spec.tsx deleted file mode 100644 index a457da2aaca..00000000000 --- a/src/app/query/bitcoin/address/utxos-by-address.spec.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { mockInscriptionsList } from '@tests/mocks/mock-inscriptions'; -import { mockRunesOutputsByAddressList } from '@tests/mocks/mock-runes'; -import { mockUtxos, mockUtxosListWithRunes } from '@tests/mocks/mock-utxos'; - -import { filterUtxosWithInscriptions, filterUtxosWithRunes } from './utxos-by-address.hooks'; - -describe(filterUtxosWithInscriptions, () => { - test('that it filters out utxos with inscriptions so they are not spent', () => { - const filteredUtxos = filterUtxosWithInscriptions(mockInscriptionsList, mockUtxos); - expect(filteredUtxos).toEqual([]); - }); -}); - -describe(filterUtxosWithRunes, () => { - test('that it filters out utxos with runes so they are not spent', () => { - const filteredUtxos = filterUtxosWithRunes( - mockRunesOutputsByAddressList, - mockUtxosListWithRunes - ); - - expect(filteredUtxos).toEqual([ - { - txid: '66ff7d54e345170e3a76819dc90140971fdae054c9b7eea2089ba5a9720f6e44', - vout: 1, - status: { - confirmed: true, - block_height: 2585955, - block_hash: '00000000000000181cae54c3c19d6ed02511a2f6302a666c3d78bcf1777bb029', - block_time: 1712829917, - }, - value: 546, - }, - ]); - }); -}); diff --git a/src/app/query/bitcoin/balance/bitcoin-contracts-balance.hooks.ts b/src/app/query/bitcoin/balance/bitcoin-contracts-balance.hooks.ts index 7aad9c4d246..210c36f027f 100644 --- a/src/app/query/bitcoin/balance/bitcoin-contracts-balance.hooks.ts +++ b/src/app/query/bitcoin/balance/bitcoin-contracts-balance.hooks.ts @@ -1,12 +1,12 @@ import { useMemo } from 'react'; +import { useCalculateBitcoinFiatValue } from '@leather-wallet/query'; import BigNumber from 'bignumber.js'; import { createMoney } from '@shared/models/money.model'; import { isUndefined } from '@shared/utils'; import { i18nFormatCurrency } from '@app/common/money/format-money'; -import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks'; import { useBitcoinContractsBalanceQuery } from './bitcoin-contracts-balance.query'; diff --git a/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts b/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts index e962da88c77..fd7814d2c00 100644 --- a/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts +++ b/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import type { BtcCryptoAssetBalance, Money } from '@leather-wallet/models'; +import { useNativeSegwitUtxosByAddress, useRunesEnabled } from '@leather-wallet/query'; import BigNumber from 'bignumber.js'; import { createMoney } from '@shared/models/money.model'; @@ -9,9 +10,6 @@ import { isUndefined } from '@shared/utils'; import { sumNumbers } from '@app/common/math/helpers'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { useNativeSegwitUtxosByAddress } from '../address/utxos-by-address.hooks'; -import { useRunesEnabled } from '../runes/runes.hooks'; - function createBtcCryptoAssetBalance(balance: Money): BtcCryptoAssetBalance { return { availableBalance: balance, diff --git a/src/app/query/bitcoin/balance/btc-balance-taproot.hooks.ts b/src/app/query/bitcoin/balance/btc-balance-taproot.hooks.ts deleted file mode 100644 index 3cb985c9374..00000000000 --- a/src/app/query/bitcoin/balance/btc-balance-taproot.hooks.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useMemo } from 'react'; - -import { createMoney } from '@shared/models/money.model'; - -import { sumNumbers } from '@app/common/math/helpers'; - -import { filterUtxosWithInscriptions } from '../address/utxos-by-address.hooks'; -import { useTaprootAccountUtxosQuery } from '../address/utxos-by-address.query'; -import { UtxoWithDerivationPath } from '../bitcoin-client'; -import { useGetInscriptionsInfiniteQuery } from '../ordinals/inscriptions.query'; - -const RETRIEVE_UTXO_DUST_AMOUNT = 10000; - -export function useCurrentTaprootAccountUninscribedUtxos() { - const { data: utxos = [] } = useTaprootAccountUtxosQuery(); - - const query = useGetInscriptionsInfiniteQuery(); - - return useMemo(() => { - const inscriptions = query.data?.pages?.flatMap(page => page.inscriptions) ?? []; - - const filteredUtxosList = utxos - .filter(utxo => utxo.status.confirmed) - .filter(utxo => utxo.value > RETRIEVE_UTXO_DUST_AMOUNT); - - return filterUtxosWithInscriptions(inscriptions, filteredUtxosList) as UtxoWithDerivationPath[]; - }, [query.data?.pages, utxos]); -} - -export function useCurrentTaprootAccountBalance() { - const uninscribedUtxos = useCurrentTaprootAccountUninscribedUtxos(); - - return useMemo( - () => createMoney(sumNumbers(uninscribedUtxos.map(utxo => Number(utxo.value))), 'BTC'), - [uninscribedUtxos] - ); -} diff --git a/src/app/query/bitcoin/bitcoin-client.ts b/src/app/query/bitcoin/bitcoin-client.ts deleted file mode 100644 index eb6d74d3cb1..00000000000 --- a/src/app/query/bitcoin/bitcoin-client.ts +++ /dev/null @@ -1,413 +0,0 @@ -import axios from 'axios'; -import PQueue from 'p-queue'; - -import { - BESTINSLOT_API_BASE_URL_MAINNET, - BESTINSLOT_API_BASE_URL_TESTNET, - type BitcoinNetworkModes, -} from '@shared/constants'; -import type { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; - -import { getBlockstreamRatelimiter } from './blockstream-rate-limiter'; - -class Configuration { - constructor(public baseUrl: string) {} -} - -export interface UtxoResponseItem { - txid: string; - vout: number; - status: { - confirmed: boolean; - block_height: number; - block_hash: string; - block_time: number; - }; - value: number; -} - -export interface UtxoWithDerivationPath extends UtxoResponseItem { - derivationPath: string; -} - -interface BestinslotInscription { - inscription_name: string | null; - inscription_id: string; - inscription_number: number; - metadata: any | null; - wallet: string; - mime_type: string; - media_length: number; - genesis_ts: number; - genesis_height: number; - genesis_fee: number; - output_value: number; - satpoint: string; - collection_name: string | null; - collection_slug: string | null; - last_transfer_block_height: number; - content_url: string; - bis_url: string; - byte_size: number; -} - -interface BestinslotInscriptionByIdResponse { - data: BestinslotInscription; - block_height: number; -} - -interface BestinslotInscriptionsByTxIdResponse { - data: { inscription_id: string }[]; - blockHeight: number; -} - -/* BRC-20 */ -interface Brc20Balance { - ticker: string; - overall_balance: string; - available_balance: string; - transferrable_balance: string; - image_url: string | null; - min_listed_unit_price: number | null; -} - -interface Brc20TickerInfo { - id: string; - number: number; - block_height: number; - tx_id: string; - address: string; - ticker: string; - max_supply: string; - mint_limit: string; - decimals: number; - deploy_timestamp: number; - minted_supply: string; - tx_count: number; -} - -interface Brc20TickerInfoResponse { - block_height: number; - data: Brc20TickerInfo; -} - -interface Brc20WalletBalancesResponse { - block_height: number; - data: Brc20Balance[]; -} - -/* RUNES */ -export interface RuneBalance { - pkscript: string; - rune_id: string; - rune_name: string; - spaced_rune_name: string; - total_balance: string; - wallet_addr: string; -} - -interface RunesWalletBalancesResponse { - block_height: number; - data: RuneBalance[]; -} - -export interface RuneTickerInfo { - rune_id: string; - rune_number: string; - rune_name: string; - spaced_rune_name: string; - symbol: string; - decimals: number; - per_mint_amount: string; - mint_cnt: string; - mint_cnt_limit: string; - premined_supply: string; - total_minted_supply: string; - burned_supply: string; - circulating_supply: string; - mint_progress: number; - mint_start_block: number | null; - mint_end_block: number | null; - genesis_block: number; - deploy_ts: string; - deploy_txid: string; - auto_upgrade: boolean; - holder_count: number; - event_count: number; - mintable: boolean; -} -interface RunesTickerInfoResponse { - block_height: number; - data: RuneTickerInfo; -} - -export interface RunesOutputsByAddress { - pkscript: string; - wallet_addr: string; - output: string; - rune_ids: string[]; - balances: number[]; - rune_names: string[]; - spaced_rune_names: string[]; -} - -interface RunesOutputsByAddressArgs { - address: string; - network?: BitcoinNetworkModes; - sortBy?: 'output'; - order?: 'asc' | 'desc'; - offset?: number; - count?: number; - signal?: AbortSignal; -} - -interface RunesOutputsByAddressResponse { - block_height: number; - data: RunesOutputsByAddress[]; -} - -class BestinSlotApi { - url = BESTINSLOT_API_BASE_URL_MAINNET; - testnetUrl = BESTINSLOT_API_BASE_URL_TESTNET; - - private defaultOptions = { - headers: { - 'x-api-key': `${process.env.BESTINSLOT_API_KEY}`, - }, - }; - constructor(public configuration: Configuration) {} - - async getInscriptionsByTransactionId(id: string) { - const resp = await axios.get( - `${this.url}/inscription/in_transaction?tx_id=${id}`, - { - ...this.defaultOptions, - } - ); - - return resp.data; - } - - async getInscriptionById(id: string) { - const resp = await axios.get( - `${this.url}/inscription/single_info_id?inscription_id=${id}`, - { - ...this.defaultOptions, - } - ); - return resp.data; - } - - /* BRC-20 */ - async getBrc20Balances(address: string) { - const resp = await axios.get( - `${this.url}/brc20/wallet_balances?address=${address}`, - { - ...this.defaultOptions, - } - ); - return resp.data; - } - - async getBrc20TickerInfo(ticker: string) { - const resp = await axios.get( - `${this.url}/brc20/ticker_info?ticker=${ticker}`, - { - ...this.defaultOptions, - } - ); - return resp.data; - } - - /* RUNES */ - async getRunesWalletBalances(address: string, network: BitcoinNetworkModes) { - const baseUrl = network === 'mainnet' ? this.url : this.testnetUrl; - const resp = await axios.get( - `${baseUrl}/runes/wallet_balances?address=${address}`, - { ...this.defaultOptions } - ); - return resp.data.data; - } - - async getRunesTickerInfo(runeName: string, network: BitcoinNetworkModes) { - const baseUrl = network === 'mainnet' ? this.url : this.testnetUrl; - const resp = await axios.get( - `${baseUrl}/runes/ticker_info?rune_name=${runeName}`, - { ...this.defaultOptions } - ); - return resp.data.data; - } - - async getRunesBatchOutputsInfo(outputs: string[], network: BitcoinNetworkModes) { - const baseUrl = network === 'mainnet' ? this.url : this.testnetUrl; - - const resp = await axios.post( - `${baseUrl}/runes/batch_output_info`, - { queries: outputs }, - { ...this.defaultOptions } - ); - return resp.data.data; - } - - /** - * @see https://docs.bestinslot.xyz/reference/api-reference/ordinals-and-brc-20-and-runes-and-bitmap-v3-api-mainnet+testnet/runes#runes-wallet-valid-outputs - */ - async getRunesOutputsByAddress({ - address, - network = 'mainnet', - sortBy = 'output', - order = 'asc', - offset = 0, - count = 100, - signal, - }: RunesOutputsByAddressArgs) { - const baseUrl = network === 'mainnet' ? this.url : this.testnetUrl; - const queryParams = new URLSearchParams({ - address, - sort_by: sortBy, - order, - offset: offset.toString(), - count: count.toString(), - }); - - const resp = await axios.get( - `${baseUrl}/runes/wallet_valid_outputs?${queryParams}`, - { ...this.defaultOptions, signal } - ); - return resp.data.data; - } -} - -class AddressApi { - rateLimiter: PQueue; - constructor(public configuration: Configuration) { - this.rateLimiter = getBlockstreamRatelimiter(this.configuration.baseUrl); - } - - async getTransactionsByAddress(address: string, signal?: AbortSignal) { - const resp = await this.rateLimiter.add( - () => - axios.get(`${this.configuration.baseUrl}/address/${address}/txs`, { - signal, - }), - { signal, throwOnTimeout: true } - ); - return resp.data; - } - - async getUtxosByAddress(address: string, signal?: AbortSignal): Promise { - const resp = await this.rateLimiter.add( - () => - axios.get(`${this.configuration.baseUrl}/address/${address}/utxo`, { - signal, - }), - { signal, priority: 1, throwOnTimeout: true } - ); - return resp.data.sort((a, b) => a.vout - b.vout); - } -} - -interface FeeEstimateEarnApiResponse { - name: string; - height: number; - hash: string; - time: string; - latest_url: string; - previous_hash: string; - previous_url: string; - peer_count: number; - unconfirmed_count: number; - high_fee_per_kb: number; - medium_fee_per_kb: number; - low_fee_per_kb: number; - last_fork_height: number; - last_fork_hash: string; -} -interface FeeEstimateMempoolSpaceApiResponse { - fastestFee: number; - halfHourFee: number; - hourFee: number; - economyFee: number; - minimumFee: number; -} - -interface FeeResult { - fast: number; - medium: number; - slow: number; -} - -class FeeEstimatesApi { - constructor(public configuration: Configuration) {} - - async getFeeEstimatesFromBlockcypherApi(network: 'main' | 'test3'): Promise { - // https://www.blockcypher.com/dev/bitcoin/#restful-resources - const resp = await axios.get( - `https://api.blockcypher.com/v1/btc/${network}` - ); - const { low_fee_per_kb, medium_fee_per_kb, high_fee_per_kb } = resp.data; - // These fees are in satoshis per kb - return { - slow: low_fee_per_kb / 1000, - medium: medium_fee_per_kb / 1000, - fast: high_fee_per_kb / 1000, - }; - } - - async getFeeEstimatesFromMempoolSpaceApi(): Promise { - const resp = await axios.get( - `https://mempool.space/api/v1/fees/recommended` - ); - const { fastestFee, halfHourFee, hourFee } = resp.data; - return { - slow: hourFee, - medium: halfHourFee, - fast: fastestFee, - }; - } -} - -class TransactionsApi { - constructor(public configuration: Configuration) {} - - async getBitcoinTransaction(txid: string) { - const resp = await axios.get(`${this.configuration.baseUrl}/tx/${txid}`); - return resp.data; - } - - async getBitcoinTransactionHex(txid: string) { - const resp = await axios.get(`${this.configuration.baseUrl}/tx/${txid}/hex`, { - responseType: 'text', - }); - return resp.data; - } - - async broadcastTransaction(tx: string) { - // TODO: refactor to use `axios` - // https://github.com/leather-wallet/extension/issues/4521 - // eslint-disable-next-line no-restricted-globals - return fetch(`${this.configuration.baseUrl}/tx`, { - method: 'POST', - body: tx, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }); - } -} - -export class BitcoinClient { - configuration: Configuration; - addressApi: AddressApi; - feeEstimatesApi: FeeEstimatesApi; - transactionsApi: TransactionsApi; - bestinSlotApi: BestinSlotApi; - - constructor(baseUrl: string) { - this.configuration = new Configuration(baseUrl); - this.addressApi = new AddressApi(this.configuration); - this.feeEstimatesApi = new FeeEstimatesApi(this.configuration); - this.transactionsApi = new TransactionsApi(this.configuration); - this.bestinSlotApi = new BestinSlotApi(this.configuration); - } -} diff --git a/src/app/query/bitcoin/blockstream-rate-limiter.ts b/src/app/query/bitcoin/blockstream-rate-limiter.ts deleted file mode 100644 index 5e548fc2415..00000000000 --- a/src/app/query/bitcoin/blockstream-rate-limiter.ts +++ /dev/null @@ -1,18 +0,0 @@ -import PQueue from 'p-queue'; - -import { BITCOIN_API_BASE_URL_TESTNET } from '@shared/constants'; - -const blockstreamMainnetApiLimiter = new PQueue({ - interval: 5000, - intervalCap: 30, -}); - -const blockstreamTestnetApiLimiter = new PQueue({ - interval: 5000, - intervalCap: 30, -}); - -export function getBlockstreamRatelimiter(url: string) { - if (url.includes(BITCOIN_API_BASE_URL_TESTNET)) return blockstreamTestnetApiLimiter; - return blockstreamMainnetApiLimiter; -} diff --git a/src/app/query/bitcoin/contract/send-accepted-bitcoin-contract-offer.ts b/src/app/query/bitcoin/contract/send-accepted-bitcoin-contract-offer.ts deleted file mode 100644 index 8df410306b7..00000000000 --- a/src/app/query/bitcoin/contract/send-accepted-bitcoin-contract-offer.ts +++ /dev/null @@ -1,21 +0,0 @@ -export async function sendAcceptedBitcoinContractOfferToProtocolWallet( - acceptedBitcoinContractOffer: string, - counterpartyWalletURL: string -) { - // TODO: refactor to use `axios` - // https://github.com/leather-wallet/extension/issues/4521 - // eslint-disable-next-line no-restricted-globals - const response = await fetch(`${counterpartyWalletURL}/offer/accept`, { - method: 'put', - body: JSON.stringify({ - acceptMessage: acceptedBitcoinContractOffer, - }), - headers: { 'Content-Type': 'application/json' }, - }); - - if (!response.ok) { - throw new Error('The counterparty was unable to process the Bitcoin Contract.'); - } - - return response.json(); -} diff --git a/src/app/query/bitcoin/fees/fee-estimates.hooks.ts b/src/app/query/bitcoin/fees/fee-estimates.hooks.ts deleted file mode 100644 index 63d613a8ad4..00000000000 --- a/src/app/query/bitcoin/fees/fee-estimates.hooks.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { logger } from '@shared/logger'; - -import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; -import { calculateMeanAverage } from '@app/common/math/calculate-averages'; -import { initBigNumber } from '@app/common/math/helpers'; -import { isFulfilled, isRejected } from '@app/common/utils'; - -import { useGetAllBitcoinFeeEstimatesQuery } from './fee-estimates.query'; - -export function useAverageBitcoinFeeRates() { - const analytics = useAnalytics(); - return useGetAllBitcoinFeeEstimatesQuery({ - onError: err => logger.error('Error getting all apis bitcoin fee estimates', { err }), - select(feeEstimates) { - if (feeEstimates.every(isRejected)) { - void analytics.track('error_using_fallback_bitcoin_fees'); - return { - fastestFee: initBigNumber(15), - halfHourFee: initBigNumber(10), - hourFee: initBigNumber(5), - }; - } - - const fees = feeEstimates.filter(isFulfilled).map(result => result.value); - - return { - fastestFee: calculateMeanAverage(fees.map(fee => fee.fast)).decimalPlaces(0), - halfHourFee: calculateMeanAverage(fees.map(fee => fee.medium)).decimalPlaces(0), - hourFee: calculateMeanAverage(fees.map(fee => fee.slow)).decimalPlaces(0), - }; - }, - }); -} diff --git a/src/app/query/bitcoin/fees/fee-estimates.query.ts b/src/app/query/bitcoin/fees/fee-estimates.query.ts deleted file mode 100644 index bc87277a868..00000000000 --- a/src/app/query/bitcoin/fees/fee-estimates.query.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { BitcoinNetworkModes } from '@shared/constants'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -import { BitcoinClient } from '../bitcoin-client'; - -function fetchAllBitcoinFeeEstimates(client: BitcoinClient, network: BitcoinNetworkModes) { - return async () => - network === 'mainnet' - ? Promise.allSettled([ - client.feeEstimatesApi.getFeeEstimatesFromMempoolSpaceApi(), - client.feeEstimatesApi.getFeeEstimatesFromBlockcypherApi('main'), - ]) - : // Using `allSettled` so we can add more testnet apis to the array - Promise.allSettled([client.feeEstimatesApi.getFeeEstimatesFromBlockcypherApi('test3')]); -} - -type FetchAllBitcoinFeeEstimatesResp = Awaited< - ReturnType> ->; - -export function useGetAllBitcoinFeeEstimatesQuery< - T extends unknown = FetchAllBitcoinFeeEstimatesResp, ->(options?: AppUseQueryConfig) { - const client = useBitcoinClient(); - const network = useCurrentNetwork(); - - return useQuery({ - queryKey: ['average-bitcoin-fee-estimates', network.chain.bitcoin.bitcoinNetwork], - queryFn: fetchAllBitcoinFeeEstimates(client, network.chain.bitcoin.bitcoinNetwork), - refetchInterval: 2000 * 60, - ...options, - }); -} diff --git a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts index ba39c62dd25..f3a63a55941 100644 --- a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts +++ b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts @@ -1,27 +1,28 @@ import type { Brc20CryptoAssetInfo } from '@leather-wallet/models'; +import { + createBrc20TransferInscription, + encodeBrc20TransferInscription, + useAverageBitcoinFeeRates, + useCalculateBitcoinFiatValue, + useConfigOrdinalsbot, + useGetBrc20TokensQuery, + useOrdinalsbotClient, +} from '@leather-wallet/query'; import BigNumber from 'bignumber.js'; import { createMarketData, createMarketPair } from '@shared/models/market.model'; import { createMoney } from '@shared/models/money.model'; import { unitToFractionalUnit } from '@app/common/money/unit-conversion'; -import { useGetBrc20TokensQuery } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.query'; -import { useCalculateBitcoinFiatValue } from '@app/query/common/market-data/market-data.hooks'; import { createCryptoAssetBalance } from '@app/query/common/models'; -import { useConfigOrdinalsbot } from '@app/query/common/remote-config/remote-config.query'; import { isFetchedWithSuccess } from '@app/query/query-config'; import { useAppDispatch } from '@app/store'; import { useCurrentAccountIndex } from '@app/store/accounts/account'; +import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; import { brc20TransferInitiated } from '@app/store/ordinals/ordinals.slice'; -import { useAverageBitcoinFeeRates } from '../../fees/fee-estimates.hooks'; -import { useOrdinalsbotClient } from '../../ordinalsbot-client'; -import { - createBrc20TransferInscription, - encodeBrc20TransferInscription, -} from './brc20-tokens.utils'; - // ts-unused-exports:disable-next-line export function useBrc20FeatureFlag() { const currentNetwork = useCurrentNetwork(); @@ -98,7 +99,13 @@ function createBrc20CryptoAssetInfo(decimals: number, ticker: string): Brc20Cryp export function useBrc20Tokens() { const calculateBitcoinFiatValue = useCalculateBitcoinFiatValue(); - const result = useGetBrc20TokensQuery(); + const createTaprootSigner = useCurrentAccountTaprootSigner(); + const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); + + const result = useGetBrc20TokensQuery({ + createTaprootSigner, + nativeSegwitAddress: nativeSegwitSigner.address, + }); if (!isFetchedWithSuccess(result)) return []; diff --git a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts deleted file mode 100644 index 36d685caac1..00000000000 --- a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { useCallback, useEffect } from 'react'; - -import { useInfiniteQuery } from '@tanstack/react-query'; - -import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; -import { createNumArrayOfRange } from '@app/common/utils'; -import { QueryPrefixes } from '@app/query/query-prefixes'; -import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -const addressesSimultaneousFetchLimit = 3; -const stopSearchAfterNumberAddressesWithoutBrc20Tokens = 3; - -export function useGetBrc20TokensQuery() { - const network = useCurrentNetwork(); - const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); - const currentNsBitcoinAddress = nativeSegwitSigner.address; - const createSigner = useCurrentAccountTaprootSigner(); - const analytics = useAnalytics(); - const client = useBitcoinClient(); - - if (!createSigner) throw new Error('No signer'); - - const getNextTaprootAddressBatch = useCallback( - (fromIndex: number, toIndex: number) => { - return createNumArrayOfRange(fromIndex, toIndex - 1).map(num => { - const address = createSigner(num).address; - return address; - }); - }, - [createSigner] - ); - - const query = useInfiniteQuery({ - queryKey: [QueryPrefixes.GetBrc20Tokens, currentNsBitcoinAddress, network.id], - async queryFn({ pageParam }) { - const fromIndex: number = pageParam?.fromIndex ?? 0; - let addressesWithoutTokens = pageParam?.addressesWithoutTokens ?? 0; - - const addressesData = getNextTaprootAddressBatch( - fromIndex, - fromIndex + addressesSimultaneousFetchLimit - ); - - if (fromIndex === 0) { - addressesData.unshift(currentNsBitcoinAddress); - } - - const brc20TokensPromises = addressesData.map(async address => { - const brc20Tokens = await client.bestinSlotApi.getBrc20Balances(address); - - const tickerPromises = await Promise.all( - brc20Tokens.data.map(token => { - return client.bestinSlotApi.getBrc20TickerInfo(token.ticker); - }) - ); - - return brc20Tokens.data.map((token, index) => { - return { - balance: token, - holderAddress: address, - info: tickerPromises[index].data, - }; - }); - }); - - const brc20Tokens = await Promise.all(brc20TokensPromises); - addressesWithoutTokens += brc20Tokens.filter(tokens => tokens.length === 0).length; - - return { - addressesWithoutTokens, - brc20Tokens, - fromIndex, - }; - }, - getNextPageParam(prevInscriptionQuery) { - const { fromIndex, brc20Tokens, addressesWithoutTokens } = prevInscriptionQuery; - - if (addressesWithoutTokens >= stopSearchAfterNumberAddressesWithoutBrc20Tokens) { - return undefined; - } - - return { - fromIndex: fromIndex + addressesSimultaneousFetchLimit, - addressesWithoutTokens, - brc20Tokens, - }; - }, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: true, - staleTime: 5 * 60 * 1000, - }); - - // Auto-trigger next request - useEffect(() => { - if (query.hasNextPage) { - void query.fetchNextPage(); - } - }, [query, query.data]); - - useEffect(() => { - const brc20AcrossAddressesCount = query.data?.pages.reduce((acc, page) => { - return acc + page.brc20Tokens.flatMap(item => item).length; - }, 0); - - if (!query.hasNextPage && brc20AcrossAddressesCount && brc20AcrossAddressesCount > 0) { - void analytics.identify({ - brc20_across_addresses_count: brc20AcrossAddressesCount, - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [analytics, query.hasNextPage]); - return query; -} diff --git a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.utils.spec.ts b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.utils.spec.ts deleted file mode 100644 index d916ffc4a03..00000000000 --- a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.utils.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { encodeBrc20TransferInscription } from './brc20-tokens.utils'; - -describe(encodeBrc20TransferInscription.name, () => { - test('that it encodes the BRC-20 transfer correctly', () => { - const result = encodeBrc20TransferInscription({ - p: 'brc-20', - op: 'transfer', - tick: 'anas', - amt: '1', - }); - expect(result.payload).toEqual( - 'data:plain/text;base64,eyJwIjoiYnJjLTIwIiwib3AiOiJ0cmFuc2ZlciIsInRpY2siOiJhbmFzIiwiYW10IjoiMSJ9' - ); - expect(result.size).toEqual(54); - }); -}); diff --git a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.utils.ts b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.utils.ts deleted file mode 100644 index 7143a084b0c..00000000000 --- a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.utils.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { utf8ToBytes } from '@noble/hashes/utils'; -import { base64 } from '@scure/base'; -import BigNumber from 'bignumber.js'; -import * as yup from 'yup'; - -// ts-unused-exports:disable-next-line -export interface Brc20TransferInscription { - p: 'brc-20'; - op: 'transfer'; - tick: string; - amt: string; -} - -const brc20TransferInscriptionSchema = yup.object({ - p: yup.string().required().equals(['brc-20']), - op: yup.string().required().equals(['transfer']), - tick: yup - .string() - .required() - .test(value => value.length > 0 && value.length <= 4), - amt: yup - .string() - .required() - .test(value => new BigNumber(value).isFinite()), -}); - -function validateBrc20TransferInscription(val: unknown): val is Brc20TransferInscription { - return brc20TransferInscriptionSchema.isValidSync(val); -} - -// ts-unused-exports:disable-next-line -export function createBrc20TransferInscription(tick: string, amt: number) { - const transfer: Brc20TransferInscription = { - p: 'brc-20', - op: 'transfer', - tick, - amt: amt.toString(), - }; - - if (!validateBrc20TransferInscription(transfer)) throw new Error('Invalid transfer inscription'); - - return transfer; -} - -export function encodeBrc20TransferInscription(transfer: Brc20TransferInscription) { - const transferBytes = utf8ToBytes(JSON.stringify(transfer)); - const encodedTransfer = base64.encode(transferBytes); - const size = transferBytes.length; - const dataUriPrefix = 'data:plain/text;base64,'; - return { payload: dataUriPrefix + encodedTransfer, transferBytes, size }; -} diff --git a/src/app/query/bitcoin/ordinals/brc20/use-check-order-status.ts b/src/app/query/bitcoin/ordinals/brc20/use-check-order-status.ts index 42fcf1d3d18..026648c1e0c 100644 --- a/src/app/query/bitcoin/ordinals/brc20/use-check-order-status.ts +++ b/src/app/query/bitcoin/ordinals/brc20/use-check-order-status.ts @@ -1,3 +1,4 @@ +import { fetchInscripionById, useOrdinalsbotClient } from '@leather-wallet/query'; import { useQueries } from '@tanstack/react-query'; import { useAppDispatch } from '@app/store'; @@ -9,9 +10,6 @@ import { usePendingBrc20TransferEntities, } from '@app/store/ordinals/ordinals.slice'; -import { useOrdinalsbotClient } from '../../ordinalsbot-client'; -import { fetchInscripionById } from '../inscription-by-id.query'; - export function useCheckOrderStatuses(ids: string[]) { const ordinalsbotClient = useOrdinalsbotClient(); diff --git a/src/app/query/bitcoin/ordinals/inscription-by-id.query.ts b/src/app/query/bitcoin/ordinals/inscription-by-id.query.ts deleted file mode 100644 index da73c8d918b..00000000000 --- a/src/app/query/bitcoin/ordinals/inscription-by-id.query.ts +++ /dev/null @@ -1,8 +0,0 @@ -import axios from 'axios'; - -import { HIRO_INSCRIPTIONS_API_URL } from '@shared/constants'; -import { InscriptionResponseItem } from '@shared/models/inscription.model'; - -export async function fetchInscripionById(id: string) { - return axios.get(`${HIRO_INSCRIPTIONS_API_URL}/${id}`); -} diff --git a/src/app/query/bitcoin/ordinals/inscription-text-content.query.ts b/src/app/query/bitcoin/ordinals/inscription-text-content.query.ts deleted file mode 100644 index 893dae93451..00000000000 --- a/src/app/query/bitcoin/ordinals/inscription-text-content.query.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; - -import { QueryPrefixes } from '@app/query/query-prefixes'; -import { useHiroApiRateLimiter } from '@app/query/stacks/hiro-rate-limiter'; - -async function getInscriptionTextContent(src: string) { - const res = await axios.get(src, { responseType: 'text' }); - return res.data; -} - -export function useInscriptionTextContentQuery(contentSrc: string) { - const limiter = useHiroApiRateLimiter(); - return useQuery( - [QueryPrefixes.OrdinalTextContent, contentSrc], - async () => { - return limiter.add(() => getInscriptionTextContent(contentSrc), { - throwOnTimeout: true, - }); - }, - { - cacheTime: Infinity, - staleTime: Infinity, - } - ); -} diff --git a/src/app/query/bitcoin/ordinals/inscription.hooks.ts b/src/app/query/bitcoin/ordinals/inscription.hooks.ts deleted file mode 100644 index b5236c378ba..00000000000 --- a/src/app/query/bitcoin/ordinals/inscription.hooks.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { HIRO_INSCRIPTIONS_API_URL } from '@shared/constants'; -import { - Inscription, - SupportedInscription, - whenInscriptionType, -} from '@shared/models/inscription.model'; - -import { useGetInscriptionQuery } from './inscription.query'; - -export function createInscriptionInfoUrl(id: string) { - return `https://ordinals.hiro.so/inscription/${id}`; -} - -function createIframePreviewUrl(id: string) { - return `https://ordinals.com/preview/${id}`; -} - -export function convertInscriptionToSupportedInscriptionType(inscription: Inscription) { - const title = `Inscription ${inscription.number}`; - return whenInscriptionType(inscription.content_type, { - audio: () => ({ - infoUrl: createInscriptionInfoUrl(inscription.id), - src: createIframePreviewUrl(inscription.id), - title, - type: 'audio', - ...inscription, - }), - html: () => ({ - infoUrl: createInscriptionInfoUrl(inscription.id), - src: createIframePreviewUrl(inscription.id), - title, - type: 'html', - ...inscription, - }), - image: () => ({ - infoUrl: createInscriptionInfoUrl(inscription.id), - src: `${HIRO_INSCRIPTIONS_API_URL}/${inscription.id}/content`, - title, - type: 'image', - ...inscription, - }), - svg: () => ({ - infoUrl: createInscriptionInfoUrl(inscription.id), - src: createIframePreviewUrl(inscription.id), - title, - type: 'svg', - ...inscription, - }), - text: () => ({ - contentSrc: `${HIRO_INSCRIPTIONS_API_URL}/${inscription.id}/content`, - infoUrl: createInscriptionInfoUrl(inscription.id), - title, - type: 'text', - ...inscription, - }), - video: () => ({ - infoUrl: createInscriptionInfoUrl(inscription.id), - src: createIframePreviewUrl(inscription.id), - title, - type: 'video', - ...inscription, - }), - gltf: () => ({ - infoUrl: createInscriptionInfoUrl(inscription.id), - src: createIframePreviewUrl(inscription.id), - title, - type: 'gltf', - ...inscription, - }), - other: () => ({ - infoUrl: createInscriptionInfoUrl(inscription.id), - title, - type: 'other', - ...inscription, - }), - }); -} - -export function useInscription(id: string) { - return useGetInscriptionQuery(id, { - select: resp => convertInscriptionToSupportedInscriptionType(resp), - }); -} diff --git a/src/app/query/bitcoin/ordinals/inscription.query.ts b/src/app/query/bitcoin/ordinals/inscription.query.ts deleted file mode 100644 index 5e77ae41ccc..00000000000 --- a/src/app/query/bitcoin/ordinals/inscription.query.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; - -import { HIRO_INSCRIPTIONS_API_URL } from '@shared/constants'; -import { Inscription } from '@shared/models/inscription.model'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { QueryPrefixes } from '@app/query/query-prefixes'; - -const inscriptionQueryOptions = { - staleTime: Infinity, - cacheTime: Infinity, -} as const; - -/** - * @param path - inscription/:inscription_id - */ -function fetchInscription() { - return async (id: string) => { - const res = await axios.get(`${HIRO_INSCRIPTIONS_API_URL}/${id}`); - return res.data as Inscription; - }; -} - -type FetchInscriptionResp = Awaited>>; - -export function useGetInscriptionQuery( - id: string, - options?: AppUseQueryConfig -) { - return useQuery({ - enabled: !!id, - queryKey: [QueryPrefixes.InscriptionMetadata, id], - queryFn: () => fetchInscription()(id), - ...inscriptionQueryOptions, - ...options, - }); -} diff --git a/src/app/query/bitcoin/ordinals/inscriptions-by-param.query.ts b/src/app/query/bitcoin/ordinals/inscriptions-by-param.query.ts deleted file mode 100644 index 253b080a5b5..00000000000 --- a/src/app/query/bitcoin/ordinals/inscriptions-by-param.query.ts +++ /dev/null @@ -1,65 +0,0 @@ -import * as btc from '@scure/btc-signer'; -import { bytesToHex } from '@stacks/common'; -import { useQueries, useQuery } from '@tanstack/react-query'; -import axios from 'axios'; - -import { HIRO_INSCRIPTIONS_API_URL } from '@shared/constants'; -import { Paginated } from '@shared/models/api-types'; -import { Inscription } from '@shared/models/inscription.model'; -import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; - -import { AppUseQueryConfig } from '@app/query/query-config'; - -type FetchInscriptionResp = Awaited>>; - -function fetchInscriptionsByParam() { - return async (param: string) => { - const res = await axios.get(`${HIRO_INSCRIPTIONS_API_URL}?${param}`); - return res.data as Paginated; - }; -} - -const weekInMs = 1000 * 60 * 60 * 24 * 7; -const inscriptionsByOutputQueryOptions = { - staleTime: weekInMs, - cacheTime: weekInMs, -} as const; - -export function useGetInscriptionsByOutputQuery( - transaction: BitcoinTx, - options?: AppUseQueryConfig -) { - const inputsLength = transaction.vin.length; - const index = inputsLength === 1 ? 0 : inputsLength - 2; - const isPending = !transaction.status.confirmed; - const id = isPending ? transaction.vin[index].txid : transaction.txid; - const param = `output=${id}:${index}`; - - return useQuery({ - enabled: !!param, - queryKey: ['inscription-by-param', isPending, param], - queryFn: () => fetchInscriptionsByParam()(param), - ...inscriptionsByOutputQueryOptions, - ...options, - }); -} - -const inscriptionsByOutputQueriesOptions = { - cacheTime: Infinity, - staleTime: 15 * 60 * 1000, - refetchOnWindowFocus: false, -} as const; - -export function useGetInscriptionsByOutputQueries(inputs: btc.TransactionInput[]) { - return useQueries({ - queries: inputs.map(input => { - const param = input.txid ? `output=${bytesToHex(input.txid)}:${input.index}` : ''; - - return { - queryKey: ['inscription-by-param', false, param], - queryFn: () => fetchInscriptionsByParam()(param), - ...inscriptionsByOutputQueriesOptions, - }; - }), - }); -} diff --git a/src/app/query/bitcoin/ordinals/inscriptions.hooks.ts b/src/app/query/bitcoin/ordinals/inscriptions.hooks.ts deleted file mode 100644 index bdf404ea130..00000000000 --- a/src/app/query/bitcoin/ordinals/inscriptions.hooks.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useCallback } from 'react'; - -import { InscriptionResponseItem } from '@shared/models/inscription.model'; -import { isUndefined } from '@shared/utils'; - -import { useGetInscriptionsInfiniteQuery } from './inscriptions.query'; - -interface FindInscriptionsOnUtxoArgs { - index: number; - inscriptions: InscriptionResponseItem[]; - txId: string; -} -export function findInscriptionsOnUtxo({ index, inscriptions, txId }: FindInscriptionsOnUtxoArgs) { - return inscriptions?.filter(inscription => { - return `${txId}:${index.toString()}` === inscription.output; - }); -} - -export function useNumberOfInscriptionsOnUtxo() { - const query = useGetInscriptionsInfiniteQuery(); - const inscriptions = query.data?.pages?.flatMap(page => page.inscriptions); - - return useCallback( - (txId: string, index: number) => { - if (isUndefined(inscriptions)) return 0; - const foundInscriptionsOnUtxo = findInscriptionsOnUtxo({ index, inscriptions, txId }); - if (isUndefined(foundInscriptionsOnUtxo)) return 0; - return foundInscriptionsOnUtxo?.length; - }, - [inscriptions] - ); -} diff --git a/src/app/query/bitcoin/ordinals/inscriptions.query.ts b/src/app/query/bitcoin/ordinals/inscriptions.query.ts deleted file mode 100644 index f00af79d71d..00000000000 --- a/src/app/query/bitcoin/ordinals/inscriptions.query.ts +++ /dev/null @@ -1,279 +0,0 @@ -import { useCallback, useEffect } from 'react'; - -import { useInfiniteQuery } from '@tanstack/react-query'; -import axios from 'axios'; - -import { HIRO_INSCRIPTIONS_API_URL } from '@shared/constants'; -import { getTaprootAddress } from '@shared/crypto/bitcoin/bitcoin.utils'; -import { InscriptionResponseItem } from '@shared/models/inscription.model'; -import { ensureArray } from '@shared/utils'; - -import { createNumArrayOfRange } from '@app/common/utils'; -import { QueryPrefixes } from '@app/query/query-prefixes'; -import { useHiroApiRateLimiter } from '@app/query/stacks/hiro-rate-limiter'; -import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { useCurrentTaprootAccount } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -const stopSearchAfterNumberAddressesWithoutOrdinals = 5; -const addressesSimultaneousFetchLimit = 5; - -// Hiro API max limit = 60 -const inscriptionsLazyLoadLimit = 20; - -interface InfiniteQueryPageParam { - pageParam?: { - fromIndex: number; - offset: number; - addressesWithoutOrdinalsNum: number; - addressesMap: Record; - }; - signal?: AbortSignal; -} - -interface InscriptionsQueryResponse { - results: InscriptionResponseItem[]; - limit: number; - offset: number; - total: number; -} - -interface FetchInscriptionsArgs { - addresses: string | string[]; - offset?: number; - limit?: number; - signal?: AbortSignal; -} - -async function fetchInscriptions({ - addresses, - offset = 0, - limit = 60, - signal, -}: FetchInscriptionsArgs) { - const params = new URLSearchParams(); - ensureArray(addresses).forEach(address => params.append('address', address)); - params.append('limit', limit.toString()); - params.append('offset', offset.toString()); - - const res = await axios.get( - `${HIRO_INSCRIPTIONS_API_URL}?${params.toString()}`, - { - signal, - } - ); - - return res.data; -} - -/** - * Returns all inscriptions for the user's current account - */ -export function useGetInscriptionsInfiniteQuery() { - const network = useCurrentNetwork(); - const account = useCurrentTaprootAccount(); - const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); - const currentBitcoinAddress = nativeSegwitSigner.address; - const limiter = useHiroApiRateLimiter(); - - const getTaprootAddressData = useCallback( - (fromIndex: number, toIndex: number) => { - return createNumArrayOfRange(fromIndex, toIndex - 1).reduce( - (acc: Record, i: number) => { - const address = getTaprootAddress({ - index: i, - keychain: account?.keychain, - network: network.chain.bitcoin.bitcoinNetwork, - }); - acc[address] = i; - return acc; - }, - {} - ); - }, - [account, network.chain.bitcoin.bitcoinNetwork] - ); - - const query = useInfiniteQuery({ - queryKey: [QueryPrefixes.GetInscriptions, currentBitcoinAddress, network.id], - async queryFn({ pageParam, signal }: InfiniteQueryPageParam) { - const responsesArr: InscriptionsQueryResponse[] = []; - let fromIndex = pageParam?.fromIndex ?? 0; - let addressesWithoutOrdinalsNum = pageParam?.addressesWithoutOrdinalsNum ?? 0; - let addressesMap = - pageParam?.addressesMap ?? - getTaprootAddressData(fromIndex, fromIndex + addressesSimultaneousFetchLimit); - - let addressesData = getTaprootAddressData( - fromIndex, - fromIndex + addressesSimultaneousFetchLimit - ); - - let offset = pageParam?.offset || 0; - // Loop through addresses until we reach the limit, or until we find an address with many inscriptions - while (addressesWithoutOrdinalsNum < stopSearchAfterNumberAddressesWithoutOrdinals) { - const addresses = Object.keys(addressesData); - - // add native segwit address to 1 chunk of addresses - if (fromIndex === 0) { - addresses.unshift(currentBitcoinAddress); - } - const response = await limiter.add( - () => - fetchInscriptions({ - addresses, - offset, - limit: inscriptionsLazyLoadLimit, - }), - { - signal, - throwOnTimeout: true, - } - ); - - responsesArr.push(response); - - // stop loop to dynamically fetch inscriptions from 1 address if there are many inscriptions - if (response.total > inscriptionsLazyLoadLimit && response.results.length > 0) { - addressesWithoutOrdinalsNum = 0; - break; - } - - // case when we fetched all inscriptions from address with many inscriptions - if (response.total === 0 && response.offset > 0) { - offset = 0; - } - - fromIndex += addressesSimultaneousFetchLimit; - addressesWithoutOrdinalsNum += addressesSimultaneousFetchLimit; - - addressesData = getTaprootAddressData( - fromIndex, - fromIndex + addressesSimultaneousFetchLimit - ); - - // add new addresses to the map - addressesMap = { - ...addressesMap, - ...addressesData, - }; - if (response.results.length > 0) { - addressesWithoutOrdinalsNum = 0; - } - } - - const results = responsesArr.flatMap(response => response.results); - - // get offset and total from the last response - const total = responsesArr[responsesArr.length - 1]?.total; - - return { - offset, - total, - stopNextFetch: addressesWithoutOrdinalsNum >= stopSearchAfterNumberAddressesWithoutOrdinals, - inscriptions: results.map(inscription => { - let addressIndex = addressesMap[inscription.address]; - if (inscription.address === currentBitcoinAddress) { - addressIndex = 0; - } - return { - addressIndex, - ...inscription, - }; - }), - fromIndex, - addressesWithoutOrdinalsNum, - addressesMap, - }; - }, - getNextPageParam(prevInscriptionsQuery) { - const { offset, total, stopNextFetch, fromIndex, addressesWithoutOrdinalsNum, addressesMap } = - prevInscriptionsQuery; - if (stopNextFetch) return undefined; - - // calculate offset for next fetch - let calculatedOffset = offset + inscriptionsLazyLoadLimit; - - if (offset + inscriptionsLazyLoadLimit > total) { - calculatedOffset = offset + (total - offset); - } - - return { - offset: calculatedOffset, - total, - fromIndex, - addressesWithoutOrdinalsNum, - addressesMap, - }; - }, - staleTime: 3 * 60 * 1000, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: true, - }); - - return query; -} - -export function useInscriptionsByAddressQuery(address: string) { - const network = useCurrentNetwork(); - const limiter = useHiroApiRateLimiter(); - - const query = useInfiniteQuery({ - queryKey: [QueryPrefixes.InscriptionsByAddress, network.id, address], - async queryFn({ pageParam = 0, signal }) { - return limiter.add( - () => - fetchInscriptions({ - addresses: address, - offset: pageParam, - signal, - }), - { priority: 1, signal, throwOnTimeout: true } - ); - }, - getNextPageParam(prevInscriptionsQuery) { - if (prevInscriptionsQuery.offset >= prevInscriptionsQuery.total) return undefined; - return prevInscriptionsQuery.offset + 60; - }, - refetchOnMount: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, - staleTime: 3 * 60 * 1000, - }); - - // Auto-trigger next request - useEffect(() => { - void query.fetchNextPage(); - }, [query, query.data]); - - return query; -} - -// In lieu of reliable API, we scrape HTML from the Ordinals.com explorer and -// parses the HTML -// Example: -// https://ordinals.com/output/758bd2703dd9f0a2df31c2898aecf6caba05a906498c9bc076947f9fc4d8f081:0 -async function getOrdinalsComTxOutputHtmlPage(id: string, index: number) { - const resp = await axios.get(`https://ordinals-explorer.generative.xyz/output/${id}:${index}`); - return new DOMParser().parseFromString(resp.data, 'text/html'); -} - -export async function getNumberOfInscriptionOnUtxoUsingOrdinalsCom(id: string, index: number) { - const utxoPage = await getOrdinalsComTxOutputHtmlPage(id, index); - - // First content on page is inscrption section header and thumbnail of - // inscrptions in utxo - const firstSectionHeader = utxoPage.querySelector('dl > dt:first-child'); - if (!firstSectionHeader) - throw new Error('If no element matching this selector is found, something is wrong'); - - const firstHeaderText = firstSectionHeader.textContent; - const thumbnailCount = utxoPage.querySelectorAll('dl > dt:first-child + dd.thumbnails a').length; - - // Were HTML to page to change, thumbnailCount alone would dangerously return - // zero 0, hence additional check that inscrption header is also missing - if (thumbnailCount === 0 && firstHeaderText !== 'inscriptions') return 0; - - return thumbnailCount; -} diff --git a/src/app/query/bitcoin/ordinals/inscriptions.spec.ts b/src/app/query/bitcoin/ordinals/inscriptions.spec.ts deleted file mode 100644 index f1f31963598..00000000000 --- a/src/app/query/bitcoin/ordinals/inscriptions.spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { mockInscriptionsList } from '@tests/mocks/mock-inscriptions'; - -import { findInscriptionsOnUtxo } from './inscriptions.hooks'; - -describe(findInscriptionsOnUtxo, () => { - test('that it finds an inscription on a utxo', () => { - const foundInscriptions = findInscriptionsOnUtxo({ - index: 0, - inscriptions: mockInscriptionsList, - txId: '58d44000884f0ba4cdcbeb1ac082e6c802d300c16b0d3251738e8cf6a57397ce', - }); - expect(foundInscriptions.length).toEqual(1); - }); -}); diff --git a/src/app/query/bitcoin/ordinalsbot-client.ts b/src/app/query/bitcoin/ordinalsbot-client.ts deleted file mode 100644 index b72b219d502..00000000000 --- a/src/app/query/bitcoin/ordinalsbot-client.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { useMemo } from 'react'; - -import axios from 'axios'; -import urlJoin from 'url-join'; - -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -import { useConfigOrdinalsbot } from '../common/remote-config/remote-config.query'; - -interface InscriptionOrderSuccessResponse { - status: 'ok'; - receiveAddress: string; - texts: string[]; - lowPostage: boolean; - fee: number; - files: { - dataURL: string; - name: string; - size: number; - }[]; - charge: { - id: string; - description: string; - desc_hash: boolean; - created_at: number; - status: string; - amount: number; - callback_url: string; - success_url: any; - hosted_checkout_url: string; - order_id: any; - currency: string; - source_fiat_value: number; - fiat_value: number; - auto_settle: boolean; - notif_email: any; - address: string; - chain_invoice: { - address: string; - }; - uri: string; - ttl: number; - lightning_invoice: { - expires_at: number; - payreq: string; - }; - }; - id: string; - chainFee: number; - serviceFee: number; - baseFee: number; - orderType: string; - createdAt: { - '.sv': string; - }; -} - -interface InscriptionOrderArgs { - file: string; - receiveAddress: string; - // in vbytes - fee: number; - // in bytes - size: number; - name: string; -} - -interface OrderStatusSuccessResponse { - status: 'success'; - paid: boolean; - underpaid: boolean; - expired: boolean; - id: string; - state: string; - charge: { - id: string; - description: string; - desc_hash: boolean; - created_at: number; - status: string; - amount: number; - callback_url: string; - success_url: any; - hosted_checkout_url: string; - order_id: any; - currency: string; - source_fiat_value: number; - fiat_value: number; - auto_settle: boolean; - notif_email: any; - address: string; - chain_invoice: { - address: string; - }; - uri: string; - ttl: number; - lightning_invoice: { - expires_at: number; - payreq: string; - }; - }; - files: { - dataURL: string; - name: string; - size: number; - type: string; - url: string; - processing: boolean; - - tx?: { - commit: string; - fees: number; - inscription: string; - reveal: string; - }; - }[]; - - sent: string; -} - -interface OrderStatusErrorResponse { - status: 'error'; - error: string; -} - -class OrdinalsbotClient { - constructor(readonly baseUrl: string) {} - - async isAvailable() { - return axios.get<{ status: string }>(urlJoin(this.baseUrl, 'status')); - } - - async order({ receiveAddress, file, fee, size, name }: InscriptionOrderArgs) { - return axios.post(urlJoin(this.baseUrl, 'order'), { - receiveAddress, - files: [{ dataURL: file, size, name, type: 'plain/text' }], - fee, - lowPostage: true, - }); - } - - async orderStatus(id: string) { - return axios.get( - urlJoin(this.baseUrl, 'order'), - { - params: { id }, - } - ); - } -} - -function useOrdinalsbotApiUrl() { - const currentNetwork = useCurrentNetwork(); - const ordinalsbotConfig = useConfigOrdinalsbot(); - - if (currentNetwork.chain.bitcoin.bitcoinNetwork === 'mainnet') - return ordinalsbotConfig.mainnetApiUrl; - return ordinalsbotConfig.signetApiUrl; -} - -export function useOrdinalsbotClient() { - const apiUrl = useOrdinalsbotApiUrl(); - return useMemo(() => new OrdinalsbotClient(apiUrl), [apiUrl]); -} diff --git a/src/app/query/bitcoin/runes/runes-outputs-by-address.query.ts b/src/app/query/bitcoin/runes/runes-outputs-by-address.query.ts deleted file mode 100644 index 4290192b97c..00000000000 --- a/src/app/query/bitcoin/runes/runes-outputs-by-address.query.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import type { AppUseQueryConfig } from '@app/query/query-config'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -import type { RunesOutputsByAddress } from '../bitcoin-client'; -import { useRunesEnabled } from './runes.hooks'; - -const queryOptions = { staleTime: 5 * 60 * 1000 }; - -export function useGetRunesOutputsByAddressQuery( - address: string, - options?: AppUseQueryConfig -) { - const client = useBitcoinClient(); - const runesEnabled = useRunesEnabled(); - const network = useCurrentNetwork(); - - return useQuery({ - enabled: !!address && runesEnabled, - queryKey: ['runes-outputs-by-address', address], - queryFn: ({ signal }) => - client.bestinSlotApi.getRunesOutputsByAddress({ - address, - network: network.chain.bitcoin.bitcoinNetwork, - signal, - }), - ...queryOptions, - ...options, - }); -} diff --git a/src/app/query/bitcoin/runes/runes-ticker-info.query.ts b/src/app/query/bitcoin/runes/runes-ticker-info.query.ts deleted file mode 100644 index 068f6a094c9..00000000000 --- a/src/app/query/bitcoin/runes/runes-ticker-info.query.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { useQueries } from '@tanstack/react-query'; - -import { isDefined } from '@shared/utils'; - -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -import type { RuneBalance, RuneTickerInfo } from '../bitcoin-client'; -import { useRunesEnabled } from './runes.hooks'; -import { createRuneCryptoAssetDetails } from './runes.utils'; - -const queryOptions = { staleTime: 5 * 60 * 1000 }; - -export function useGetRunesTickerInfoQuery(runesBalances: RuneBalance[]) { - const client = useBitcoinClient(); - const network = useCurrentNetwork(); - const runesEnabled = useRunesEnabled(); - - return useQueries({ - queries: runesBalances.map(runeBalance => { - return { - enabled: isDefined(runeBalance) && runesEnabled, - queryKey: ['runes-ticker-info', runeBalance.rune_name], - queryFn: () => - client.bestinSlotApi.getRunesTickerInfo( - runeBalance.rune_name, - network.chain.bitcoin.bitcoinNetwork - ), - select: (resp: RuneTickerInfo) => createRuneCryptoAssetDetails(runeBalance, resp), - ...queryOptions, - }; - }), - }); -} diff --git a/src/app/query/bitcoin/runes/runes-wallet-balances.query.ts b/src/app/query/bitcoin/runes/runes-wallet-balances.query.ts deleted file mode 100644 index c33fcf5536b..00000000000 --- a/src/app/query/bitcoin/runes/runes-wallet-balances.query.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useQueries } from '@tanstack/react-query'; - -import { useConfigRunesEnabled } from '@app/query/common/remote-config/remote-config.query'; -import type { AppUseQueryConfig } from '@app/query/query-config'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -import type { RuneBalance } from '../bitcoin-client'; - -const queryOptions = { staleTime: 5 * 60 * 1000 }; - -export function useGetRunesWalletBalancesByAddressesQuery( - addresses: string[], - options?: AppUseQueryConfig -) { - const client = useBitcoinClient(); - const network = useCurrentNetwork(); - const runesEnabled = useConfigRunesEnabled(); - - return useQueries({ - queries: addresses.map(address => { - return { - enabled: !!address && (network.chain.bitcoin.bitcoinNetwork === 'testnet' || runesEnabled), - queryKey: ['runes-wallet-balances', address], - queryFn: () => - client.bestinSlotApi.getRunesWalletBalances( - address, - network.chain.bitcoin.bitcoinNetwork - ), - ...queryOptions, - ...options, - }; - }), - }); -} diff --git a/src/app/query/bitcoin/runes/runes.hooks.ts b/src/app/query/bitcoin/runes/runes.hooks.ts deleted file mode 100644 index 2a6f88ecff7..00000000000 --- a/src/app/query/bitcoin/runes/runes.hooks.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useMemo } from 'react'; - -import { isDefined } from '@shared/utils'; - -import { useConfigRunesEnabled } from '@app/query/common/remote-config/remote-config.query'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -import { useGetRunesOutputsByAddressQuery } from './runes-outputs-by-address.query'; -import { useGetRunesTickerInfoQuery } from './runes-ticker-info.query'; -import { useGetRunesWalletBalancesByAddressesQuery } from './runes-wallet-balances.query'; - -export function useRunesEnabled() { - const runesEnabled = useConfigRunesEnabled(); - const network = useCurrentNetwork(); - - return runesEnabled || network.chain.bitcoin.bitcoinNetwork === 'testnet'; -} - -export function useRuneTokens(addresses: string[]) { - const runesBalances = useGetRunesWalletBalancesByAddressesQuery(addresses) - .flatMap(query => query.data) - .filter(isDefined); - - const results = useGetRunesTickerInfoQuery(runesBalances); - - 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 = results.some(query => query.isInitialLoading); - const runes = results.map(query => query.data).filter(isDefined); - - return { isInitialLoading, runes }; - }, [results]); -} - -export function useRunesOutputsByAddress(address: string) { - return useGetRunesOutputsByAddressQuery(address); -} diff --git a/src/app/query/bitcoin/runes/runes.utils.ts b/src/app/query/bitcoin/runes/runes.utils.ts deleted file mode 100644 index 7849c3bd300..00000000000 --- a/src/app/query/bitcoin/runes/runes.utils.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { RuneCryptoAssetInfo } from '@leather-wallet/models'; - -import { createMoney } from '@shared/models/money.model'; - -import { createCryptoAssetBalance } from '@app/query/common/models'; - -import type { RuneBalance, RuneTickerInfo } from '../bitcoin-client'; - -const defaultRunesSymbol = 'ยค'; - -function createRuneCryptoAssetInfo(tickerInfo: RuneTickerInfo): RuneCryptoAssetInfo { - return { - decimals: tickerInfo.decimals, - hasMemo: false, - name: 'rune', - runeName: tickerInfo.rune_name, - spacedRuneName: tickerInfo.spaced_rune_name, - symbol: tickerInfo.symbol ?? defaultRunesSymbol, - }; -} - -export function createRuneCryptoAssetDetails(runeBalance: RuneBalance, tickerInfo: RuneTickerInfo) { - return { - balance: createCryptoAssetBalance( - createMoney(Number(runeBalance.total_balance), tickerInfo.rune_name, tickerInfo.decimals) - ), - info: createRuneCryptoAssetInfo(tickerInfo), - }; -} diff --git a/src/app/query/bitcoin/stamps/stamps-by-address.hooks.ts b/src/app/query/bitcoin/stamps/stamps-by-address.hooks.ts deleted file mode 100644 index aec9723c0d0..00000000000 --- a/src/app/query/bitcoin/stamps/stamps-by-address.hooks.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { Src20CryptoAssetInfo } from '@leather-wallet/models'; -import BigNumber from 'bignumber.js'; - -import { createMoney } from '@shared/models/money.model'; - -import { createCryptoAssetBalance } from '@app/query/common/models'; - -import { type Src20Token, useStampsByAddressQuery } from './stamps-by-address.query'; - -export function useStampsByAddress(address: string) { - return useStampsByAddressQuery(address, { - select: resp => resp.data?.stamps, - }); -} - -function createSrc20CryptoAssetInfo(src20: Src20Token): Src20CryptoAssetInfo { - return { - decimals: 0, - hasMemo: false, - id: src20.id ?? '', - name: 'src-20', - symbol: src20.tick, - }; -} - -export function useSrc20TokensByAddress(address: string) { - return useStampsByAddressQuery(address, { - select: resp => - resp.data.src20.map(token => ({ - balance: createCryptoAssetBalance( - createMoney(new BigNumber(token.amt ?? 0), token.tick, 0) - ), - info: createSrc20CryptoAssetInfo(token), - })), - }); -} diff --git a/src/app/query/bitcoin/stamps/stamps-by-address.query.ts b/src/app/query/bitcoin/stamps/stamps-by-address.query.ts deleted file mode 100644 index 9c9dced493b..00000000000 --- a/src/app/query/bitcoin/stamps/stamps-by-address.query.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; -import { ZodError, z } from 'zod'; - -import { analytics } from '@shared/utils/analytics'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { QueryPrefixes } from '@app/query/query-prefixes'; - -const stampSchema = z.object({ - stamp: z.number(), - block_index: z.number().optional(), - cpid: z.string().optional(), - asset_longname: z.string().optional(), - creator: z.string(), - divisible: z.number(), - keyburn: z.number().optional(), - locked: z.number(), - message_index: z.number().optional(), - stamp_base64: z.string(), - stamp_mimetype: z.string(), - stamp_url: z.string(), - supply: z.number(), - timestamp: z.string().optional(), - tx_hash: z.string(), - tx_index: z.number().optional(), - src_data: z.string().optional(), - ident: z.string().optional(), - creator_name: z.string().optional().nullable(), - stamp_gen: z.string().optional(), - stamp_hash: z.string().optional(), - is_btc_stamp: z.number().nullable().optional(), - is_reissue: z.number().optional(), - file_hash: z.string().optional(), -}); - -export type Stamp = z.infer; - -const src20TokenSchema = z.object({ - id: z.string().optional(), - address: z.string(), - cpid: z.string().optional(), - p: z.string(), - tick: z.string(), - amt: z.string().optional(), - block_time: z.string(), - last_update: z.number(), -}); - -export type Src20Token = z.infer; - -const stampsByAdressSchema = z.object({ - page: z.number(), - limit: z.number(), - totalPages: z.number(), - total: z.number(), - last_block: z.number().optional(), - btc: z.object({ - address: z.string(), - balance: z.number(), - txCount: z.number(), - unconfirmedBalance: z.number(), - unconfirmedTxCount: z.number(), - }), - data: z.object({ - stamps: z.array(stampSchema), - src20: z.array(src20TokenSchema), - }), -}); - -type StampsByAddressQueryResponse = z.infer; - -/** - * @see https://stampchain.io/docs#/default/get_api_v2_balance__address_ - */ -async function fetchStampsByAddress(address: string): Promise { - const resp = await axios.get( - `https://stampchain.io/api/v2/balance/${address}` - ); - try { - return stampsByAdressSchema.parse(resp.data); - } catch (e) { - if (e instanceof ZodError) void analytics.track('schema_fail', { ...e }); - throw e; - } -} - -type FetchStampsByAddressResp = Awaited>; - -export function useStampsByAddressQuery( - address: string, - options?: AppUseQueryConfig -) { - return useQuery({ - queryKey: [QueryPrefixes.StampsByAddress, address], - queryFn: () => fetchStampsByAddress(address), - ...options, - refetchOnWindowFocus: false, - }); -} diff --git a/src/app/query/bitcoin/transaction/transaction.query.ts b/src/app/query/bitcoin/transaction/transaction.query.ts deleted file mode 100644 index 329e68c1a45..00000000000 --- a/src/app/query/bitcoin/transaction/transaction.query.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { UseQueryResult, useQueries, useQuery } from '@tanstack/react-query'; - -import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; - -import { AppUseQueryConfig } from '@app/query/query-config'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; - -import { BitcoinClient } from '../bitcoin-client'; - -function fetchBitcoinTransaction(client: BitcoinClient) { - return async (txid: string) => { - return client.transactionsApi.getBitcoinTransaction(txid); - }; -} - -type FetchBitcoinTransaction = Awaited>>; - -// ts-unused-exports:disable-next-line -export function useGetBitcoinTransaction( - txid: string, - options?: AppUseQueryConfig -) { - const client = useBitcoinClient(); - return useQuery({ - queryKey: ['bitcoin-transaction', txid], - queryFn: () => fetchBitcoinTransaction(client)(txid), - staleTime: Infinity, - refetchOnWindowFocus: false, - refetchOnMount: false, - refetchOnReconnect: false, - ...options, - }); -} - -const queryOptions = { - cacheTime: Infinity, - staleTime: 15 * 60 * 1000, - refetchOnWindowFocus: false, -} as const; - -// ts-unused-exports:disable-next-line -export function useGetBitcoinTransactionQueries(txids: string[]): UseQueryResult[] { - const client = useBitcoinClient(); - - return useQueries({ - queries: txids.map(txid => { - return { - queryKey: ['bitcoin-transaction', txid], - queryFn: () => fetchBitcoinTransaction(client)(txid), - ...queryOptions, - }; - }), - }); -} diff --git a/src/app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction.ts b/src/app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction.ts deleted file mode 100644 index 46b73f18ef6..00000000000 --- a/src/app/query/bitcoin/transaction/use-bitcoin-broadcast-transaction.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { useCallback, useState } from 'react'; - -import * as btc from '@scure/btc-signer'; - -import { decodeBitcoinTx } from '@shared/crypto/bitcoin/bitcoin.utils'; -import { delay, isError } from '@shared/utils'; - -import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; - -import { filterOutIntentionalUtxoSpend, useCheckUnspendableUtxos } from './use-check-utxos'; - -interface BroadcastCallbackArgs { - tx: string; - skipSpendableCheckUtxoIds?: string[] | 'all'; - delayTime?: number; - onSuccess?(txid: string): void; - onError?(error: Error): void; - onFinally?(): void; -} - -export function useBitcoinBroadcastTransaction() { - const client = useBitcoinClient(); - const [isBroadcasting, setIsBroadcasting] = useState(false); - const analytics = useAnalytics(); - const { checkIfUtxosListIncludesInscribed } = useCheckUnspendableUtxos(); - - const broadcastTx = useCallback( - async ({ - tx, - onSuccess, - onError, - onFinally, - skipSpendableCheckUtxoIds = [], - delayTime = 700, - }: BroadcastCallbackArgs) => { - try { - if (skipSpendableCheckUtxoIds !== 'all') { - // Filter out intentional spend inscription txid from the check list - const utxos: btc.TransactionInput[] = filterOutIntentionalUtxoSpend({ - inputs: decodeBitcoinTx(tx).inputs, - intentionalSpendUtxoIds: skipSpendableCheckUtxoIds, - }); - - const hasInscribedUtxos = await checkIfUtxosListIncludesInscribed(utxos); - - if (hasInscribedUtxos) { - return; - } - } - - setIsBroadcasting(true); - const resp = await client.transactionsApi.broadcastTransaction(tx); - // simulate slower broadcast time to allow mempool refresh - await delay(delayTime); - if (!resp.ok) throw new Error(await resp.text()); - const txid = await resp.text(); - onSuccess?.(txid); - return txid; - } catch (e) { - onError?.(e as Error); - void analytics.track('error_broadcasting_transaction', { - errorName: isError(e) ? e.name : 'unknown', - errorMsg: isError(e) ? e.message : 'unknown', - }); - return; - } finally { - setIsBroadcasting(false); - onFinally?.(); - return; - } - }, - [analytics, checkIfUtxosListIncludesInscribed, client] - ); - - return { broadcastTx, isBroadcasting }; -} diff --git a/src/app/query/bitcoin/transaction/use-check-utxos.ts b/src/app/query/bitcoin/transaction/use-check-utxos.ts deleted file mode 100644 index 09fe190bdab..00000000000 --- a/src/app/query/bitcoin/transaction/use-check-utxos.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { useCallback, useState } from 'react'; - -import * as btc from '@scure/btc-signer'; -import { bytesToHex } from '@stacks/common'; - -import { IS_TEST_ENV } from '@shared/environment'; -import { isUndefined } from '@shared/utils'; - -import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; -import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; -import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; - -import type { BitcoinClient } from '../bitcoin-client'; -import { getNumberOfInscriptionOnUtxoUsingOrdinalsCom } from '../ordinals/inscriptions.query'; - -class PreventTransactionError extends Error { - constructor(message: string) { - super(message); - this.name = 'PreventTransactionError'; - } -} - -interface FilterOutIntentionalInscriptionsSpendArgs { - inputs: btc.TransactionInput[]; - intentionalSpendUtxoIds: string[]; -} - -export function filterOutIntentionalUtxoSpend({ - inputs, - intentionalSpendUtxoIds, -}: FilterOutIntentionalInscriptionsSpendArgs): btc.TransactionInput[] { - return inputs.filter(input => { - if (!input.txid) throw new Error('Transaction ID is missing in the input'); - const inputTxid = bytesToHex(input.txid); - - return intentionalSpendUtxoIds.every(id => { - return id !== inputTxid; - }); - }); -} - -interface CheckInscribedUtxosByBestinslotArgs { - inputs: btc.TransactionInput[]; - txids: string[]; - client: BitcoinClient; -} - -async function checkInscribedUtxosByBestinslot({ - inputs, - txids, - client, -}: CheckInscribedUtxosByBestinslotArgs): Promise { - /** - * @description Get the list of inscriptions moving on a transaction - * @see https://docs.bestinslot.xyz/reference/api-reference/ordinals-and-brc-20-and-bitmap-v3-api-mainnet+testnet/inscriptions - */ - const inscriptionIdsList = await Promise.all( - txids.map(id => client.bestinSlotApi.getInscriptionsByTransactionId(id)) - ); - - const inscriptionIds = inscriptionIdsList.flatMap(inscription => - inscription.data.map(data => data.inscription_id) - ); - - const inscriptionsList = await Promise.all( - inscriptionIds.map(id => client.bestinSlotApi.getInscriptionById(id)) - ); - - const hasInscribedUtxos = inscriptionsList.some(resp => { - return inputs.some(input => { - if (!input.txid) throw new Error('Transaction ID is missing in the input'); - const idWithIndex = `${bytesToHex(input.txid)}:${input.index}`; - return resp.data.satpoint.includes(idWithIndex); - }); - }); - - return hasInscribedUtxos; -} - -export function useCheckUnspendableUtxos(blockTxAction?: () => void) { - const client = useBitcoinClient(); - const analytics = useAnalytics(); - const [isLoading, setIsLoading] = useState(false); - const { isTestnet } = useCurrentNetworkState(); - - const preventTransaction = useCallback(() => { - if (blockTxAction) return blockTxAction(); - throw new PreventTransactionError( - 'Transaction is prevented due to inscribed utxos in the transaction. Please contact support for more information.' - ); - }, [blockTxAction]); - - const checkIfUtxosListIncludesInscribed = useCallback( - async (inputs: btc.TransactionInput[]) => { - setIsLoading(true); - const txids = inputs.map(input => { - if (!input.txid) throw new Error('Transaction ID is missing in the input'); - return bytesToHex(input.txid); - }); - - try { - // no need to check for inscriptions on testnet - if (isTestnet && !IS_TEST_ENV) { - return false; - } - - if (txids.length === 0) { - throw new Error('Utxos list cannot be empty'); - } - - const ordinalsComResponses = await Promise.all( - txids.map(async (id, index) => { - const inscriptionIndex = inputs[index].index; - if (isUndefined(inscriptionIndex)) { - throw new Error('Inscription index is missing in the input'); - } - const num = await getNumberOfInscriptionOnUtxoUsingOrdinalsCom(id, inscriptionIndex); - return num > 0; - }) - ); - - const hasInscribedUtxo = ordinalsComResponses.some(resp => resp); - - // if there are inscribed utxos in the transaction, and no error => prevent the transaction - if (hasInscribedUtxo) { - void analytics.track('utxos_includes_inscribed_one', { - txids, - inputs, - }); - preventTransaction(); - return true; - } - - // if there are no inscribed utxos in the transaction => allow the transaction - return false; - } catch (e) { - if (e instanceof PreventTransactionError) { - throw e; - } - - void analytics.track('error_checking_utxos_from_ordinalscom', { - txids, - inputs, - }); - - const hasInscribedUtxo = await checkInscribedUtxosByBestinslot({ - inputs, - txids, - client, - }); - - if (hasInscribedUtxo) { - void analytics.track('utxos_includes_inscribed_one', { - txids, - inputs, - }); - preventTransaction(); - return true; - } - - // if there are no inscribed utxos in the transaction => allow the transaction - return false; - } finally { - setIsLoading(false); - } - }, - [analytics, client, isTestnet, preventTransaction] - ); - - return { - checkIfUtxosListIncludesInscribed, - isLoading, - }; -} diff --git a/src/app/query/common/market-data/market-data.hooks.ts b/src/app/query/common/market-data/market-data.hooks.ts deleted file mode 100644 index 1c4d63a9ca3..00000000000 --- a/src/app/query/common/market-data/market-data.hooks.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { useCallback, useMemo } from 'react'; - -import BigNumber from 'bignumber.js'; - -import { CryptoCurrencies } from '@shared/models/currencies.model'; -import { MarketData, createMarketData, createMarketPair } from '@shared/models/market.model'; -import { Money, createMoney, currencyDecimalsMap } from '@shared/models/money.model'; - -import { calculateMeanAverage } from '@app/common/math/calculate-averages'; -import { - baseCurrencyAmountInQuote, - convertAmountToFractionalUnit, -} from '@app/common/money/calculate-money'; - -import { - selectBinanceUsdPrice, - useBinanceMarketDataQuery, -} from './vendors/binance-market-data.query'; -import { - selectCoincapUsdPrice, - useCoincapMarketDataQuery, -} from './vendors/coincap-market-data.query'; -import { - selectCoingeckoUsdPrice, - useCoinGeckoMarketDataQuery, -} from './vendors/coingecko-market-data.query'; - -interface MarketDataVendorWithPriceSelector { - result: unknown; - selector(v: unknown): string | number; -} -function pullPriceDataFromAvailableResponses(responses: MarketDataVendorWithPriceSelector[]) { - return responses - .filter(({ result }) => !!result) - .map(({ result, selector: priceSelector }) => priceSelector(result)) - .map(val => new BigNumber(val)) - .map(val => convertAmountToFractionalUnit(val, currencyDecimalsMap.USD)); -} - -export function useCryptoCurrencyMarketDataMeanAverage(currency: CryptoCurrencies): MarketData { - const { data: coingecko } = useCoinGeckoMarketDataQuery(currency); - const { data: coincap } = useCoincapMarketDataQuery(currency); - const { data: binance } = useBinanceMarketDataQuery(currency); - - return useMemo(() => { - const priceData = pullPriceDataFromAvailableResponses([ - { result: coingecko, selector: selectCoingeckoUsdPrice }, - { result: coincap, selector: selectCoincapUsdPrice }, - { result: binance, selector: selectBinanceUsdPrice }, - ]); - const meanPrice = calculateMeanAverage(priceData); - - return createMarketData(createMarketPair(currency, 'USD'), createMoney(meanPrice, 'USD')); - }, [binance, coincap, coingecko, currency]); -} - -export function useCalculateBitcoinFiatValue() { - const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); - - return useCallback( - (value: Money) => baseCurrencyAmountInQuote(value, btcMarketData), - [btcMarketData] - ); -} diff --git a/src/app/query/common/market-data/market-data.query.ts b/src/app/query/common/market-data/market-data.query.ts deleted file mode 100644 index ae8dc1eac23..00000000000 --- a/src/app/query/common/market-data/market-data.query.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { UseQueryOptions } from '@tanstack/react-query'; - -export const marketDataQueryOptions: UseQueryOptions = { - staleTime: 1000 * 60, - refetchOnMount: false, - refetchInterval: false, - refetchOnReconnect: false, - refetchOnWindowFocus: false, - refetchIntervalInBackground: false, -} as const; diff --git a/src/app/query/common/market-data/vendors/binance-market-data.query.ts b/src/app/query/common/market-data/vendors/binance-market-data.query.ts deleted file mode 100644 index 7a0bccb2e67..00000000000 --- a/src/app/query/common/market-data/vendors/binance-market-data.query.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; - -import { CryptoCurrencies } from '@shared/models/currencies.model'; - -import { marketDataQueryOptions } from '../market-data.query'; - -async function fetchBinanceMarketData(currency: CryptoCurrencies) { - const resp = await axios.get( - `https://api1.binance.com/api/v3/ticker/price?symbol=${currency}USDT` - ); - return resp.data; -} - -export function selectBinanceUsdPrice(resp: any) { - return resp?.price; -} - -export function useBinanceMarketDataQuery(currency: CryptoCurrencies) { - return useQuery({ - queryFn: () => fetchBinanceMarketData(currency), - queryKey: [`binance-market-data-${currency}`], - ...marketDataQueryOptions, - }); -} diff --git a/src/app/query/common/market-data/vendors/coincap-market-data.query.ts b/src/app/query/common/market-data/vendors/coincap-market-data.query.ts deleted file mode 100644 index d1a1c1a3b77..00000000000 --- a/src/app/query/common/market-data/vendors/coincap-market-data.query.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; - -import { CryptoCurrencies } from '@shared/models/currencies.model'; - -import { marketDataQueryOptions } from '../market-data.query'; - -const currencyNameMap: Record = { - BTC: 'bitcoin', - STX: 'stacks', -}; - -async function fetchCoincapMarketData(currency: CryptoCurrencies) { - const resp = await axios.get(`https://api.coincap.io/v2/assets/${currencyNameMap[currency]}`); - return resp.data; -} - -export function selectCoincapUsdPrice(resp: any) { - return resp?.data?.priceUsd; -} -export function useCoincapMarketDataQuery(currency: CryptoCurrencies) { - return useQuery({ - queryFn: () => fetchCoincapMarketData(currency), - queryKey: [`coincap-market-data-${currency}`], - ...marketDataQueryOptions, - }); -} diff --git a/src/app/query/common/market-data/vendors/coingecko-market-data.query.ts b/src/app/query/common/market-data/vendors/coingecko-market-data.query.ts deleted file mode 100644 index 1021db95000..00000000000 --- a/src/app/query/common/market-data/vendors/coingecko-market-data.query.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; - -import { CryptoCurrencies } from '@shared/models/currencies.model'; - -import { marketDataQueryOptions } from '../market-data.query'; - -const currencyNameMap: Record = { - BTC: 'bitcoin', - STX: 'blockstack', -}; - -async function fetchCoingeckoMarketData(currency: CryptoCurrencies) { - const resp = await axios.get( - `https://api.coingecko.com/api/v3/simple/price?ids=${currencyNameMap[currency]}&vs_currencies=usd` - ); - return resp.data; -} - -export function selectCoingeckoUsdPrice(resp: any) { - return (Object.values(resp)[0] as any)?.usd; -} - -export function useCoinGeckoMarketDataQuery(currency: CryptoCurrencies) { - return useQuery({ - queryFn: () => fetchCoingeckoMarketData(currency), - queryKey: [`coin-gecko-market-data-${currency}`], - ...marketDataQueryOptions, - }); -} 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 6d33fa10219..de7920a4a27 100644 --- a/src/app/query/common/remote-config/remote-config.query.ts +++ b/src/app/query/common/remote-config/remote-config.query.ts @@ -1,111 +1,24 @@ -import { useQuery } from '@tanstack/react-query'; -import axios from 'axios'; -import get from 'lodash.get'; - -import { GITHUB_ORG, GITHUB_REPO } from '@shared/constants'; -import { BRANCH_NAME, IS_DEV_ENV, IS_TEST_ENV, WALLET_ENVIRONMENT } from '@shared/environment'; -import { createMoney } from '@shared/models/money.model'; -import { isUndefined } from '@shared/utils'; +import { useRemoteConfig } from '@leather-wallet/query'; import { useWalletType } from '@app/common/use-wallet-type'; import { useHasCurrentBitcoinAccount } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; -import localConfig from '../../../../../config/wallet-config.json'; - -export interface HiroMessage { - id: string; - title: string; - text: string; - img?: string; - imgWidth?: string; - purpose: 'error' | 'info' | 'warning'; - publishedAt: string; - dismissible: boolean; - chainTarget: 'all' | 'mainnet' | 'testnet'; - learnMoreUrl?: string; - learnMoreText?: string; -} - -export enum AvailableRegions { - InsideUsa = 'inside-usa-only', - OutsideUsa = 'outside-usa-only', - Global = 'global', -} - -export interface ActiveFiatProvider { - availableRegions: AvailableRegions; - enabled: boolean; - hasFastCheckoutProcess: boolean; - hasTradingFees: boolean; - name: string; -} - -interface FeeEstimationsConfig { - maxValues?: number[]; - maxValuesEnabled?: boolean; - minValues?: number[]; - minValuesEnabled?: boolean; -} - -interface RemoteConfig { - messages: any; - activeFiatProviders?: Record; - bitcoinEnabled: boolean; - bitcoinSendEnabled: boolean; - feeEstimationsMinMax?: FeeEstimationsConfig; - nftMetadataEnabled: boolean; - ordinalsbot: { - integrationEnabled: boolean; - mainnetApiUrl: string; - signetApiUrl: string; - }; -} - -// TODO: BRANCH_NAME is not working here for config changes on PR branches -// Playwright tests fail with config changes not on main -const defaultBranch = IS_DEV_ENV || WALLET_ENVIRONMENT === 'testing' ? 'dev' : 'main'; -const githubWalletConfigRawUrl = `https://raw.githubusercontent.com/${GITHUB_ORG}/${GITHUB_REPO}/${ - BRANCH_NAME || defaultBranch -}/config/wallet-config.json`; - -async function fetchLeatherMessages(): Promise { - if (WALLET_ENVIRONMENT !== 'production' || IS_TEST_ENV) return localConfig as RemoteConfig; - const resp = await axios.get(githubWalletConfigRawUrl); - return resp.data; -} - -function useRemoteConfig() { - const { data } = useQuery(['walletConfig'], fetchLeatherMessages, { - // As we're fetching from Github, a third-party, we want - // to avoid any unnecessary stress on their services, so - // we use quite slow stale/retry times - staleTime: 1000 * 60 * 10, - retryDelay: 1000 * 60, - }); - return data; -} - -export function useRemoteLeatherMessages(): HiroMessage[] { - const config = useRemoteConfig(); - return get(config, 'messages.global', []); -} - -export function useActiveFiatProviders() { - const config = useRemoteConfig(); - if (!config?.activeFiatProviders) return {} as Record; - - return Object.fromEntries( - Object.entries(config.activeFiatProviders).filter(([_, provider]) => provider.enabled) - ); -} - -export function useHasFiatProviders() { - const activeProviders = useActiveFiatProviders(); - return ( - activeProviders && - Object.keys(activeProviders).reduce((acc, key) => activeProviders[key].enabled || acc, false) - ); -} +export { + HiroMessage, + AvailableRegions, + ActiveFiatProvider, + useRemoteLeatherMessages, + useActiveFiatProviders, + useHasFiatProviders, + useRecoverUninscribedTaprootUtxosFeatureEnabled, + useConfigFeeEstimationsMaxEnabled, + useConfigFeeEstimationsMaxValues, + useConfigFeeEstimationsMinEnabled, + useConfigFeeEstimationsMinValues, + useConfigNftMetadataEnabled, + useConfigRunesEnabled, + useConfigSwapEnabled, +} from '@leather-wallet/query'; export function useConfigBitcoinEnabled() { const { whenWallet } = useWalletType(); @@ -126,60 +39,3 @@ export function useConfigBitcoinSendEnabled() { software: config?.bitcoinSendEnabled ?? true, }); } - -export function useRecoverUninscribedTaprootUtxosFeatureEnabled() { - const config = useRemoteConfig(); - return get(config, 'recoverUninscribedTaprootUtxosFeatureEnabled', false); -} - -export function useConfigFeeEstimationsMaxEnabled() { - const config = useRemoteConfig(); - if (isUndefined(config) || isUndefined(config?.feeEstimationsMinMax)) return; - return config.feeEstimationsMinMax.maxValuesEnabled; -} - -export function useConfigFeeEstimationsMaxValues() { - const config = useRemoteConfig(); - if (typeof config?.feeEstimationsMinMax === 'undefined') return; - if (!config.feeEstimationsMinMax.maxValues) return; - if (!Array.isArray(config.feeEstimationsMinMax.maxValues)) return; - return config.feeEstimationsMinMax.maxValues.map(value => createMoney(value, 'STX')); -} - -export function useConfigFeeEstimationsMinEnabled() { - const config = useRemoteConfig(); - if (isUndefined(config) || isUndefined(config?.feeEstimationsMinMax)) return; - return config.feeEstimationsMinMax.minValuesEnabled; -} - -export function useConfigFeeEstimationsMinValues() { - const config = useRemoteConfig(); - if (typeof config?.feeEstimationsMinMax === 'undefined') return; - if (!config.feeEstimationsMinMax.minValues) return; - if (!Array.isArray(config.feeEstimationsMinMax.minValues)) return; - return config.feeEstimationsMinMax.minValues.map(value => createMoney(value, 'STX')); -} - -export function useConfigNftMetadataEnabled() { - const config = useRemoteConfig(); - return config?.nftMetadataEnabled ?? true; -} - -export function useConfigOrdinalsbot() { - const config = useRemoteConfig(); - return { - integrationEnabled: get(config, 'ordinalsbot.integrationEnabled', true), - mainnetApiUrl: get(config, 'ordinalsbot.mainnetApiUrl', 'https://api2.ordinalsbot.com'), - signetApiUrl: get(config, 'ordinalsbot.signetApiUrl', 'https://signet.ordinalsbot.com'), - }; -} - -export function useConfigRunesEnabled() { - const config = useRemoteConfig(); - return get(config, 'runesEnabled', false); -} - -export function useConfigSwapEnabled() { - const config = useRemoteConfig(); - return get(config, 'swapEnabled', false); -} diff --git a/src/app/store/common/api-clients.hooks.ts b/src/app/store/common/api-clients.hooks.ts index 33c3a59934c..6c401e486d4 100644 --- a/src/app/store/common/api-clients.hooks.ts +++ b/src/app/store/common/api-clients.hooks.ts @@ -1,14 +1,15 @@ import { useMemo } from 'react'; +import { bitcoinClient } from '@leather-wallet/query'; + import { wrappedFetch as fetchApi } from '@app/common/api/fetch-wrapper'; -import { BitcoinClient } from '@app/query/bitcoin/bitcoin-client'; import { StacksClient } from '@app/query/stacks/stacks-client'; import { useCurrentNetworkState } from '../networks/networks.hooks'; export function useBitcoinClient() { const network = useCurrentNetworkState(); - return new BitcoinClient(network.chain.bitcoin.bitcoinUrl); + return bitcoinClient(network.chain.bitcoin.bitcoinUrl); } export function useStacksClient() { diff --git a/src/app/store/software-keys/software-key.actions.ts b/src/app/store/software-keys/software-key.actions.ts index 6eed407f894..09fcdb85155 100644 --- a/src/app/store/software-keys/software-key.actions.ts +++ b/src/app/store/software-keys/software-key.actions.ts @@ -1,3 +1,4 @@ +import type { BitcoinClient } from '@leather-wallet/query'; import { AddressVersion } from '@stacks/transactions'; import { decryptMnemonic, encryptMnemonic } from '@shared/crypto/mnemonic-encryption'; @@ -8,7 +9,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 { BitcoinClient } from '@app/query/bitcoin/bitcoin-client'; import { fetchNamesForAddress } from '@app/query/stacks/bns/bns.utils'; import { StacksClient } from '@app/query/stacks/stacks-client'; import { AppThunk } from '@app/store'; diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 85401fe292b..c4c6d0fd730 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -75,16 +75,12 @@ export interface NetworkConfiguration { }; } -export const BESTINSLOT_API_BASE_URL_MAINNET = 'https://leatherapi.bestinslot.xyz/v3'; -export const BESTINSLOT_API_BASE_URL_TESTNET = 'https://leatherapi_testnet.bestinslot.xyz/v3'; - 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.testnet.hiro.so'; export const HIRO_API_BASE_URL_NAKAMOTO_TESTNET = 'https://api.nakamoto.testnet.hiro.so'; -export const HIRO_INSCRIPTIONS_API_URL = 'https://api.hiro.so/ordinals/v1/inscriptions'; export const BITCOIN_API_BASE_URL_MAINNET = 'https://blockstream.info/api'; export const BITCOIN_API_BASE_URL_TESTNET = 'https://blockstream.info/testnet/api'; diff --git a/src/shared/environment.ts b/src/shared/environment.ts index 6b7140de0fe..ad67e9ae243 100644 --- a/src/shared/environment.ts +++ b/src/shared/environment.ts @@ -3,6 +3,7 @@ export const BRANCH_NAME = process.env.GITHUB_HEAD_REF ?? process.env.BRANCH_NAM export const PR_NUMBER = process.env.PR_NUMBER; export const COINBASE_APP_ID = process.env.COINBASE_APP_ID ?? ''; export const COMMIT_SHA = process.env.COMMIT_SHA; +// ts-unused-exports:disable-next-line export const IS_DEV_ENV = process.env.WALLET_ENVIRONMENT === 'development'; export const IS_TEST_ENV = process.env.WALLET_ENVIRONMENT === 'testing'; export const MOONPAY_API_KEY = process.env.MOONPAY_API_KEY ?? ''; diff --git a/src/shared/models/form.model.ts b/src/shared/models/form.model.ts index 841ee224360..2555bfa5b07 100644 --- a/src/shared/models/form.model.ts +++ b/src/shared/models/form.model.ts @@ -1,4 +1,5 @@ -import { SupportedInscription } from './inscription.model'; +import type { Inscription } from '@leather-wallet/models'; + import type { Money } from './money.model'; export interface BitcoinSendFormValues { @@ -15,7 +16,7 @@ export interface BitcoinSendFormValues { export interface OrdinalSendFormValues { feeRate: number; recipient: string; - inscription: SupportedInscription; + inscription: Inscription; } // TODO: Remove assetId and optional symbol with legacy send form diff --git a/src/shared/models/inscription.model.ts b/src/shared/models/inscription.model.ts deleted file mode 100644 index 99e240f8de9..00000000000 --- a/src/shared/models/inscription.model.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { analytics } from '@shared/utils/analytics'; - -export interface InscriptionResponseItem { - address: string; - content_length: number; - content_type: string; - curse_type: string | null; - genesis_address: string; - genesis_block_hash: string; - genesis_block_height: number; - genesis_fee: string; - genesis_timestamp: number; - genesis_tx_id: string; - id: string; - location: string; - mime_type: string; - number: number; - offset: string; - output: string; - recursive: boolean; - recursion_refs: string | null; - sat_coinbase_height: number; - sat_ordinal: string; - sat_rarity: string; - timestamp: number; - tx_id: string; - value: string; -} - -export interface Inscription extends InscriptionResponseItem { - addressIndex: number; -} - -/** - * Inscriptions contain arbitrary data. When retrieving an inscription, it should be - * classified into one of the types below, indicating that the app can handle it - * appropriately and securely. Inscriptions of types not ready to be handled by the - * app should be classified as "other". - */ -const supportedInscriptionTypes = [ - 'audio', - 'html', - 'image', - 'svg', - 'text', - 'video', - 'gltf', - 'other', -] as const; - -type SupportedInscriptionType = (typeof supportedInscriptionTypes)[number]; - -interface BaseSupportedInscription extends Inscription { - /** - * The kind of inscription as classified by this app. Different kinds of inscriptions - * require different treatment (e.g., images vs documents). - */ - type: SupportedInscriptionType; - title: string; - infoUrl: string; -} - -interface AudioInscription extends BaseSupportedInscription { - type: 'audio'; - src: string; -} - -interface HtmlInscription extends BaseSupportedInscription { - type: 'html'; - src: string; -} - -interface ImageInscription extends BaseSupportedInscription { - type: 'image'; - src: string; -} - -interface SvgInscription extends BaseSupportedInscription { - type: 'svg'; - src: string; -} - -interface TextInscription extends BaseSupportedInscription { - type: 'text'; - contentSrc: string; -} - -interface VideoInscription extends BaseSupportedInscription { - type: 'video'; - src: string; -} - -interface GltfInscription extends BaseSupportedInscription { - type: 'gltf'; - src: string; -} - -interface OtherInscription extends BaseSupportedInscription { - type: 'other'; -} - -/** - * Information useful to the app about an inscription depending on its - * type. Typically, API data will be used to construct this object. This is - * *not* the result of any one API response. - */ -export type SupportedInscription = - | AudioInscription - | HtmlInscription - | ImageInscription - | SvgInscription - | TextInscription - | VideoInscription - | GltfInscription - | OtherInscription; - -export function whenInscriptionType( - mimeType: string, - branches: { [k in SupportedInscriptionType]?: () => T } -) { - if (mimeType.startsWith('audio/') && branches.audio) { - return branches.audio(); - } - - if (mimeType.startsWith('text/html') && branches.html) { - return branches.html(); - } - - if (mimeType.startsWith('image/svg') && branches.svg) { - return branches.svg(); - } - - if (mimeType.startsWith('image/') && branches.image) { - return branches.image(); - } - - if (mimeType.startsWith('text') && branches.text) { - return branches.text(); - } - - if (mimeType.startsWith('video/') && branches.video) { - return branches.video(); - } - - if (mimeType.startsWith('model/gltf') && branches.gltf) { - return branches.gltf(); - } - - if (branches.other) { - void analytics.track('unsupported_mime_type', { mimeType }); - return branches.other(); - } - - throw new Error('Unhandled inscription type'); -} diff --git a/src/shared/models/money.model.ts b/src/shared/models/money.model.ts index 62abccc5e34..e061a748328 100644 --- a/src/shared/models/money.model.ts +++ b/src/shared/models/money.model.ts @@ -15,7 +15,7 @@ export interface Money { // Units of `Money` should be declared in their smallest unit. Similar to // Rosetta, we model currencies with their respective resolution -export const currencyDecimalsMap = { +const currencyDecimalsMap = { BTC: BTC_DECIMALS, STX: STX_DECIMALS, USD: 2, diff --git a/src/shared/utils.ts b/src/shared/utils.ts index cedfcf76b4e..eb8f07e6496 100644 --- a/src/shared/utils.ts +++ b/src/shared/utils.ts @@ -63,10 +63,6 @@ export function whenNetwork(mode: NetworkModes) { return >(networkMap: T) => networkMap[mode] as T[NetworkModes]; } -export function isEmptyArray(data: unknown[]) { - return data.length === 0; -} - export const defaultWalletKeyId = 'default'; export function closeWindow() { diff --git a/tests/mocks/mock-inscriptions.ts b/tests/mocks/mock-inscriptions.ts index 3f5e5eb510e..3bdd23dd2bc 100644 --- a/tests/mocks/mock-inscriptions.ts +++ b/tests/mocks/mock-inscriptions.ts @@ -1,8 +1,7 @@ -import { Inscription } from '@shared/models/inscription.model'; +import { type InscriptionResponseHiro, createInscriptionHiro } from '@leather-wallet/query'; -export const mockInscription1: Inscription = { +export const mockInscriptionResponse1: InscriptionResponseHiro = { address: 'bc1pwrmewwprc8k8l2k63x4advg0nx0jk50xzqnee996lm87mcuza7kq6drg2k', - addressIndex: 0, content_length: 55, content_type: 'image/png', curse_type: '', @@ -28,9 +27,10 @@ export const mockInscription1: Inscription = { value: '10000', }; -export const mockInscription2: Inscription = { +export const mockInscription1 = createInscriptionHiro(mockInscriptionResponse1); + +export const mockInscriptionResponse2: InscriptionResponseHiro = { address: 'bc1pwrmewwprc8k8l2k63x4advg0nx0jk50xzqnee996lm87mcuza7kq6drg2k', - addressIndex: 0, content_length: 55, content_type: 'image/png', curse_type: '', @@ -56,7 +56,9 @@ export const mockInscription2: Inscription = { value: '10000', }; -export const mockInscriptionsList = [ +export const mockInscription2 = createInscriptionHiro(mockInscriptionResponse2); + +export const mockInscriptionResponsesList = [ { address: 'bc1q530dz4h80kwlzywlhx2qn0k6vdtftd93c499yq', id: 'a5ab63799f0bbd2571d1b90de9ebff815f7526787e27263d2f604e22f9118d0ci0', @@ -84,3 +86,4 @@ export const mockInscriptionsList = [ value: '546', }, ]; +export const mockInscriptionsList = mockInscriptionResponsesList.map(createInscriptionHiro); diff --git a/webpack/webpack.config.base.js b/webpack/webpack.config.base.js index 3f02caf9868..62803dfb1cf 100755 --- a/webpack/webpack.config.base.js +++ b/webpack/webpack.config.base.js @@ -149,6 +149,12 @@ export const config = { }, ], }, + { + test: /\.m?js$/, + resolve: { + fullySpecified: false, // disable the behavior, reference: https://webpack.js.org/configuration/module/#resolvefullyspecified + }, + }, { test: /\.(ts|tsx)$/, exclude: /node_modules/,