From 7856411d813ed50aaf2930cbe3e3843edb00ccc0 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Wed, 8 May 2024 12:40:04 -0500 Subject: [PATCH 1/3] refactor: btc balance --- package.json | 3 +- pnpm-lock.yaml | 404 +----------------- .../hooks/balance/btc/use-btc-balance.ts | 37 -- .../use-btc-crypto-currency-asset-balance.ts | 11 - .../hooks/balance/use-total-balance.tsx | 23 +- src/app/components/balance-btc.tsx | 17 - .../balance/bitcoin-balance-loader.tsx | 13 - src/app/components/balance/btc-balance.tsx | 17 + .../stx-balance.tsx} | 0 .../hooks/use-bitcoin-custom-fee.tsx | 4 +- .../use-bitcoin-fees-list.ts | 4 +- .../brc20-token-asset-list.tsx | 10 +- .../crypto-asset-list-item.tsx | 4 +- .../choose-crypto-asset/crypto-asset-list.tsx | 10 +- .../crypto-asset-item.layout.tsx} | 33 +- .../crypto-asset-item.utils.ts | 44 ++ .../crypto-currency-asset.utils.ts | 28 -- .../components/loaders/btc-balance-loader.tsx | 12 + src/app/features/asset-list/asset-list.tsx | 58 +-- .../add-stacks-ledger-keys-item.tsx | 4 +- .../components/btc-balance-list-item.tsx | 38 ++ .../stacks-balance-list-item.layout.tsx | 4 +- .../bitcoin-choose-fee/bitcoin-choose-fee.tsx | 8 +- .../hooks/use-validate-bitcoin-spend.ts | 4 +- src/app/features/container/total-balance.tsx | 4 +- .../hooks/use-btc-increase-fee.ts | 6 +- .../increase-btc-fee-dialog.tsx | 8 +- .../pending-brc-20-transfers.tsx | 10 +- .../bitcoin-contract-request-actions.tsx | 6 +- src/app/pages/fund/fund.tsx | 18 +- .../form/btc/btc-send-form.tsx | 15 +- .../form/btc/use-btc-send-form.tsx | 10 +- .../query/bitcoin/address/address.utils.ts | 19 - .../btc-native-segwit-balance.hooks.ts | 30 +- 34 files changed, 275 insertions(+), 641 deletions(-) delete mode 100644 src/app/common/hooks/balance/btc/use-btc-balance.ts delete mode 100644 src/app/common/hooks/balance/btc/use-btc-crypto-currency-asset-balance.ts delete mode 100644 src/app/components/balance-btc.tsx delete mode 100644 src/app/components/balance/bitcoin-balance-loader.tsx create mode 100644 src/app/components/balance/btc-balance.tsx rename src/app/components/{balance-stx.tsx => balance/stx-balance.tsx} (100%) rename src/app/components/crypto-assets/{crypto-currency-asset/crypto-currency-asset-item.layout.tsx => crypto-asset-item/crypto-asset-item.layout.tsx} (70%) create mode 100644 src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils.ts delete mode 100644 src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset.utils.ts create mode 100644 src/app/components/loaders/btc-balance-loader.tsx create mode 100644 src/app/features/asset-list/components/btc-balance-list-item.tsx diff --git a/package.json b/package.json index d8f23902d42..005499c7139 100644 --- a/package.json +++ b/package.json @@ -135,6 +135,7 @@ "@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.4.0", "@leather-wallet/tokens": "0.0.14", "@ledgerhq/hw-transport-webusb": "6.27.19", "@noble/hashes": "1.3.2", @@ -252,7 +253,7 @@ "@btckit/types": "0.0.19", "@chromatic-com/storybook": "1.2.23", "@leather-wallet/prettier-config": "0.0.5", - "@leather-wallet/types": "0.0.20", + "@leather-wallet/rpc": "0.3.0", "@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 24bca004867..c0136802b15 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ dependencies: '@hirosystems/token-metadata-api-client': specifier: 1.2.0 version: 1.2.0 + '@leather-wallet/models': + specifier: 0.4.0 + version: 0.4.0 '@leather-wallet/tokens': specifier: 0.0.14 version: 0.0.14 @@ -376,9 +379,9 @@ devDependencies: '@leather-wallet/prettier-config': specifier: 0.0.5 version: 0.0.5 - '@leather-wallet/types': - specifier: 0.0.20 - version: 0.0.20(@swc/core@1.4.8)(postcss@8.4.38)(ts-node@10.9.2)(typescript@5.4.4) + '@leather-wallet/rpc': + specifier: 0.3.0 + version: 0.3.0 '@ls-lint/ls-lint': specifier: 2.2.3 version: 2.2.3 @@ -3119,15 +3122,6 @@ packages: resolution: {integrity: sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==} dev: true - /@esbuild/aix-ppc64@0.19.12: - resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true - optional: true - /@esbuild/aix-ppc64@0.20.2: resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} engines: {node: '>=12'} @@ -3136,15 +3130,6 @@ packages: requiresBuild: true optional: true - /@esbuild/android-arm64@0.19.12: - resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm64@0.20.2: resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} engines: {node: '>=12'} @@ -3153,15 +3138,6 @@ packages: requiresBuild: true optional: true - /@esbuild/android-arm@0.19.12: - resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.20.2: resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} engines: {node: '>=12'} @@ -3170,15 +3146,6 @@ packages: requiresBuild: true optional: true - /@esbuild/android-x64@0.19.12: - resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.20.2: resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} engines: {node: '>=12'} @@ -3187,15 +3154,6 @@ packages: requiresBuild: true optional: true - /@esbuild/darwin-arm64@0.19.12: - resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.20.2: resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} engines: {node: '>=12'} @@ -3204,15 +3162,6 @@ packages: requiresBuild: true optional: true - /@esbuild/darwin-x64@0.19.12: - resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.20.2: resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} engines: {node: '>=12'} @@ -3221,15 +3170,6 @@ packages: requiresBuild: true optional: true - /@esbuild/freebsd-arm64@0.19.12: - resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.20.2: resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} engines: {node: '>=12'} @@ -3238,15 +3178,6 @@ packages: requiresBuild: true optional: true - /@esbuild/freebsd-x64@0.19.12: - resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.20.2: resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} engines: {node: '>=12'} @@ -3255,15 +3186,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-arm64@0.19.12: - resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.20.2: resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} engines: {node: '>=12'} @@ -3272,15 +3194,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-arm@0.19.12: - resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.20.2: resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} engines: {node: '>=12'} @@ -3289,15 +3202,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-ia32@0.19.12: - resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.20.2: resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} engines: {node: '>=12'} @@ -3306,15 +3210,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-loong64@0.19.12: - resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.20.2: resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} engines: {node: '>=12'} @@ -3323,15 +3218,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-mips64el@0.19.12: - resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.20.2: resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} engines: {node: '>=12'} @@ -3340,15 +3226,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-ppc64@0.19.12: - resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.20.2: resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} engines: {node: '>=12'} @@ -3357,15 +3234,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-riscv64@0.19.12: - resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.20.2: resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} engines: {node: '>=12'} @@ -3374,15 +3242,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-s390x@0.19.12: - resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.20.2: resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} engines: {node: '>=12'} @@ -3391,15 +3250,6 @@ packages: requiresBuild: true optional: true - /@esbuild/linux-x64@0.19.12: - resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.20.2: resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} engines: {node: '>=12'} @@ -3408,15 +3258,6 @@ packages: requiresBuild: true optional: true - /@esbuild/netbsd-x64@0.19.12: - resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.20.2: resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} engines: {node: '>=12'} @@ -3425,15 +3266,6 @@ packages: requiresBuild: true optional: true - /@esbuild/openbsd-x64@0.19.12: - resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.20.2: resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} engines: {node: '>=12'} @@ -3442,15 +3274,6 @@ packages: requiresBuild: true optional: true - /@esbuild/sunos-x64@0.19.12: - resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.20.2: resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} engines: {node: '>=12'} @@ -3459,15 +3282,6 @@ packages: requiresBuild: true optional: true - /@esbuild/win32-arm64@0.19.12: - resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.20.2: resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} engines: {node: '>=12'} @@ -3476,15 +3290,6 @@ packages: requiresBuild: true optional: true - /@esbuild/win32-ia32@0.19.12: - resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.20.2: resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} engines: {node: '>=12'} @@ -3493,15 +3298,6 @@ packages: requiresBuild: true optional: true - /@esbuild/win32-x64@0.19.12: - resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.20.2: resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} engines: {node: '>=12'} @@ -3775,6 +3571,11 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@leather-wallet/models@0.4.0: + resolution: {integrity: sha512-6uwhdJxEhIpCI/29TPQIXAzfcz4niVIIujkGuwRFSyUiT/E8IEvjM30A3p9pMtEPCzO2UnpVERGLiLSIk48fgg==} + dependencies: + bignumber.js: 9.1.2 + /@leather-wallet/prettier-config@0.0.5: resolution: {integrity: sha512-cK44e4XqYghcbd5ow8AKzK0NyNQ0r0V/PGVm940PN17tDxZ7JykBcEmSI1iGRkCnTCy/RE/qHkgGBW5gYXhM7g==} dependencies: @@ -3785,23 +3586,16 @@ packages: - supports-color dev: true + /@leather-wallet/rpc@0.3.0: + resolution: {integrity: sha512-jRexusk82Bax0CMIC9T2ApvnxA5rvd1yWjRyOxeImHdfQUK140MMij/Za8OcC8TYcFGMZbm3DEK1Xh9/NY6oIg==} + dependencies: + '@leather-wallet/models': 0.4.0 + dev: true + /@leather-wallet/tokens@0.0.14: resolution: {integrity: sha512-xCg+aIcn8DexmQhfvxYM85IGP2Q1JNfUBq80ZwV4horKw18MxxTdX5FeEthTrO8quRZu7up+IW6m/l3wElnDsA==} dev: false - /@leather-wallet/types@0.0.20(@swc/core@1.4.8)(postcss@8.4.38)(ts-node@10.9.2)(typescript@5.4.4): - resolution: {integrity: sha512-cypcCgzjfjzFJ4B+3fIlqZuBVFPGDTxlZJDzpkCVJSRBhVRX6z/YRjN7I6VMoCxaN/gBU9KHU7gzD5MTaoznSg==} - dependencies: - tsup: 8.0.2(@swc/core@1.4.8)(postcss@8.4.38)(ts-node@10.9.2)(typescript@5.4.4) - transitivePeerDependencies: - - '@microsoft/api-extractor' - - '@swc/core' - - postcss - - supports-color - - ts-node - - typescript - dev: true - /@ledgerhq/devices@8.2.0: resolution: {integrity: sha512-XROTW2gTmmuy+YPPDjdtKKTQ3mfxrPtKtV+a9QFbj8f5MnjVMV0Zpy1BIB4CyIMsVVi4z6+nI67auT7IlsM3SQ==} dependencies: @@ -11585,7 +11379,6 @@ packages: /bignumber.js@9.1.2: resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} - dev: false /binary-extensions@2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} @@ -11884,16 +11677,6 @@ packages: run-applescript: 7.0.0 dev: true - /bundle-require@4.0.2(esbuild@0.19.12): - resolution: {integrity: sha512-jwzPOChofl67PSTW2SGubV9HBQAhhR2i6nskiOThauo9dzwDUgOWQScFVaJkjEfYX+UXiD+LEx8EblQMc2wIag==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - peerDependencies: - esbuild: '>=0.17' - dependencies: - esbuild: 0.19.12 - load-tsconfig: 0.2.5 - dev: true - /bunyan@1.8.15: resolution: {integrity: sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==} engines: {'0': node >=0.10.0} @@ -12510,11 +12293,6 @@ packages: graceful-readlink: 1.0.1 dev: true - /commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - dev: true - /commander@6.2.1: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} engines: {node: '>= 6'} @@ -14320,37 +14098,6 @@ packages: - supports-color dev: true - /esbuild@0.19.12: - resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true - optionalDependencies: - '@esbuild/aix-ppc64': 0.19.12 - '@esbuild/android-arm': 0.19.12 - '@esbuild/android-arm64': 0.19.12 - '@esbuild/android-x64': 0.19.12 - '@esbuild/darwin-arm64': 0.19.12 - '@esbuild/darwin-x64': 0.19.12 - '@esbuild/freebsd-arm64': 0.19.12 - '@esbuild/freebsd-x64': 0.19.12 - '@esbuild/linux-arm': 0.19.12 - '@esbuild/linux-arm64': 0.19.12 - '@esbuild/linux-ia32': 0.19.12 - '@esbuild/linux-loong64': 0.19.12 - '@esbuild/linux-mips64el': 0.19.12 - '@esbuild/linux-ppc64': 0.19.12 - '@esbuild/linux-riscv64': 0.19.12 - '@esbuild/linux-s390x': 0.19.12 - '@esbuild/linux-x64': 0.19.12 - '@esbuild/netbsd-x64': 0.19.12 - '@esbuild/openbsd-x64': 0.19.12 - '@esbuild/sunos-x64': 0.19.12 - '@esbuild/win32-arm64': 0.19.12 - '@esbuild/win32-ia32': 0.19.12 - '@esbuild/win32-x64': 0.19.12 - dev: true - /esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} @@ -17152,11 +16899,6 @@ packages: react: 18.2.0 dev: false - /joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} - dev: true - /js-cookie@3.0.1: resolution: {integrity: sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==} engines: {node: '>=12'} @@ -17700,11 +17442,6 @@ packages: lightningcss-win32-x64-msvc: 1.23.0 dev: true - /lilconfig@3.1.1: - resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} - engines: {node: '>=14'} - dev: true - /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true @@ -17729,11 +17466,6 @@ packages: import-meta-resolve: 4.0.0 dev: true - /load-tsconfig@0.2.5: - resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true - /load-yaml-file@0.2.0: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} @@ -20286,24 +20018,6 @@ packages: postcss: 8.4.38 dev: false - /postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2): - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - dependencies: - lilconfig: 3.1.1 - postcss: 8.4.38 - ts-node: 10.9.2(@swc/core@1.4.8)(@types/node@20.12.4)(typescript@5.4.4) - yaml: 2.3.4 - dev: true - /postcss-loader@8.1.1(postcss@8.4.38)(typescript@5.4.4)(webpack@5.91.0): resolution: {integrity: sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==} engines: {node: '>= 18.12.0'} @@ -22553,13 +22267,6 @@ packages: engines: {node: '>= 8'} dev: true - /source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} - dependencies: - whatwg-url: 7.1.0 - dev: true - /space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} dev: true @@ -23014,20 +22721,6 @@ packages: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} dev: true - /sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - commander: 4.1.1 - glob: 10.3.10 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.6 - ts-interface-checker: 0.1.13 - dev: true - /sumchecker@3.0.1: resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} engines: {node: '>= 8.0'} @@ -23362,12 +23055,6 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - /tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - dependencies: - punycode: 2.3.1 - dev: true - /tr46@4.1.1: resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} engines: {node: '>=14'} @@ -23447,10 +23134,6 @@ packages: typescript: 5.4.4 dev: true - /ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - dev: true - /ts-morph@21.0.1: resolution: {integrity: sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg==} dependencies: @@ -23552,47 +23235,6 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - /tsup@8.0.2(@swc/core@1.4.8)(postcss@8.4.38)(ts-node@10.9.2)(typescript@5.4.4): - resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - '@microsoft/api-extractor': ^7.36.0 - '@swc/core': ^1 - postcss: ^8.4.12 - typescript: '>=4.5.0' - peerDependenciesMeta: - '@microsoft/api-extractor': - optional: true - '@swc/core': - optional: true - postcss: - optional: true - typescript: - optional: true - dependencies: - '@swc/core': 1.4.8 - bundle-require: 4.0.2(esbuild@0.19.12) - cac: 6.7.14 - chokidar: 3.6.0 - debug: 4.3.4(supports-color@5.5.0) - esbuild: 0.19.12 - execa: 5.1.1 - globby: 11.1.0 - joycon: 3.1.1 - postcss: 8.4.38 - postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2) - resolve-from: 5.0.0 - rollup: 4.14.0 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 - tree-kill: 1.2.2 - typescript: 5.4.4 - transitivePeerDependencies: - - supports-color - - ts-node - dev: true - /tsutils@3.21.0(typescript@5.4.4): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -24560,10 +24202,6 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - /webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - dev: true - /webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -24810,14 +24448,6 @@ packages: tr46: 0.0.3 webidl-conversions: 3.0.1 - /whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - dev: true - /when@3.7.7: resolution: {integrity: sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==} dev: true diff --git a/src/app/common/hooks/balance/btc/use-btc-balance.ts b/src/app/common/hooks/balance/btc/use-btc-balance.ts deleted file mode 100644 index 2965ab84e16..00000000000 --- a/src/app/common/hooks/balance/btc/use-btc-balance.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useMemo } from 'react'; - -import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; -import { i18nFormatCurrency } from '@app/common/money/format-money'; -import { createBitcoinCryptoCurrencyAssetTypeWrapper } from '@app/query/bitcoin/address/address.utils'; -import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; - -export function useBtcAssetBalance(btcAddress: string) { - const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); - const { - btcBalance: btcAssetBalance, - isLoading, - isFetching, - isInitialLoading, - } = useNativeSegwitBalance(btcAddress); - - return useMemo( - () => ({ - btcAddress, - btcAssetBalance, - btcUsdBalance: i18nFormatCurrency( - baseCurrencyAmountInQuote(btcAssetBalance.balance, btcMarketData) - ), - btcAvailableAssetBalance: createBitcoinCryptoCurrencyAssetTypeWrapper( - btcAssetBalance.balance - ), - btcAvailableUsdBalance: i18nFormatCurrency( - baseCurrencyAmountInQuote(btcAssetBalance.balance, btcMarketData) - ), - isLoading, - isFetching, - isInitialLoading, - }), - [btcAddress, btcAssetBalance, btcMarketData, isLoading, isInitialLoading, isFetching] - ); -} diff --git a/src/app/common/hooks/balance/btc/use-btc-crypto-currency-asset-balance.ts b/src/app/common/hooks/balance/btc/use-btc-crypto-currency-asset-balance.ts deleted file mode 100644 index 1c61fc3b462..00000000000 --- a/src/app/common/hooks/balance/btc/use-btc-crypto-currency-asset-balance.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; -import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; - -export function useBtcCryptoCurrencyAssetBalance() { - const currentBtcSigner = useCurrentAccountNativeSegwitSigner(); - // TODO: it would be better if we could skip providing the empty string to this hook. - const bitcoinBalance = useNativeSegwitBalance(currentBtcSigner?.(0).address ?? ''); - - if (!currentBtcSigner?.(0).address) return undefined; - return bitcoinBalance; -} diff --git a/src/app/common/hooks/balance/use-total-balance.tsx b/src/app/common/hooks/balance/use-total-balance.tsx index 107dfa4e0f0..bfac58ddd42 100644 --- a/src/app/common/hooks/balance/use-total-balance.tsx +++ b/src/app/common/hooks/balance/use-total-balance.tsx @@ -4,16 +4,14 @@ import { createMoney } from '@shared/models/money.model'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; +import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; -import { useBtcAssetBalance } from './btc/use-btc-balance'; - interface UseTotalBalanceArgs { btcAddress: string; stxAddress: string; } - export function useTotalBalance({ btcAddress, stxAddress }: UseTotalBalanceArgs) { // get market data const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); @@ -30,16 +28,19 @@ export function useTotalBalance({ btcAddress, stxAddress }: UseTotalBalanceArgs) // get btc balance const { - btcAvailableAssetBalance, + btcCryptoAssetBalance, isLoading: isLoadingBtcBalance, isFetching: isFetchingBtcBalance, isInitialLoading: isInititalLoadingBtcBalance, - } = useBtcAssetBalance(btcAddress); + } = useNativeSegwitBtcCryptoAssetBalance(btcAddress); return useMemo(() => { // calculate total balance const stxUsdAmount = baseCurrencyAmountInQuote(stxBalance, stxMarketData); - const btcUsdAmount = baseCurrencyAmountInQuote(btcAvailableAssetBalance.balance, btcMarketData); + const btcUsdAmount = baseCurrencyAmountInQuote( + btcCryptoAssetBalance.availableBalance, + btcMarketData + ); const totalBalance = { ...stxUsdAmount, amount: stxUsdAmount.amount.plus(btcUsdAmount.amount) }; return { @@ -53,14 +54,14 @@ export function useTotalBalance({ btcAddress, stxAddress }: UseTotalBalanceArgs) isFetching: isFetchingStacksBalance || isFetchingBtcBalance, }; }, [ - btcAvailableAssetBalance.balance, - btcMarketData, - isInitialLoading, - isInititalLoadingBtcBalance, - stxMarketData, stxBalance, + stxMarketData, + btcCryptoAssetBalance.availableBalance, + btcMarketData, isLoading, isLoadingBtcBalance, + isInitialLoading, + isInititalLoadingBtcBalance, isFetchingStacksBalance, isFetchingBtcBalance, ]); diff --git a/src/app/components/balance-btc.tsx b/src/app/components/balance-btc.tsx deleted file mode 100644 index d8f0c498faa..00000000000 --- a/src/app/components/balance-btc.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { formatMoney } from '@app/common/money/format-money'; -import { Caption } from '@app/ui/components/typography/caption'; - -import { BitcoinNativeSegwitAccountLoader } from './account/bitcoin-account-loader'; -import { BitcoinBalanceLoader } from './balance/bitcoin-balance-loader'; - -export function BtcBalance() { - return ( - - {signer => ( - - {balance => {formatMoney(balance.balance)}} - - )} - - ); -} diff --git a/src/app/components/balance/bitcoin-balance-loader.tsx b/src/app/components/balance/bitcoin-balance-loader.tsx deleted file mode 100644 index ff3d4a7426b..00000000000 --- a/src/app/components/balance/bitcoin-balance-loader.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { BitcoinCryptoCurrencyAssetBalance } from '@shared/models/crypto-asset-balance.model'; - -import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; - -interface BitcoinBalanceLoaderProps { - address: string; - children(balance: BitcoinCryptoCurrencyAssetBalance, isInitialLoading: boolean): React.ReactNode; -} - -export function BitcoinBalanceLoader({ address, children }: BitcoinBalanceLoaderProps) { - const { btcBalance, isInitialLoading } = useNativeSegwitBalance(address); - return children(btcBalance, isInitialLoading); -} diff --git a/src/app/components/balance/btc-balance.tsx b/src/app/components/balance/btc-balance.tsx new file mode 100644 index 00000000000..43feb74cb75 --- /dev/null +++ b/src/app/components/balance/btc-balance.tsx @@ -0,0 +1,17 @@ +import { formatMoney } from '@app/common/money/format-money'; +import { Caption } from '@app/ui/components/typography/caption'; + +import { BitcoinNativeSegwitAccountLoader } from '../account/bitcoin-account-loader'; +import { BtcBalanceLoader } from '../loaders/btc-balance-loader'; + +export function BtcBalance() { + return ( + + {signer => ( + + {balance => {formatMoney(balance.availableBalance)}} + + )} + + ); +} diff --git a/src/app/components/balance-stx.tsx b/src/app/components/balance/stx-balance.tsx similarity index 100% rename from src/app/components/balance-stx.tsx rename to src/app/components/balance/stx-balance.tsx 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 8fa74adfb7a..1a64c58fd8e 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 @@ -10,7 +10,7 @@ import { determineUtxosForSpendAll, } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { useCurrentNativeSegwitAddressBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; export const MAX_FEE_RATE_MULTIPLIER = 50; @@ -22,7 +22,7 @@ interface UseBitcoinCustomFeeArgs { } export function useBitcoinCustomFee({ amount, isSendingMax, recipients }: UseBitcoinCustomFeeArgs) { - const { balance } = useCurrentNativeSegwitAddressBalance(); + const { balance } = useCurrentNativeSegwitAvailableBalance(); const { data: utxos = [] } = useCurrentNativeSegwitUtxos(); const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); 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 0d079bd6408..3c0051573f1 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 @@ -10,7 +10,7 @@ import { determineUtxosForSpend, determineUtxosForSpendAll, } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; -import { useCurrentNativeSegwitAddressBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.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'; @@ -43,7 +43,7 @@ export function useBitcoinFeesList({ recipient, utxos, }: UseBitcoinFeesListArgs) { - const { balance } = useCurrentNativeSegwitAddressBalance(); + const { balance } = useCurrentNativeSegwitAvailableBalance(); const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const { data: feeRates, isLoading } = useAverageBitcoinFeeRates(); diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx index e63cfd9a422..f509a9495c3 100644 --- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx +++ b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx @@ -5,9 +5,8 @@ import { Stack } from 'leather-styles/jsx'; import { RouteUrls } from '@shared/route-urls'; -import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; import { Brc20Token } from '@app/query/bitcoin/bitcoin-client'; -import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { Brc20TokenAssetItemLayout } from './brc20-token-asset-item.layout'; @@ -17,12 +16,9 @@ interface Brc20TokenAssetListProps { } export function Brc20TokenAssetList({ tokens, variant }: Brc20TokenAssetListProps) { const navigate = useNavigate(); - const currentAccountBtcAddress = useCurrentAccountNativeSegwitAddressIndexZero(); - const { btcBalance: btcCryptoCurrencyAssetBalance } = - useNativeSegwitBalance(currentAccountBtcAddress); + const { balance } = useCurrentNativeSegwitAvailableBalance(); - const hasPositiveBtcBalanceForFees = - variant === 'send' && btcCryptoCurrencyAssetBalance.balance.amount.isGreaterThan(0); + const hasPositiveBtcBalanceForFees = variant === 'send' && balance.amount.isGreaterThan(0); function navigateToBrc20SendForm(token: Brc20Token) { const { balance, holderAddress, marketData, tokenData } = token; diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx index 4e790a4e35f..c0c73b970c5 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx +++ b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx @@ -1,6 +1,6 @@ import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model'; -import { CryptoCurrencyAssetItemLayout } from '../crypto-currency-asset/crypto-currency-asset-item.layout'; +import { CryptoAssetItemLayout } from '../crypto-asset-item/crypto-asset-item.layout'; import { CryptoCurrencyAssetIcon } from './crypto-currency-asset-icon'; import { FungibleTokenAssetItem } from './fungible-token-asset-item'; @@ -15,7 +15,7 @@ export function CryptoAssetListItem(props: CryptoAssetListItemProps) { switch (type) { case 'crypto-currency': return ( - } onClick={onClick} diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx index a6bdb402f4e..4af494db221 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx +++ b/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx @@ -6,12 +6,12 @@ import { StacksFungibleTokenAsset } from '@shared/models/crypto-asset.model'; import { useWalletType } from '@app/common/use-wallet-type'; import { BitcoinNativeSegwitAccountLoader } from '@app/components/account/bitcoin-account-loader'; -import { BitcoinBalanceLoader } from '@app/components/balance/bitcoin-balance-loader'; import { Brc20TokenAssetList } from '@app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list'; import { Brc20TokensLoader } from '@app/components/loaders/brc20-tokens-loader'; +import { BtcBalanceLoader } from '@app/components/loaders/btc-balance-loader'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; -import { CryptoCurrencyAssetItemLayout } from '../crypto-currency-asset/crypto-currency-asset-item.layout'; +import { CryptoAssetItemLayout } from '../crypto-asset-item/crypto-asset-item.layout'; import { CryptoAssetListItem } from './crypto-asset-list-item'; interface CryptoAssetListProps { @@ -30,16 +30,16 @@ export function CryptoAssetList({ {signer => ( - + {(balance, isLoading) => ( - } onClick={() => onItemClick(balance)} isLoading={isLoading} /> )} - + )} {cryptoAssetBalances.map(cryptoAssetBalance => ( diff --git a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout.tsx b/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx similarity index 70% rename from src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout.tsx rename to src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx index 4362246859c..af5856c23af 100644 --- a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout.tsx +++ b/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx @@ -1,9 +1,10 @@ import { ReactNode } from 'react'; +import type { CryptoAssetBalances } from '@leather-wallet/models'; import { Box, Flex, styled } from 'leather-styles/jsx'; -import { AllCryptoCurrencyAssetBalances } from '@shared/models/crypto-asset-balance.model'; - +import { capitalize } from '@app/common/utils'; +import { spamFilter } from '@app/common/utils/spam-filter'; import { BulletSeparator } from '@app/ui/components/bullet-separator/bullet-separator'; import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; import { SkeletonLoader } from '@app/ui/components/skeleton-loader/skeleton-loader'; @@ -11,32 +12,34 @@ import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; import { Caption } from '@app/ui/components/typography/caption'; import { Pressable } from '@app/ui/pressable/pressable'; -import { parseCryptoCurrencyAssetBalance } from './crypto-currency-asset.utils'; +import { parseCryptoAssetBalance } from './crypto-asset-item.utils'; -interface CryptoCurrencyAssetItemLayoutProps { +interface CryptoAssetItemLayoutProps { additionalBalanceInfo?: ReactNode; additionalUsdBalanceInfo?: ReactNode; address?: string; - assetBalance: AllCryptoCurrencyAssetBalances; + assetBalance: CryptoAssetBalances; icon: React.ReactNode; isLoading?: boolean; + name: string; onClick?(): void; rightElement?: React.ReactNode; usdBalance?: string; } -export function CryptoCurrencyAssetItemLayout({ +export function CryptoAssetItemLayout({ additionalBalanceInfo, additionalUsdBalanceInfo, address = '', assetBalance, icon, + isLoading = false, + name, onClick, rightElement, usdBalance, - isLoading = false, -}: CryptoCurrencyAssetItemLayoutProps) { - const { balance, dataTestId, formattedBalance, title } = - parseCryptoCurrencyAssetBalance(assetBalance); +}: CryptoAssetItemLayoutProps) { + const { balance, dataTestId, formattedBalance } = parseCryptoAssetBalance(assetBalance); + const title = spamFilter(capitalize(name)); const titleRight = ( @@ -45,7 +48,9 @@ export function CryptoCurrencyAssetItemLayout({ ) : ( @@ -62,7 +67,9 @@ export function CryptoCurrencyAssetItemLayout({ - {balance.amount.toNumber() > 0 && address ? usdBalance : null} + + {balance.availableBalance.amount.toNumber() > 0 && address ? usdBalance : null} + {additionalUsdBalanceInfo} @@ -77,7 +84,7 @@ export function CryptoCurrencyAssetItemLayout({ diff --git a/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils.ts b/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils.ts new file mode 100644 index 00000000000..955a07cb671 --- /dev/null +++ b/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils.ts @@ -0,0 +1,44 @@ +import type { + BtcCryptoAssetInfo, + CryptoAssetBalances, + StxCryptoAssetInfo, +} from '@leather-wallet/models'; +import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; + +import { BTC_DECIMALS, STX_DECIMALS } from '@shared/constants'; + +import { formatBalance } from '@app/common/format-balance'; +import { ftDecimals } from '@app/common/stacks-utils'; + +export const btcCryptoAssetInfo: BtcCryptoAssetInfo = { + decimals: BTC_DECIMALS, + hasMemo: true, + name: 'bitcoin', + symbol: 'BTC', +}; + +export const stxCryptoAssetInfo: StxCryptoAssetInfo = { + decimals: STX_DECIMALS, + hasMemo: true, + name: 'stacks', + symbol: 'STX', +}; + +export function parseCryptoAssetBalance(balance: CryptoAssetBalances) { + const { availableBalance } = balance; + + const amount = availableBalance.decimals + ? ftDecimals(availableBalance.amount, availableBalance.decimals) + : availableBalance.amount.toString(); + const dataTestId = CryptoAssetSelectors.CryptoAssetListItem.replace( + '{symbol}', + availableBalance.symbol.toLowerCase() + ); + const formattedBalance = formatBalance(amount); + + return { + balance, + dataTestId, + formattedBalance, + }; +} diff --git a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset.utils.ts b/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset.utils.ts deleted file mode 100644 index 041ca288ea5..00000000000 --- a/src/app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset.utils.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; - -import { AllCryptoCurrencyAssetBalances } from '@shared/models/crypto-asset-balance.model'; - -import { formatBalance } from '@app/common/format-balance'; -import { ftDecimals } from '@app/common/stacks-utils'; -import { spamFilter } from '@app/common/utils/spam-filter'; - -export function parseCryptoCurrencyAssetBalance(assetBalance: AllCryptoCurrencyAssetBalances) { - const { asset, balance } = assetBalance; - - const amount = balance.decimals - ? ftDecimals(balance.amount, balance.decimals) - : balance.amount.toString(); - const dataTestId = CryptoAssetSelectors.CryptoAssetListItem.replace( - '{symbol}', - balance.symbol.toLowerCase() - ); - const formattedBalance = formatBalance(amount); - const title = spamFilter(asset.name); - - return { - balance, - dataTestId, - formattedBalance, - title, - }; -} diff --git a/src/app/components/loaders/btc-balance-loader.tsx b/src/app/components/loaders/btc-balance-loader.tsx new file mode 100644 index 00000000000..76a3c56c068 --- /dev/null +++ b/src/app/components/loaders/btc-balance-loader.tsx @@ -0,0 +1,12 @@ +import { BtcCryptoAssetBalance } from '@leather-wallet/models'; + +import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; + +interface BtcBalanceLoaderProps { + address: string; + children(balance: BtcCryptoAssetBalance, isInitialLoading: boolean): React.ReactNode; +} +export function BtcBalanceLoader({ address, children }: BtcBalanceLoaderProps) { + const { btcCryptoAssetBalance, isInitialLoading } = useNativeSegwitBtcCryptoAssetBalance(address); + return children(btcCryptoAssetBalance, isInitialLoading); +} diff --git a/src/app/features/asset-list/asset-list.tsx b/src/app/features/asset-list/asset-list.tsx index 87a5be7445d..d1d79ceb1c4 100644 --- a/src/app/features/asset-list/asset-list.tsx +++ b/src/app/features/asset-list/asset-list.tsx @@ -3,7 +3,6 @@ import { Outlet } from 'react-router-dom'; import { HomePageSelectors } from '@tests/selectors/home.selectors'; import { Stack } from 'leather-styles/jsx'; -import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balance'; import { useWalletType } from '@app/common/use-wallet-type'; import { BitcoinNativeSegwitAccountLoader, @@ -13,21 +12,20 @@ import { BitcoinContractEntryPoint } from '@app/components/bitcoin-contract-entr import { Brc20TokenAssetList } from '@app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list'; import { RunesAssetList } from '@app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-list'; import { Src20TokenAssetList } from '@app/components/crypto-assets/bitcoin/src20-token-asset-list/src20-token-asset-list'; -import { CryptoCurrencyAssetItemLayout } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout'; import { Stx20TokenAssetList } from '@app/components/crypto-assets/stacks/stx20-token-asset-list/stx20-token-asset-list'; import { Brc20TokensLoader } from '@app/components/loaders/brc20-tokens-loader'; +import { BtcBalanceLoader } from '@app/components/loaders/btc-balance-loader'; import { RunesLoader } from '@app/components/loaders/runes-loader'; import { Src20TokensLoader } from '@app/components/loaders/src20-tokens-loader'; import { CurrentStacksAccountLoader } from '@app/components/loaders/stacks-account-loader'; import { Stx20TokensLoader } from '@app/components/loaders/stx20-tokens-loader'; import { useHasBitcoinLedgerKeychain } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger'; -import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; -import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; import { Collectibles } from '../collectibles/collectibles'; import { PendingBrc20TransferList } from '../pending-brc-20-transfers/pending-brc-20-transfers'; import { AddStacksLedgerKeysItem } from './components/add-stacks-ledger-keys-item'; +import { BtcBalanceListItem } from './components/btc-balance-list-item'; import { ConnectLedgerAssetBtn } from './components/connect-ledger-asset-button'; import { StacksBalanceListItem } from './components/stacks-balance-list-item'; import { StacksFungibleTokenAssetList } from './components/stacks-fungible-token-asset-list'; @@ -35,38 +33,44 @@ import { StacksUnsupportedTokenAssetList } from './components/stacks-unsupported export function AssetsList() { const hasBitcoinLedgerKeys = useHasBitcoinLedgerKeychain(); - const bitcoinAddressNativeSegwit = useCurrentAccountNativeSegwitAddressIndexZero(); const network = useCurrentNetwork(); - - const { btcAvailableAssetBalance, btcAvailableUsdBalance, isInitialLoading } = useBtcAssetBalance( - bitcoinAddressNativeSegwit - ); - const { whenWallet } = useWalletType(); return ( {whenWallet({ software: ( - } - address={bitcoinAddressNativeSegwit} - isLoading={isInitialLoading} - /> + + {nativeSegwitAccount => ( + + {(balance, isInitialLoading) => ( + + )} + + )} + ), ledger: ( - } - address={bitcoinAddressNativeSegwit} - isLoading={isInitialLoading} - rightElement={ - hasBitcoinLedgerKeys ? undefined : - } - /> + + {nativeSegwitAccount => ( + + {(balance, isInitialLoading) => ( + + } + /> + )} + + )} + ), })} diff --git a/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx b/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx index 2309e849ab5..e3540cb461e 100644 --- a/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx +++ b/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js'; -import { CryptoCurrencyAssetItemLayout } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout'; +import { CryptoAssetItemLayout } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; @@ -8,7 +8,7 @@ import { ConnectLedgerAssetBtn } from './connect-ledger-asset-button'; export function AddStacksLedgerKeysItem() { return ( - } rightElement={} diff --git a/src/app/features/asset-list/components/btc-balance-list-item.tsx b/src/app/features/asset-list/components/btc-balance-list-item.tsx new file mode 100644 index 00000000000..c52199982cd --- /dev/null +++ b/src/app/features/asset-list/components/btc-balance-list-item.tsx @@ -0,0 +1,38 @@ +import type { BtcCryptoAssetBalance } from '@leather-wallet/models'; + +import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; +import { i18nFormatCurrency } from '@app/common/money/format-money'; +import { CryptoAssetItemLayout } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; +import { btcCryptoAssetInfo } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; +import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; + +interface BtcBalanceListItemProps { + address: string; + balance: BtcCryptoAssetBalance; + isLoading: boolean; + rightElement?: React.ReactNode; +} +export function BtcBalanceListItem({ + address, + balance, + isLoading, + rightElement, +}: BtcBalanceListItemProps) { + const marketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); + const availableBalanceAsFiat = i18nFormatCurrency( + baseCurrencyAmountInQuote(balance.availableBalance, marketData) + ); + + return ( + } + isLoading={isLoading} + name={btcCryptoAssetInfo.name} + rightElement={rightElement} + usdBalance={availableBalanceAsFiat} + /> + ); +} diff --git a/src/app/features/asset-list/components/stacks-balance-list-item.layout.tsx b/src/app/features/asset-list/components/stacks-balance-list-item.layout.tsx index 25ab3a29254..c706c43fbe5 100644 --- a/src/app/features/asset-list/components/stacks-balance-list-item.layout.tsx +++ b/src/app/features/asset-list/components/stacks-balance-list-item.layout.tsx @@ -4,7 +4,7 @@ import type { StacksCryptoCurrencyAssetBalance } from '@shared/models/crypto-ass import type { Money } from '@shared/models/money.model'; import { ftDecimals } from '@app/common/stacks-utils'; -import { CryptoCurrencyAssetItemLayout } from '@app/components/crypto-assets/crypto-currency-asset/crypto-currency-asset-item.layout'; +import { CryptoAssetItemLayout } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; import { BulletOperator } from '@app/ui/components/bullet-separator/bullet-separator'; import { Caption } from '@app/ui/components/typography/caption'; @@ -39,7 +39,7 @@ export function StacksBalanceListItemLayout(props: StacksBalanceListItemLayoutPr ) : undefined; return ( - - + diff --git a/src/app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend.ts b/src/app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend.ts index 58168c1641b..48825a68969 100644 --- a/src/app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend.ts +++ b/src/app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend.ts @@ -3,11 +3,11 @@ import { useState } from 'react'; import { Money, createMoney } from '@shared/models/money.model'; import { subtractMoney, sumMoney } from '@app/common/money/calculate-money'; -import { useCurrentNativeSegwitAddressBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; export function useValidateBitcoinSpend(amount?: Money, isSendingMax?: boolean) { const [showInsufficientBalanceError, setShowInsufficientBalanceError] = useState(false); - const { balance } = useCurrentNativeSegwitAddressBalance(); + const { balance } = useCurrentNativeSegwitAvailableBalance(); return { showInsufficientBalanceError, diff --git a/src/app/features/container/total-balance.tsx b/src/app/features/container/total-balance.tsx index 2ca38223807..7070924fd0b 100644 --- a/src/app/features/container/total-balance.tsx +++ b/src/app/features/container/total-balance.tsx @@ -2,8 +2,8 @@ import { Suspense } from 'react'; import { Box, HStack } from 'leather-styles/jsx'; -import { BtcBalance } from '@app/components/balance-btc'; -import { StxBalance } from '@app/components/balance-stx'; +import { BtcBalance } from '@app/components/balance/btc-balance'; +import { StxBalance } from '@app/components/balance/stx-balance'; import { LoadingRectangle } from '@app/components/loading-rectangle'; import { useConfigBitcoinEnabled } from '@app/query/common/remote-config/remote-config.query'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; 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 91d9666d902..bd3a48330bf 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 @@ -11,7 +11,6 @@ import { RouteUrls } from '@shared/route-urls'; import { isError } from '@shared/utils'; import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; -import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balance'; import { btcToSat } from '@app/common/money/unit-conversion'; import { queryClient } from '@app/common/persistence'; import { @@ -23,6 +22,7 @@ import { MAX_FEE_RATE_MULTIPLIER } from '@app/components/bitcoin-custom-fee/hook import { useBitcoinFeesList } from '@app/components/bitcoin-fees-list/use-bitcoin-fees-list'; import { useToast } from '@app/features/toasts/use-toast'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; +import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.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'; @@ -51,7 +51,7 @@ export function useBtcIncreaseFee(btcTx: BitcoinTx) { [btcTx.vin.length, btcTx.vout.length, recipient] ); - const { btcAvailableAssetBalance } = useBtcAssetBalance(currentBitcoinAddress); + const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance(currentBitcoinAddress); const sendingAmount = getBitcoinTxValue(currentBitcoinAddress, btcTx); const { feesList } = useBitcoinFeesList({ amount: createMoney(btcToSat(sendingAmount), 'BTC'), @@ -163,7 +163,7 @@ export function useBtcIncreaseFee(btcTx: BitcoinTx) { } // check if fee is higher than the available balance - return bnValue.isLessThanOrEqualTo(btcAvailableAssetBalance.balance.amount); + return bnValue.isLessThanOrEqualTo(btcCryptoAssetBalance.availableBalance.amount); }, }), }); diff --git a/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx b/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx index 5c5c71c623b..57bdd9bd78c 100644 --- a/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx +++ b/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx @@ -8,7 +8,6 @@ import { createMoney } from '@shared/models/money.model'; import { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; import { RouteUrls } from '@shared/route-urls'; -import { useBtcAssetBalance } from '@app/common/hooks/balance/btc/use-btc-balance'; import { useLocationStateWithCache } from '@app/common/hooks/use-location-state'; import { formatMoney } from '@app/common/money/format-money'; import { btcToSat } from '@app/common/money/unit-conversion'; @@ -16,6 +15,7 @@ import { getBitcoinTxValue } from '@app/common/transactions/bitcoin/utils'; import { BitcoinCustomFeeInput } from '@app/components/bitcoin-custom-fee/bitcoin-custom-fee-input'; import { BitcoinTransactionItem } from '@app/components/bitcoin-transaction-item/bitcoin-transaction-item'; import { LoadingSpinner } from '@app/components/loading-spinner'; +import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { Dialog } from '@app/ui/components/containers/dialog/dialog'; import { Footer } from '@app/ui/components/containers/footers/footer'; @@ -34,11 +34,11 @@ export function IncreaseBtcFeeDialog() { const btcTx = tx; const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); const currentBitcoinAddress = nativeSegwitSigner.address; - const { btcAvailableAssetBalance } = useBtcAssetBalance(currentBitcoinAddress); + const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance(currentBitcoinAddress); const { isBroadcasting, sizeInfo, onSubmit, validationSchema, recipient } = useBtcIncreaseFee(btcTx); - const balance = formatMoney(btcAvailableAssetBalance.balance); + const balance = formatMoney(btcCryptoAssetBalance.availableBalance); const recipients = [ { @@ -104,7 +104,7 @@ export function IncreaseBtcFeeDialog() { /> - {btcAvailableAssetBalance && Balance: {balance}} + {btcCryptoAssetBalance && Balance: {balance}} 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 ed426dc800c..20b863d3272 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 @@ -8,12 +8,11 @@ import { noop } from '@shared/utils'; import { usePressable } from '@app/components/item-hover'; import { StatusPending } from '@app/components/status-pending'; import { StatusReady } from '@app/components/status-ready'; -import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.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 { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { OrdinalsbotInscriptionStatus, PendingBrc20Transfer, @@ -91,12 +90,9 @@ function PendingBrcTransfer({ order }: PendingBrcTransferProps) { const [component, bind] = usePressable(order.status === 'ready'); const navigate = useNavigate(); const ordinalsbotClient = useOrdinalsbotClient(); - const currentAccountBtcAddress = useCurrentAccountNativeSegwitAddressIndexZero(); - const { btcBalance: btcCryptoCurrencyAssetBalance } = - useNativeSegwitBalance(currentAccountBtcAddress); + const { balance } = useCurrentNativeSegwitAvailableBalance(); - const hasPositiveBtcBalanceForFees = - btcCryptoCurrencyAssetBalance.balance.amount.isGreaterThan(0); + const hasPositiveBtcBalanceForFees = balance.amount.isGreaterThan(0); return ( diff --git a/src/app/pages/fund/fund.tsx b/src/app/pages/fund/fund.tsx index 68bb064d802..7c98a83afcd 100644 --- a/src/app/pages/fund/fund.tsx +++ b/src/app/pages/fund/fund.tsx @@ -1,16 +1,15 @@ import { Outlet, useParams } from 'react-router-dom'; +import type { BtcCryptoAssetBalance } from '@leather-wallet/models'; + import type { Blockchains } from '@shared/models/blockchain.model'; -import type { - BitcoinCryptoCurrencyAssetBalance, - StacksCryptoCurrencyAssetBalance, -} from '@shared/models/crypto-asset-balance.model'; +import type { StacksCryptoCurrencyAssetBalance } from '@shared/models/crypto-asset-balance.model'; import type { CryptoCurrencies } from '@shared/models/currencies.model'; import { RouteUrls } from '@shared/route-urls'; -import { useBtcCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/btc/use-btc-crypto-currency-asset-balance'; import { useStxCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance'; import { FullPageLoadingSpinner } from '@app/components/loading-spinner'; +import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSignerNullable } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; @@ -19,7 +18,7 @@ import { FiatProvidersList } from './fiat-providers-list'; interface FundCryptoCurrencyInfo { address?: string; - balance?: BitcoinCryptoCurrencyAssetBalance | StacksCryptoCurrencyAssetBalance; + balance?: BtcCryptoAssetBalance | StacksCryptoCurrencyAssetBalance; blockchain: Blockchains; route: string; symbol: CryptoCurrencies; @@ -28,14 +27,16 @@ interface FundCryptoCurrencyInfo { export function FundPage() { const currentStxAccount = useCurrentStacksAccount(); const bitcoinSigner = useCurrentAccountNativeSegwitIndexZeroSignerNullable(); - const btcCryptoCurrencyAssetBalance = useBtcCryptoCurrencyAssetBalance(); + const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance( + currentStxAccount?.address ?? '' + ); const stxCryptoCurrencyAssetBalance = useStxCryptoCurrencyAssetBalance(); const { currency = 'STX' } = useParams(); const fundCryptoCurrencyMap: Record = { BTC: { address: bitcoinSigner?.address, - balance: btcCryptoCurrencyAssetBalance?.btcBalance, + balance: btcCryptoAssetBalance, blockchain: 'Bitcoin', route: RouteUrls.ReceiveBtc, symbol: currency, @@ -52,6 +53,7 @@ export function FundPage() { const { address, balance, blockchain, route, symbol } = fundCryptoCurrencyMap[currency as CryptoCurrencies]; + // TODO: Asset refactor: Why is the balance needed here? if (!address || !balance) return ; return ( 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 abe8ee7bd14..c36e6bf1382 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 @@ -9,7 +9,7 @@ import { CryptoCurrencies } from '@shared/models/currencies.model'; import { formatMoney } from '@app/common/money/format-money'; import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog'; -import { useNativeSegwitBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; @@ -37,7 +37,10 @@ export function BtcSendForm() { const btcMarketData = useCryptoCurrencyMarketDataMeanAverage(symbol); const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); - const { btcBalance } = useNativeSegwitBalance(nativeSegwitSigner.address); + // TODO: Asset refactor: need asset here + const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance( + nativeSegwitSigner.address + ); const { calcMaxSpend, @@ -80,17 +83,19 @@ export function BtcSendForm() { > Continue - + } > createBitcoinCryptoCurrencyAssetTypeWrapper(balance), - [balance] - ); + const btcCryptoAssetBalance = useMemo(() => makeBtcCryptoAssetBalance(balance), [balance]); return { - btcBalance: wrappedBalance, + btcCryptoAssetBalance, isInitialLoading, isLoading, isFetching, }; } -export function useCurrentNativeSegwitAddressBalance() { +export function useNativeSegwitAvailableBalance(address: string) { + return useGetBitcoinBalanceByAddress(address); +} + +export function useCurrentNativeSegwitAvailableBalance() { const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); return useGetBitcoinBalanceByAddress(nativeSegwitSigner.address); } From 25b62a87e4de18f1db5d3720dfc8fbc29dfd36f7 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Thu, 9 May 2024 10:20:45 -0500 Subject: [PATCH 2/3] refactor: stx balance --- pnpm-lock.yaml | 202 +++++++++++++++--- .../analytics/use-track-switch-account.ts | 6 +- .../hooks/balance/stx/use-stx-balance.ts | 55 ----- .../use-stx-crypto-currency-asset-balance.ts | 7 +- .../hooks/balance/use-total-balance.tsx | 12 +- .../use-transferable-asset-balances.hooks.ts | 8 +- src/app/components/balance/stx-balance.tsx | 16 +- .../hooks/use-bitcoin-custom-fee.tsx | 4 +- .../use-bitcoin-fees-list.ts | 4 +- .../brc20-token-asset-list.tsx | 4 +- .../crypto-asset-item.layout.tsx | 12 +- .../components/loaders/btc-balance-loader.tsx | 4 +- src/app/features/asset-list/asset-list.tsx | 6 +- .../components/btc-balance-list-item.tsx | 2 +- .../components/stacks-balance-list-item.tsx | 17 -- ... stx-balance-list-item.layout.stories.tsx} | 4 +- ...t.tsx => stx-balance-list-item.layout.tsx} | 2 +- .../components/stx-balance-list-item.tsx | 52 +++++ .../bitcoin-choose-fee/bitcoin-choose-fee.tsx | 4 +- .../hooks/use-validate-bitcoin-spend.ts | 4 +- .../components/taproot-balance-displayer.tsx | 2 +- .../hooks/use-btc-increase-fee.ts | 4 +- .../increase-btc-fee-dialog.tsx | 4 +- .../increase-stx-fee-dialog.tsx | 15 +- .../pending-brc-20-transfers.tsx | 4 +- .../retrieve-taproot-to-native-segwit.tsx | 2 +- ...use-generate-retrieve-taproot-funds-tx.tsx | 2 +- .../crypto-asset-list-item.tsx | 2 +- .../crypto-asset-list.tsx | 2 +- .../crypto-currency-asset-icon.tsx | 0 .../fungible-token-asset-item.tsx | 2 +- .../send-btc-disabled.tsx | 0 .../hooks/use-transaction-error.ts | 23 +- .../stacks-transaction-signer.tsx | 6 +- .../transaction-error/error-messages.tsx | 8 +- .../bitcoin-contract-request-actions.tsx | 4 +- .../choose-asset-to-fund.tsx | 2 +- src/app/pages/fund/fund.tsx | 17 +- src/app/pages/home/components/send-button.tsx | 10 +- src/app/pages/home/home.tsx | 4 +- .../choose-crypto-asset.tsx | 2 +- .../form/btc/btc-send-form.tsx | 4 +- .../form/btc/use-btc-send-form.tsx | 4 +- .../form/stx/use-stx-send-form.tsx | 22 +- .../send-crypto-asset-form.routes.tsx | 2 +- .../transaction-request.tsx | 6 +- ....ts => btc-balance-native-segwit.hooks.ts} | 10 +- ....hooks.ts => btc-balance-taproot.hooks.ts} | 0 .../query/common/alex-sdk/alex-sdk.hooks.ts | 10 +- .../balance/stacks-ft-balances.hooks.ts | 9 - .../query/stacks/balance/stx-balance.hooks.ts | 61 ++++-- src/app/query/stacks/mempool/mempool.hooks.ts | 27 ++- 52 files changed, 426 insertions(+), 269 deletions(-) delete mode 100644 src/app/common/hooks/balance/stx/use-stx-balance.ts delete mode 100644 src/app/features/asset-list/components/stacks-balance-list-item.tsx rename src/app/features/asset-list/components/{stacks-balance-list-item.layout.stories.tsx => stx-balance-list-item.layout.stories.tsx} (92%) rename src/app/features/asset-list/components/{stacks-balance-list-item.layout.tsx => stx-balance-list-item.layout.tsx} (96%) create mode 100644 src/app/features/asset-list/components/stx-balance-list-item.tsx rename src/app/{components/crypto-assets/choose-crypto-asset => features/selectable-asset-list}/crypto-asset-list-item.tsx (88%) rename src/app/{components/crypto-assets/choose-crypto-asset => features/selectable-asset-list}/crypto-asset-list.tsx (95%) rename src/app/{components/crypto-assets/choose-crypto-asset => features/selectable-asset-list}/crypto-currency-asset-icon.tsx (100%) rename src/app/{components/crypto-assets/choose-crypto-asset => features/selectable-asset-list}/fungible-token-asset-item.tsx (79%) rename src/app/{components/crypto-assets/choose-crypto-asset => features/selectable-asset-list}/send-btc-disabled.tsx (100%) rename src/app/query/bitcoin/balance/{btc-native-segwit-balance.hooks.ts => btc-balance-native-segwit.hooks.ts} (78%) rename src/app/query/bitcoin/balance/{btc-taproot-balance.hooks.ts => btc-balance-taproot.hooks.ts} (100%) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0136802b15..3cb84a9116d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -399,10 +399,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.4)(@types/react-dom@18.2.19)(@types/styled-components@5.1.34) + version: 4.0.0(@babel/core@7.24.5)(@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.4)(redux@5.0.1) + version: 0.9.3(@redux-devtools/core@4.0.0)(immutable@4.3.5)(redux@5.0.1) '@schemastore/web-manifest': specifier: 0.0.6 version: 0.0.6 @@ -691,6 +691,7 @@ 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==} @@ -987,6 +988,29 @@ 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'} @@ -1016,6 +1040,16 @@ 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'} @@ -1120,6 +1154,13 @@ 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'} @@ -1148,6 +1189,20 @@ 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'} @@ -1191,6 +1246,13 @@ 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'} @@ -1205,16 +1267,33 @@ 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} @@ -1256,6 +1335,17 @@ 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'} @@ -1300,6 +1390,14 @@ 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'} @@ -1446,13 +1544,13 @@ packages: '@babel/helper-plugin-utils': 7.22.5 dev: true - /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.4): + /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.24.5): resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 dependencies: - '@babel/core': 7.24.4 + '@babel/core': 7.24.5 '@babel/helper-plugin-utils': 7.22.5 dev: true @@ -2336,6 +2434,24 @@ 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'} @@ -2353,6 +2469,15 @@ 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 @@ -5381,7 +5506,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.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.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) transitivePeerDependencies: - '@types/react-dom' - bufferutil @@ -5408,7 +5533,7 @@ packages: redux-devtools-themes: 1.0.0 dev: true - /@redux-devtools/cli@4.0.0(@babel/core@7.24.4)(@types/react-dom@18.2.19)(@types/styled-components@5.1.34): + /@redux-devtools/cli@4.0.0(@babel/core@7.24.5)(@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 @@ -5436,7 +5561,7 @@ packages: semver: 7.6.0 socketcluster-server: 19.0.1 sqlite3: 5.1.6 - styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.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) uuid: 9.0.1 transitivePeerDependencies: - '@babel/core' @@ -5513,7 +5638,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.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.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) 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): @@ -5616,14 +5741,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.4)(redux@5.0.1): + /@redux-devtools/remote@0.9.3(@redux-devtools/core@4.0.0)(immutable@4.3.5)(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.4)(redux@5.0.1) + '@redux-devtools/utils': 3.0.0(@redux-devtools/core@4.0.0)(immutable@4.3.5)(redux@5.0.1) jsan: 3.1.14 querystring: 0.2.1 redux: 5.0.1 @@ -5665,18 +5790,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.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.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) transitivePeerDependencies: - react-dom dev: true - /@redux-devtools/serialize@0.4.2(immutable@4.3.4): + /@redux-devtools/serialize@0.4.2(immutable@4.3.5): resolution: {integrity: sha512-YVqZCChJld5l3Ni2psEZ5loe9x5xpf9J4ckz+7OJdzCNsplC7vzjnkQbFxE6+ULZbywRVp+nSBslTXmaXqAw4A==} peerDependencies: immutable: ^4.0.0 dependencies: '@babel/runtime': 7.24.4 - immutable: 4.3.4 + immutable: 4.3.5 jsan: 3.1.14 dev: true @@ -5699,7 +5824,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.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.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) transitivePeerDependencies: - react-dom dev: true @@ -5731,12 +5856,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.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.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) transitivePeerDependencies: - react-dom dev: true - /@redux-devtools/utils@3.0.0(@redux-devtools/core@4.0.0)(immutable@4.3.4)(redux@5.0.1): + /@redux-devtools/utils@3.0.0(@redux-devtools/core@4.0.0)(immutable@4.3.5)(redux@5.0.1): resolution: {integrity: sha512-m1AJoxQffm1/6m0qrkb7gW0FkmaAoi1/HJzmdkchAeA8sAJhzGOnXJEpsjmXPt5BIHxg0zsglA+5FsgGWXa97A==} peerDependencies: '@redux-devtools/core': ^4.0.0 @@ -5745,10 +5870,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.4) + '@redux-devtools/serialize': 0.4.2(immutable@4.3.5) '@types/get-params': 0.1.2 get-params: 0.1.2 - immutable: 4.3.4 + immutable: 4.3.5 jsan: 3.1.14 lodash: 4.17.21 nanoid: 3.3.4 @@ -6328,7 +6453,7 @@ packages: /@stacks/blockchain-api-client@6.3.4: resolution: {integrity: sha512-4O9qe7m2XKG8PNZ9n5cvhji95IDZ29WO1X2ICgeBPdGc5Y0WGmo0wgIFAROh37pGSkBJsuJjy15ICdP47iy8+w==} dependencies: - '@stacks/stacks-blockchain-api-types': 7.3.6 + '@stacks/stacks-blockchain-api-types': 6.3.4 '@types/ws': 7.4.7 cross-fetch: 3.1.5 eventemitter3: 4.0.7 @@ -6475,10 +6600,9 @@ 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==} + /@stacks/stacks-blockchain-api-types@7.10.0: + resolution: {integrity: sha512-LfDishvEsmDJ6OXfgohkOIIsLTwvKVn3NKELaWTaZndN2Pucsk3Uz8NPDhVM5Ij1rGjFGT2bwcHNoGE7k3GhxQ==} dev: false /@stacks/storage@6.11.0: @@ -11274,17 +11398,17 @@ packages: - supports-color dev: true - /babel-plugin-styled-components@2.1.4(@babel/core@7.24.4)(styled-components@5.3.11): + /babel-plugin-styled-components@2.1.4(@babel/core@7.24.5)(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.4) + '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.24.5) lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.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) transitivePeerDependencies: - '@babel/core' dev: true @@ -12085,7 +12209,7 @@ packages: peerDependencies: '@stacks/transactions': '*' dependencies: - '@stacks/stacks-blockchain-api-types': 7.3.6 + '@stacks/stacks-blockchain-api-types': 7.10.0 '@stacks/transactions': 6.9.0 axios: 1.6.8 lodash: 4.17.21 @@ -14456,7 +14580,7 @@ packages: lodash.merge: 4.6.2 minimatch: 3.1.2 natural-compare: 1.4.0 - optionator: 0.9.3 + optionator: 0.9.4 strip-ansi: 6.0.1 text-table: 0.2.0 transitivePeerDependencies: @@ -16121,6 +16245,10 @@ 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'} @@ -19243,6 +19371,18 @@ 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==} @@ -22692,7 +22832,7 @@ packages: inline-style-parser: 0.2.2 dev: true - /styled-components@5.3.11(@babel/core@7.24.4)(react-dom@18.2.0)(react-is@18.2.0)(react@18.2.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): resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} engines: {node: '>=10'} peerDependencies: @@ -22705,7 +22845,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.4)(styled-components@5.3.11) + babel-plugin-styled-components: 2.1.4(@babel/core@7.24.5)(styled-components@5.3.11) css-to-react-native: 3.2.0 hoist-non-react-statics: 3.3.2 react: 18.2.0 @@ -24584,6 +24724,10 @@ 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/common/hooks/analytics/use-track-switch-account.ts b/src/app/common/hooks/analytics/use-track-switch-account.ts index cb3f70ad2f4..23614ac1613 100644 --- a/src/app/common/hooks/analytics/use-track-switch-account.ts +++ b/src/app/common/hooks/analytics/use-track-switch-account.ts @@ -1,7 +1,6 @@ import { useCallback } from 'react'; import { queryClient } from '@app/common/persistence'; -import { parseBalanceResponse } from '@app/query/stacks/balance/stx-balance.hooks'; import { useAnalytics } from './use-analytics'; @@ -13,8 +12,9 @@ export function useTrackSwitchAccount() { const accountBalanceCache = queryClient.getQueryData(['get-address-stx-balance', address]); if (!accountBalanceCache) return; try { - const balances = parseBalanceResponse(accountBalanceCache as any); - const hasStxBalance = !!balances?.stx.unlockedStx.amount.isGreaterThan(0); + const hasStxBalance = !!(accountBalanceCache as any).stx.unlockedStx.amount.isGreaterThan( + 0 + ); void analytics.track('switch_account', { index, hasStxBalance }); } finally { } diff --git a/src/app/common/hooks/balance/stx/use-stx-balance.ts b/src/app/common/hooks/balance/stx/use-stx-balance.ts deleted file mode 100644 index 76789b68c61..00000000000 --- a/src/app/common/hooks/balance/stx/use-stx-balance.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { useMemo } from 'react'; - -import { createMoney } from '@shared/models/money.model'; -import { isDefined } from '@shared/utils'; - -import { baseCurrencyAmountInQuote, subtractMoney } from '@app/common/money/calculate-money'; -import { i18nFormatCurrency } from '@app/common/money/format-money'; -import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; -import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; -import { useCurrentStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; -import { useCurrentAccountMempoolTransactionsBalance } from '@app/query/stacks/mempool/mempool.hooks'; - -export function useStxBalance() { - const stxBalanceQuery = useCurrentStacksAccountBalances(); - const totalBalance = stxBalanceQuery.data?.stx.balance; - const unlockedStxBalance = stxBalanceQuery.data?.stx.unlockedStx; - - const stxMarketData = useCryptoCurrencyMarketDataMeanAverage('STX'); - const pendingTxsBalance = useCurrentAccountMempoolTransactionsBalance(); - - const stxEffectiveBalance = isDefined(totalBalance) - ? subtractMoney(totalBalance, pendingTxsBalance) - : createMoney(0, 'STX'); - - const stxEffectiveUsdBalance = isDefined(totalBalance) - ? i18nFormatCurrency(baseCurrencyAmountInQuote(stxEffectiveBalance, stxMarketData)) - : undefined; - - const stxLockedBalance = stxBalanceQuery.data?.stx.locked; - const stxUsdLockedBalance = isDefined(stxLockedBalance) - ? i18nFormatCurrency(baseCurrencyAmountInQuote(stxLockedBalance, stxMarketData)) - : undefined; - - return useMemo(() => { - return { - stxBalanceQuery, - stxOutboundQuery: pendingTxsBalance, - availableBalance: isDefined(unlockedStxBalance) - ? subtractMoney(unlockedStxBalance, pendingTxsBalance) - : createMoney(0, 'STX'), - stxEffectiveBalance: createStacksCryptoCurrencyAssetTypeWrapper(stxEffectiveBalance.amount), - stxEffectiveUsdBalance, - stxLockedBalance, - stxUsdLockedBalance, - }; - }, [ - stxBalanceQuery, - pendingTxsBalance, - unlockedStxBalance, - stxEffectiveBalance.amount, - stxEffectiveUsdBalance, - stxLockedBalance, - stxUsdLockedBalance, - ]); -} diff --git a/src/app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance.ts b/src/app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance.ts index 1f0a4387a39..cb9e60203d4 100644 --- a/src/app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance.ts +++ b/src/app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance.ts @@ -1,7 +1,8 @@ -import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance'; import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +// TODO: Asset refactor: remove wrapper here export function useStxCryptoCurrencyAssetBalance() { - const { availableBalance: availableStxBalance } = useStxBalance(); - return createStacksCryptoCurrencyAssetTypeWrapper(availableStxBalance.amount); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); + return createStacksCryptoCurrencyAssetTypeWrapper(availableUnlockedBalance.amount); } diff --git a/src/app/common/hooks/balance/use-total-balance.tsx b/src/app/common/hooks/balance/use-total-balance.tsx index bfac58ddd42..043bff0b394 100644 --- a/src/app/common/hooks/balance/use-total-balance.tsx +++ b/src/app/common/hooks/balance/use-total-balance.tsx @@ -4,9 +4,9 @@ import { createMoney } from '@shared/models/money.model'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; -import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; -import { useStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; interface UseTotalBalanceArgs { btcAddress: string; @@ -19,12 +19,12 @@ export function useTotalBalance({ btcAddress, stxAddress }: UseTotalBalanceArgs) // get stx balance const { - data: balances, + data: balance, isLoading, isInitialLoading, isFetching: isFetchingStacksBalance, - } = useStacksAccountBalances(stxAddress); - const stxBalance = balances ? balances.stx.balance : createMoney(0, 'STX'); + } = useStxCryptoAssetBalance(stxAddress); + const stxBalance = balance ? balance.totalBalance : createMoney(0, 'STX'); // get btc balance const { @@ -32,7 +32,7 @@ export function useTotalBalance({ btcAddress, stxAddress }: UseTotalBalanceArgs) isLoading: isLoadingBtcBalance, isFetching: isFetchingBtcBalance, isInitialLoading: isInititalLoadingBtcBalance, - } = useNativeSegwitBtcCryptoAssetBalance(btcAddress); + } = useBtcCryptoAssetBalanceNativeSegwit(btcAddress); return useMemo(() => { // calculate total balance diff --git a/src/app/common/hooks/use-transferable-asset-balances.hooks.ts b/src/app/common/hooks/use-transferable-asset-balances.hooks.ts index 4eddb7d909f..8f43c43ded2 100644 --- a/src/app/common/hooks/use-transferable-asset-balances.hooks.ts +++ b/src/app/common/hooks/use-transferable-asset-balances.hooks.ts @@ -4,16 +4,16 @@ import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-a import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; -import { useStxBalance } from './balance/stx/use-stx-balance'; - +// TODO: Asset refactor: remove wrapper here export function useAllTransferableCryptoAssetBalances(): AllTransferableCryptoAssetBalances[] { const account = useCurrentStacksAccount(); - const { availableBalance: availableStxBalance } = useStxBalance(); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); const stxCryptoCurrencyAssetBalance = createStacksCryptoCurrencyAssetTypeWrapper( - availableStxBalance.amount + availableUnlockedBalance.amount ); const stacksFtAssetBalances = useTransferableStacksFungibleTokenAssetBalances( account?.address ?? '' diff --git a/src/app/components/balance/stx-balance.tsx b/src/app/components/balance/stx-balance.tsx index bf72d248603..afde5293dc6 100644 --- a/src/app/components/balance/stx-balance.tsx +++ b/src/app/components/balance/stx-balance.tsx @@ -1,24 +1,24 @@ import { useMemo } from 'react'; import { stacksValue } from '@app/common/stacks-utils'; -import { useStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { Caption } from '@app/ui/components/typography/caption'; -interface BalanceProps { +interface StxBalanceProps { address: string; } -export function StxBalance(props: BalanceProps) { +export function StxBalance(props: StxBalanceProps) { const { address } = props; - const { data: balances } = useStacksAccountBalances(address); + const { data: balance } = useStxCryptoAssetBalance(address); - const balance = useMemo( + const stxBalance = useMemo( () => stacksValue({ - value: balances?.stx?.unlockedStx.amount ?? 0, + value: balance?.unlockedBalance.amount ?? 0, withTicker: true, }), - [balances] + [balance] ); - return {balance}; + return {stxBalance}; } 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 1a64c58fd8e..8555cf70fa7 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 @@ -10,7 +10,7 @@ import { determineUtxosForSpendAll, } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentBtcAvailableBalanceNativeSegwit } 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; @@ -22,7 +22,7 @@ interface UseBitcoinCustomFeeArgs { } export function useBitcoinCustomFee({ amount, isSendingMax, recipients }: UseBitcoinCustomFeeArgs) { - const { balance } = useCurrentNativeSegwitAvailableBalance(); + const { balance } = useCurrentBtcAvailableBalanceNativeSegwit(); const { data: utxos = [] } = useCurrentNativeSegwitUtxos(); const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); 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 3c0051573f1..672fb10ee7f 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 @@ -10,7 +10,7 @@ import { determineUtxosForSpend, determineUtxosForSpendAll, } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection'; -import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentBtcAvailableBalanceNativeSegwit } 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'; @@ -43,7 +43,7 @@ export function useBitcoinFeesList({ recipient, utxos, }: UseBitcoinFeesListArgs) { - const { balance } = useCurrentNativeSegwitAvailableBalance(); + const { balance } = useCurrentBtcAvailableBalanceNativeSegwit(); const btcMarketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const { data: feeRates, isLoading } = useAverageBitcoinFeeRates(); diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx index f509a9495c3..ff4c6aa7fb1 100644 --- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx +++ b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx @@ -5,7 +5,7 @@ import { Stack } from 'leather-styles/jsx'; import { RouteUrls } from '@shared/route-urls'; -import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentBtcAvailableBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { Brc20Token } from '@app/query/bitcoin/bitcoin-client'; import { Brc20TokenAssetItemLayout } from './brc20-token-asset-item.layout'; @@ -16,7 +16,7 @@ interface Brc20TokenAssetListProps { } export function Brc20TokenAssetList({ tokens, variant }: Brc20TokenAssetListProps) { const navigate = useNavigate(); - const { balance } = useCurrentNativeSegwitAvailableBalance(); + const { balance } = useCurrentBtcAvailableBalanceNativeSegwit(); const hasPositiveBtcBalanceForFees = variant === 'send' && balance.amount.isGreaterThan(0); diff --git a/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx b/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx index af5856c23af..53a2ab24b50 100644 --- a/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx +++ b/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx @@ -16,27 +16,27 @@ import { parseCryptoAssetBalance } from './crypto-asset-item.utils'; interface CryptoAssetItemLayoutProps { additionalBalanceInfo?: ReactNode; - additionalUsdBalanceInfo?: ReactNode; + additionalBalanceInfoAsFiat?: ReactNode; address?: string; assetBalance: CryptoAssetBalances; + balanceAsFiat?: string; icon: React.ReactNode; isLoading?: boolean; name: string; onClick?(): void; rightElement?: React.ReactNode; - usdBalance?: string; } export function CryptoAssetItemLayout({ additionalBalanceInfo, - additionalUsdBalanceInfo, + additionalBalanceInfoAsFiat, address = '', assetBalance, + balanceAsFiat, icon, isLoading = false, name, onClick, rightElement, - usdBalance, }: CryptoAssetItemLayoutProps) { const { balance, dataTestId, formattedBalance } = parseCryptoAssetBalance(assetBalance); const title = spamFilter(capitalize(name)); @@ -68,9 +68,9 @@ export function CryptoAssetItemLayout({ - {balance.availableBalance.amount.toNumber() > 0 && address ? usdBalance : null} + {balance.availableBalance.amount.toNumber() > 0 && address ? balanceAsFiat : null} - {additionalUsdBalanceInfo} + {additionalBalanceInfoAsFiat} diff --git a/src/app/components/loaders/btc-balance-loader.tsx b/src/app/components/loaders/btc-balance-loader.tsx index 76a3c56c068..09969971af1 100644 --- a/src/app/components/loaders/btc-balance-loader.tsx +++ b/src/app/components/loaders/btc-balance-loader.tsx @@ -1,12 +1,12 @@ import { BtcCryptoAssetBalance } from '@leather-wallet/models'; -import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; interface BtcBalanceLoaderProps { address: string; children(balance: BtcCryptoAssetBalance, isInitialLoading: boolean): React.ReactNode; } export function BtcBalanceLoader({ address, children }: BtcBalanceLoaderProps) { - const { btcCryptoAssetBalance, isInitialLoading } = useNativeSegwitBtcCryptoAssetBalance(address); + const { btcCryptoAssetBalance, isInitialLoading } = useBtcCryptoAssetBalanceNativeSegwit(address); return children(btcCryptoAssetBalance, isInitialLoading); } diff --git a/src/app/features/asset-list/asset-list.tsx b/src/app/features/asset-list/asset-list.tsx index d1d79ceb1c4..c526d7e2e2c 100644 --- a/src/app/features/asset-list/asset-list.tsx +++ b/src/app/features/asset-list/asset-list.tsx @@ -27,11 +27,11 @@ import { PendingBrc20TransferList } from '../pending-brc-20-transfers/pending-br import { AddStacksLedgerKeysItem } from './components/add-stacks-ledger-keys-item'; import { BtcBalanceListItem } from './components/btc-balance-list-item'; import { ConnectLedgerAssetBtn } from './components/connect-ledger-asset-button'; -import { StacksBalanceListItem } from './components/stacks-balance-list-item'; import { StacksFungibleTokenAssetList } from './components/stacks-fungible-token-asset-list'; import { StacksUnsupportedTokenAssetList } from './components/stacks-unsupported-token-asset-list'; +import { StxBalanceListItem } from './components/stx-balance-list-item'; -export function AssetsList() { +export function AssetList() { const hasBitcoinLedgerKeys = useHasBitcoinLedgerKeychain(); const network = useCurrentNetwork(); const { whenWallet } = useWalletType(); @@ -84,7 +84,7 @@ export function AssetsList() { }> {account => ( <> - + {tokens => } diff --git a/src/app/features/asset-list/components/btc-balance-list-item.tsx b/src/app/features/asset-list/components/btc-balance-list-item.tsx index c52199982cd..0832542ef1f 100644 --- a/src/app/features/asset-list/components/btc-balance-list-item.tsx +++ b/src/app/features/asset-list/components/btc-balance-list-item.tsx @@ -28,11 +28,11 @@ export function BtcBalanceListItem({ } isLoading={isLoading} name={btcCryptoAssetInfo.name} rightElement={rightElement} - usdBalance={availableBalanceAsFiat} /> ); } diff --git a/src/app/features/asset-list/components/stacks-balance-list-item.tsx b/src/app/features/asset-list/components/stacks-balance-list-item.tsx deleted file mode 100644 index bab8510c5b7..00000000000 --- a/src/app/features/asset-list/components/stacks-balance-list-item.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance'; - -import { StacksBalanceListItemLayout } from './stacks-balance-list-item.layout'; - -interface StacksBalanceListItemProps { - address: string; -} -export function StacksBalanceListItem({ address }: StacksBalanceListItemProps) { - const balanceDetails = useStxBalance(); - return ( - - ); -} diff --git a/src/app/features/asset-list/components/stacks-balance-list-item.layout.stories.tsx b/src/app/features/asset-list/components/stx-balance-list-item.layout.stories.tsx similarity index 92% rename from src/app/features/asset-list/components/stacks-balance-list-item.layout.stories.tsx rename to src/app/features/asset-list/components/stx-balance-list-item.layout.stories.tsx index 32e8341b0b4..f7d19d348de 100644 --- a/src/app/features/asset-list/components/stacks-balance-list-item.layout.stories.tsx +++ b/src/app/features/asset-list/components/stx-balance-list-item.layout.stories.tsx @@ -2,12 +2,12 @@ import { TooltipProvider } from '@radix-ui/react-tooltip'; import { Meta, StoryObj } from '@storybook/react'; import BigNumber from 'bignumber.js'; -import { StacksBalanceListItemLayout } from './stacks-balance-list-item.layout'; +import { StacksBalanceListItemLayout } from './stx-balance-list-item.layout'; const meta: Meta = { component: StacksBalanceListItemLayout, tags: ['autodocs'], - title: 'Feature/StacksBalanceListItem', + title: 'Feature/StxBalanceListItem', argTypes: {}, parameters: {}, decorators: [ diff --git a/src/app/features/asset-list/components/stacks-balance-list-item.layout.tsx b/src/app/features/asset-list/components/stx-balance-list-item.layout.tsx similarity index 96% rename from src/app/features/asset-list/components/stacks-balance-list-item.layout.tsx rename to src/app/features/asset-list/components/stx-balance-list-item.layout.tsx index c706c43fbe5..c96f31df9fc 100644 --- a/src/app/features/asset-list/components/stacks-balance-list-item.layout.tsx +++ b/src/app/features/asset-list/components/stx-balance-list-item.layout.tsx @@ -44,7 +44,7 @@ export function StacksBalanceListItemLayout(props: StacksBalanceListItemLayoutPr usdBalance={stxEffectiveUsdBalance} address={address} additionalBalanceInfo={stxAdditionalBalanceInfo} - additionalUsdBalanceInfo={stxAdditionalUsdBalanceInfo} + additionalBalanceInfoAsFiat={stxAdditionalUsdBalanceInfo} icon={} isLoading={isInitialLoading} /> diff --git a/src/app/features/asset-list/components/stx-balance-list-item.tsx b/src/app/features/asset-list/components/stx-balance-list-item.tsx new file mode 100644 index 00000000000..afb80bd5f64 --- /dev/null +++ b/src/app/features/asset-list/components/stx-balance-list-item.tsx @@ -0,0 +1,52 @@ +import { styled } from 'leather-styles/jsx'; + +import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; +import { i18nFormatCurrency } from '@app/common/money/format-money'; +import { ftDecimals } from '@app/common/stacks-utils'; +import { CryptoAssetItemLayout } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; +import { stxCryptoAssetInfo } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils'; +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; +import { BulletOperator } from '@app/ui/components/bullet-separator/bullet-separator'; +import { Caption } from '@app/ui/components/typography/caption'; + +interface StxBalanceListItemProps { + address: string; +} +export function StxBalanceListItem({ address }: StxBalanceListItemProps) { + const marketData = useCryptoCurrencyMarketDataMeanAverage('STX'); + const { data: stxCryptoAssetBalance, isInitialLoading } = useStxCryptoAssetBalance(address); + + if (!stxCryptoAssetBalance) return null; + + const { availableBalance, lockedBalance } = stxCryptoAssetBalance; + const showAdditionalInfo = lockedBalance.amount.isGreaterThan(0); + + const lockedBalanceAsFiat = i18nFormatCurrency( + baseCurrencyAmountInQuote(lockedBalance, marketData) + ); + const availableBalanceAsFiat = i18nFormatCurrency( + baseCurrencyAmountInQuote(availableBalance, marketData) + ); + const additionalBalanceInfo = ( + + + {ftDecimals(lockedBalance.amount, lockedBalance.decimals || 0)} locked + + ); + const additionalBalanceInfoAsFiat = {lockedBalanceAsFiat} locked; + + return ( + } + isLoading={isInitialLoading} + name={stxCryptoAssetInfo.name} + /> + ); +} diff --git a/src/app/features/bitcoin-choose-fee/bitcoin-choose-fee.tsx b/src/app/features/bitcoin-choose-fee/bitcoin-choose-fee.tsx index c2a0e023b0a..ca8d932c055 100644 --- a/src/app/features/bitcoin-choose-fee/bitcoin-choose-fee.tsx +++ b/src/app/features/bitcoin-choose-fee/bitcoin-choose-fee.tsx @@ -10,7 +10,7 @@ import { formatMoney } from '@app/common/money/format-money'; import { BitcoinCustomFee } from '@app/components/bitcoin-custom-fee/bitcoin-custom-fee'; import { MAX_FEE_RATE_MULTIPLIER } from '@app/components/bitcoin-custom-fee/hooks/use-bitcoin-custom-fee'; import { OnChooseFeeArgs } from '@app/components/bitcoin-fees-list/bitcoin-fees-list'; -import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentBtcAvailableBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { AvailableBalance } from '@app/ui/components/containers/footers/available-balance'; import { BitcoinChooseFeeLayout } from './components/bitcoin-choose-fee.layout'; @@ -47,7 +47,7 @@ export function BitcoinChooseFee({ maxRecommendedFeeRate = 0, ...rest }: BitcoinChooseFeeProps) { - const { balance } = useCurrentNativeSegwitAvailableBalance(); + const { balance } = useCurrentBtcAvailableBalanceNativeSegwit(); const hasAmount = amount.amount.isGreaterThan(0); const [customFeeInitialValue, setCustomFeeInitialValue] = useState(recommendedFeeRate); diff --git a/src/app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend.ts b/src/app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend.ts index 48825a68969..01a3764ae79 100644 --- a/src/app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend.ts +++ b/src/app/features/bitcoin-choose-fee/hooks/use-validate-bitcoin-spend.ts @@ -3,11 +3,11 @@ import { useState } from 'react'; import { Money, createMoney } from '@shared/models/money.model'; import { subtractMoney, sumMoney } from '@app/common/money/calculate-money'; -import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentBtcAvailableBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; export function useValidateBitcoinSpend(amount?: Money, isSendingMax?: boolean) { const [showInsufficientBalanceError, setShowInsufficientBalanceError] = useState(false); - const { balance } = useCurrentNativeSegwitAvailableBalance(); + const { balance } = useCurrentBtcAvailableBalanceNativeSegwit(); return { showInsufficientBalanceError, diff --git a/src/app/features/collectibles/components/taproot-balance-displayer.tsx b/src/app/features/collectibles/components/taproot-balance-displayer.tsx index e94fe3f89b1..02bf8a181cf 100644 --- a/src/app/features/collectibles/components/taproot-balance-displayer.tsx +++ b/src/app/features/collectibles/components/taproot-balance-displayer.tsx @@ -1,5 +1,5 @@ import { formatMoney } from '@app/common/money/format-money'; -import { useCurrentTaprootAccountBalance } from '@app/query/bitcoin/balance/btc-taproot-balance.hooks'; +import { useCurrentTaprootAccountBalance } from '@app/query/bitcoin/balance/btc-balance-taproot.hooks'; import { useRecoverUninscribedTaprootUtxosFeatureEnabled } from '@app/query/common/remote-config/remote-config.query'; import { Link } from '@app/ui/components/link/link'; import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; 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 bd3a48330bf..aeff83fddac 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 @@ -22,7 +22,7 @@ import { MAX_FEE_RATE_MULTIPLIER } from '@app/components/bitcoin-custom-fee/hook import { useBitcoinFeesList } from '@app/components/bitcoin-fees-list/use-bitcoin-fees-list'; import { useToast } from '@app/features/toasts/use-toast'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.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'; @@ -51,7 +51,7 @@ export function useBtcIncreaseFee(btcTx: BitcoinTx) { [btcTx.vin.length, btcTx.vout.length, recipient] ); - const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance(currentBitcoinAddress); + const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit(currentBitcoinAddress); const sendingAmount = getBitcoinTxValue(currentBitcoinAddress, btcTx); const { feesList } = useBitcoinFeesList({ amount: createMoney(btcToSat(sendingAmount), 'BTC'), diff --git a/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx b/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx index 57bdd9bd78c..77236b4655a 100644 --- a/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx +++ b/src/app/features/dialogs/increase-fee-dialog/increase-btc-fee-dialog.tsx @@ -15,7 +15,7 @@ import { getBitcoinTxValue } from '@app/common/transactions/bitcoin/utils'; import { BitcoinCustomFeeInput } from '@app/components/bitcoin-custom-fee/bitcoin-custom-fee-input'; import { BitcoinTransactionItem } from '@app/components/bitcoin-transaction-item/bitcoin-transaction-item'; import { LoadingSpinner } from '@app/components/loading-spinner'; -import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { Dialog } from '@app/ui/components/containers/dialog/dialog'; import { Footer } from '@app/ui/components/containers/footers/footer'; @@ -34,7 +34,7 @@ export function IncreaseBtcFeeDialog() { const btcTx = tx; const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); const currentBitcoinAddress = nativeSegwitSigner.address; - const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance(currentBitcoinAddress); + const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit(currentBitcoinAddress); const { isBroadcasting, sizeInfo, onSubmit, validationSchema, recipient } = useBtcIncreaseFee(btcTx); diff --git a/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx b/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx index bd4291f0722..7bb2cb0c810 100644 --- a/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx +++ b/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx @@ -9,7 +9,6 @@ import * as yup from 'yup'; import { RouteUrls } from '@shared/route-urls'; import { useRefreshAllAccountData } from '@app/common/hooks/account/use-refresh-all-account-data'; -import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance'; import { LoadingKeys, useLoading } from '@app/common/hooks/use-loading'; import { microStxToStx, stxToMicroStx } from '@app/common/money/unit-conversion'; import { stacksValue } from '@app/common/stacks-utils'; @@ -19,7 +18,7 @@ import { LoadingSpinner } from '@app/components/loading-spinner'; import { StacksTransactionItem } from '@app/components/stacks-transaction-item/stacks-transaction-item'; import { useStacksBroadcastTransaction } from '@app/features/stacks-transaction-request/hooks/use-stacks-broadcast-transaction'; import { useToast } from '@app/features/toasts/use-toast'; -import { useCurrentStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useSubmittedTransactionsActions } from '@app/store/submitted-transactions/submitted-transactions.hooks'; import { useRawDeserializedTxState, useRawTxIdState } from '@app/store/transactions/raw.hooks'; import { Dialog } from '@app/ui/components/containers/dialog/dialog'; @@ -43,8 +42,7 @@ export function IncreaseStxFeeDialog() { const refreshAccountData = useRefreshAllAccountData(); const tx = useSelectedTx(); const [, setTxId] = useRawTxIdState(); - const { data: balances } = useCurrentStacksAccountBalances(); - const { availableBalance } = useStxBalance(); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); const submittedTransactionsActions = useSubmittedTransactionsActions(); const rawTx = useRawDeserializedTxState(); const { stacksBroadcastTransaction } = useStacksBroadcastTransaction('STX'); @@ -81,7 +79,7 @@ export function IncreaseStxFeeDialog() { if (!tx || !fee) return ; - const validationSchema = yup.object({ fee: stxFeeValidator(availableBalance) }); + const validationSchema = yup.object({ fee: stxFeeValidator(availableUnlockedBalance) }); const onClose = () => { setRawTxId(null); @@ -132,10 +130,13 @@ export function IncreaseStxFeeDialog() { {tx && } - {balances?.stx.unlockedStx.amount && ( + {availableUnlockedBalance?.amount && ( Balance: - {stacksValue({ value: availableBalance.amount, fixedDecimals: true })} + {stacksValue({ + value: availableUnlockedBalance.amount, + fixedDecimals: true, + })} )} 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 20b863d3272..dc344607006 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 @@ -8,7 +8,7 @@ import { noop } from '@shared/utils'; import { usePressable } from '@app/components/item-hover'; import { StatusPending } from '@app/components/status-pending'; import { StatusReady } from '@app/components/status-ready'; -import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentBtcAvailableBalanceNativeSegwit } 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'; @@ -90,7 +90,7 @@ function PendingBrcTransfer({ order }: PendingBrcTransferProps) { const [component, bind] = usePressable(order.status === 'ready'); const navigate = useNavigate(); const ordinalsbotClient = useOrdinalsbotClient(); - const { balance } = useCurrentNativeSegwitAvailableBalance(); + const { balance } = useCurrentBtcAvailableBalanceNativeSegwit(); const hasPositiveBtcBalanceForFees = balance.amount.isGreaterThan(0); 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 4512fa76068..05ba1136a5b 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 @@ -13,7 +13,7 @@ import { useToast } from '@app/features/toasts/use-toast'; import { useCurrentTaprootAccountBalance, useCurrentTaprootAccountUninscribedUtxos, -} from '@app/query/bitcoin/balance/btc-taproot-balance.hooks'; +} 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'; import { Link } from '@app/ui/components/link/link'; 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 e2400f60a73..e3351b64d5a 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 @@ -7,7 +7,7 @@ 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-taproot-balance.hooks'; +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 { useBitcoinScureLibNetworkConfig } from '@app/store/accounts/blockchain/bitcoin/bitcoin-keychain'; diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx b/src/app/features/selectable-asset-list/crypto-asset-list-item.tsx similarity index 88% rename from src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx rename to src/app/features/selectable-asset-list/crypto-asset-list-item.tsx index c0c73b970c5..733af8c68b5 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list-item.tsx +++ b/src/app/features/selectable-asset-list/crypto-asset-list-item.tsx @@ -1,6 +1,6 @@ import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model'; -import { CryptoAssetItemLayout } from '../crypto-asset-item/crypto-asset-item.layout'; +import { CryptoAssetItemLayout } from '../../components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; import { CryptoCurrencyAssetIcon } from './crypto-currency-asset-icon'; import { FungibleTokenAssetItem } from './fungible-token-asset-item'; diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx b/src/app/features/selectable-asset-list/crypto-asset-list.tsx similarity index 95% rename from src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx rename to src/app/features/selectable-asset-list/crypto-asset-list.tsx index 4af494db221..5a2c4a9d3d0 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/crypto-asset-list.tsx +++ b/src/app/features/selectable-asset-list/crypto-asset-list.tsx @@ -11,7 +11,7 @@ import { Brc20TokensLoader } from '@app/components/loaders/brc20-tokens-loader'; import { BtcBalanceLoader } from '@app/components/loaders/btc-balance-loader'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; -import { CryptoAssetItemLayout } from '../crypto-asset-item/crypto-asset-item.layout'; +import { CryptoAssetItemLayout } from '../../components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; import { CryptoAssetListItem } from './crypto-asset-list-item'; interface CryptoAssetListProps { diff --git a/src/app/components/crypto-assets/choose-crypto-asset/crypto-currency-asset-icon.tsx b/src/app/features/selectable-asset-list/crypto-currency-asset-icon.tsx similarity index 100% rename from src/app/components/crypto-assets/choose-crypto-asset/crypto-currency-asset-icon.tsx rename to src/app/features/selectable-asset-list/crypto-currency-asset-icon.tsx diff --git a/src/app/components/crypto-assets/choose-crypto-asset/fungible-token-asset-item.tsx b/src/app/features/selectable-asset-list/fungible-token-asset-item.tsx similarity index 79% rename from src/app/components/crypto-assets/choose-crypto-asset/fungible-token-asset-item.tsx rename to src/app/features/selectable-asset-list/fungible-token-asset-item.tsx index 7be8034c33e..61876fe5430 100644 --- a/src/app/components/crypto-assets/choose-crypto-asset/fungible-token-asset-item.tsx +++ b/src/app/features/selectable-asset-list/fungible-token-asset-item.tsx @@ -2,7 +2,7 @@ import { FlexProps } from 'leather-styles/jsx'; import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; -import { StacksFungibleTokenAssetItemLayout } from '../stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout'; +import { StacksFungibleTokenAssetItemLayout } from '../../components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout'; interface FungibleTokenAssetItemProps extends FlexProps { assetBalance: StacksFungibleTokenAssetBalance; diff --git a/src/app/components/crypto-assets/choose-crypto-asset/send-btc-disabled.tsx b/src/app/features/selectable-asset-list/send-btc-disabled.tsx similarity index 100% rename from src/app/components/crypto-assets/choose-crypto-asset/send-btc-disabled.tsx rename to src/app/features/selectable-asset-list/send-btc-disabled.tsx diff --git a/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts b/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts index e79e95cbba4..b4fb9e0f94f 100644 --- a/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts +++ b/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts @@ -11,7 +11,7 @@ import { initialSearchParams } from '@app/common/initial-search-params'; import { stxToMicroStx } from '@app/common/money/unit-conversion'; import { validateStacksAddress } from '@app/common/stacks-utils'; import { TransactionErrorReason } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useContractInterface } from '@app/query/stacks/contract/contract.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; @@ -27,12 +27,12 @@ export function useTransactionError() { const { values } = useFormikContext(); const currentAccount = useCurrentStacksAccount(); - const { data: balances } = useCurrentStacksAccountBalances(); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); return useMemo(() => { if (!origin) return TransactionErrorReason.ExpiredRequest; - if (!transactionRequest || !balances || !currentAccount) { + if (!transactionRequest || !availableUnlockedBalance || !currentAccount) { return TransactionErrorReason.Generic; } @@ -42,14 +42,14 @@ export function useTransactionError() { if ((contractInterface as any)?.isError) return TransactionErrorReason.NoContract; } - if (balances && !getIsMultisig()) { - const zeroBalance = balances?.stx.unlockedStx.amount.toNumber() === 0; + if (availableUnlockedBalance && !getIsMultisig()) { + const zeroBalance = availableUnlockedBalance.amount.toNumber() === 0; if (transactionRequest.txType === TransactionTypes.STXTransfer) { if (zeroBalance) return TransactionErrorReason.StxTransferInsufficientFunds; const transferAmount = new BigNumber(transactionRequest.amount); - if (transferAmount.gte(balances?.stx.unlockedStx.amount)) + if (transferAmount.gte(availableUnlockedBalance.amount)) return TransactionErrorReason.StxTransferInsufficientFunds; } @@ -57,10 +57,17 @@ export function useTransactionError() { if (zeroBalance) return TransactionErrorReason.FeeInsufficientFunds; const feeValue = stxToMicroStx(values.fee); - if (feeValue.gte(balances?.stx.unlockedStx.amount)) + if (feeValue.gte(availableUnlockedBalance.amount)) return TransactionErrorReason.FeeInsufficientFunds; } } return; - }, [origin, transactionRequest, balances, currentAccount, contractInterface, values.fee]); + }, [ + origin, + transactionRequest, + availableUnlockedBalance, + currentAccount, + contractInterface, + values.fee, + ]); } diff --git a/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx b/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx index f3383c43070..eb2d2958fb5 100644 --- a/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx +++ b/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx @@ -26,7 +26,7 @@ import { PostConditionModeWarning } from '@app/features/stacks-transaction-reque import { PostConditions } from '@app/features/stacks-transaction-request/post-conditions/post-conditions'; import { StxTransferDetails } from '@app/features/stacks-transaction-request/stx-transfer-details/stx-transfer-details'; import { TransactionError } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; @@ -55,7 +55,7 @@ export function StacksTransactionSigner({ const transactionRequest = useTransactionRequestState(); const { data: stxFees } = useCalculateStacksTxFees(stacksTransaction); const analytics = useAnalytics(); - const { data: stacksBalances } = useCurrentStacksAccountBalances(); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); const navigate = useNavigate(); const { data: nextNonce } = useNextNonce(); const { search } = useLocation(); @@ -78,7 +78,7 @@ export function StacksTransactionSigner({ const validationSchema = !transactionRequest.sponsored && !disableFeeSelection && !isMultisig ? yup.object({ - fee: stxFeeValidator(stacksBalances?.stx.unlockedStx), + fee: stxFeeValidator(availableUnlockedBalance), nonce: nonceValidator, }) : yup.object({ diff --git a/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx b/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx index f88783c820f..0860c486752 100644 --- a/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx +++ b/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx @@ -12,7 +12,7 @@ import { useScrollLock } from '@app/common/hooks/use-scroll-lock'; import { stacksValue } from '@app/common/stacks-utils'; import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog'; import { ErrorMessage } from '@app/features/stacks-transaction-request/transaction-error/error-message'; -import { useCurrentStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; import { Button } from '@app/ui/components/button/button'; @@ -59,7 +59,7 @@ export const FeeInsufficientFundsErrorMessage = memo(props => { export const StxTransferInsufficientFundsErrorMessage = memo(props => { const pendingTransaction = useTransactionRequestState(); - const { data: balance } = useCurrentStacksAccountBalances(); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); return ( { Current balance - {balance + {availableUnlockedBalance ? stacksValue({ - value: balance.stx.unlockedStx.amount, + value: availableUnlockedBalance.amount, withTicker: true, }) : '--'} diff --git a/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-actions.tsx b/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-actions.tsx index 809e7c98e3e..40affce4377 100644 --- a/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-actions.tsx +++ b/src/app/pages/bitcoin-contract-request/components/bitcoin-contract-request-actions.tsx @@ -1,6 +1,6 @@ import { BitcoinContractRequestSelectors } from '@tests/selectors/bitcoin-contract-request.selectors'; -import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { Button } from '@app/ui/components/button/button'; interface BitcoinContractRequestActionsProps { @@ -17,7 +17,7 @@ export function BitcoinContractRequestActions({ onRejectBitcoinContractOffer, onAcceptBitcoinContractOffer, }: BitcoinContractRequestActionsProps) { - const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance(bitcoinAddress); + const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit(bitcoinAddress); const canAccept = btcCryptoAssetBalance.availableBalance.amount.isGreaterThan(requiredAmount); return ( diff --git a/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx b/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx index 6010708f8f1..d9766d2cc4d 100644 --- a/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx +++ b/src/app/pages/fund/choose-asset-to-fund/choose-asset-to-fund.tsx @@ -9,7 +9,7 @@ import { isDefined } from '@shared/utils'; import { useStxCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance'; import { useWalletType } from '@app/common/use-wallet-type'; -import { CryptoAssetList } from '@app/components/crypto-assets/choose-crypto-asset/crypto-asset-list'; +import { CryptoAssetList } from '@app/features/selectable-asset-list/crypto-asset-list'; import { useCheckLedgerBlockchainAvailable } from '@app/store/accounts/blockchain/utils'; import { Card } from '@app/ui/layout/card/card'; import { Page } from '@app/ui/layout/page/page.layout'; diff --git a/src/app/pages/fund/fund.tsx b/src/app/pages/fund/fund.tsx index 7c98a83afcd..438103dcf1a 100644 --- a/src/app/pages/fund/fund.tsx +++ b/src/app/pages/fund/fund.tsx @@ -1,15 +1,14 @@ import { Outlet, useParams } from 'react-router-dom'; -import type { BtcCryptoAssetBalance } from '@leather-wallet/models'; +import type { BtcCryptoAssetBalance, StxCryptoAssetBalance } from '@leather-wallet/models'; import type { Blockchains } from '@shared/models/blockchain.model'; -import type { StacksCryptoCurrencyAssetBalance } from '@shared/models/crypto-asset-balance.model'; import type { CryptoCurrencies } from '@shared/models/currencies.model'; import { RouteUrls } from '@shared/route-urls'; -import { useStxCryptoCurrencyAssetBalance } from '@app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance'; import { FullPageLoadingSpinner } from '@app/components/loading-spinner'; -import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSignerNullable } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; @@ -18,7 +17,7 @@ import { FiatProvidersList } from './fiat-providers-list'; interface FundCryptoCurrencyInfo { address?: string; - balance?: BtcCryptoAssetBalance | StacksCryptoCurrencyAssetBalance; + balance?: BtcCryptoAssetBalance | StxCryptoAssetBalance; blockchain: Blockchains; route: string; symbol: CryptoCurrencies; @@ -27,10 +26,12 @@ interface FundCryptoCurrencyInfo { export function FundPage() { const currentStxAccount = useCurrentStacksAccount(); const bitcoinSigner = useCurrentAccountNativeSegwitIndexZeroSignerNullable(); - const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance( + const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit( + bitcoinSigner?.address ?? '' + ); + const { data: stxCryptoAssetBalance } = useStxCryptoAssetBalance( currentStxAccount?.address ?? '' ); - const stxCryptoCurrencyAssetBalance = useStxCryptoCurrencyAssetBalance(); const { currency = 'STX' } = useParams(); const fundCryptoCurrencyMap: Record = { @@ -43,7 +44,7 @@ export function FundPage() { }, STX: { address: currentStxAccount?.address, - balance: stxCryptoCurrencyAssetBalance, + balance: stxCryptoAssetBalance, blockchain: 'Stacks', route: RouteUrls.ReceiveStx, symbol: currency, diff --git a/src/app/pages/home/components/send-button.tsx b/src/app/pages/home/components/send-button.tsx index 58df5cc0150..60fbf605d12 100644 --- a/src/app/pages/home/components/send-button.tsx +++ b/src/app/pages/home/components/send-button.tsx @@ -8,10 +8,8 @@ import { RouteUrls } from '@shared/route-urls'; import { useWalletType } from '@app/common/use-wallet-type'; import { whenPageMode } from '@app/common/utils'; import { openIndexPageInNewTab } from '@app/common/utils/open-in-new-tab'; -import { - useStacksCryptoCurrencyAssetBalance, - useTransferableStacksFungibleTokenAssetBalances, -} from '@app/query/stacks/balance/stacks-ft-balances.hooks'; +import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { IconButton } from '@app/ui/components/icon-button/icon-button'; import { SendIcon } from '@app/ui/icons'; @@ -20,9 +18,9 @@ function SendButtonSuspense() { const navigate = useNavigate(); const { whenWallet } = useWalletType(); const account = useCurrentStacksAccount(); - const stxAssetBalance = useStacksCryptoCurrencyAssetBalance(account?.address ?? ''); + const { data: stxCryptoAssetBalance } = useStxCryptoAssetBalance(account?.address ?? ''); const ftAssets = useTransferableStacksFungibleTokenAssetBalances(account?.address ?? ''); - const isDisabled = !stxAssetBalance && ftAssets?.length === 0; + const isDisabled = !stxCryptoAssetBalance && ftAssets?.length === 0; return ( - } /> + } /> }> {homePageModalRoutes} diff --git a/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx b/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx index 1bfa523827f..0b7c3caa9ca 100644 --- a/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx +++ b/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx @@ -7,7 +7,7 @@ import { RouteUrls } from '@shared/route-urls'; import { useAllTransferableCryptoAssetBalances } from '@app/common/hooks/use-transferable-asset-balances.hooks'; import { useWalletType } from '@app/common/use-wallet-type'; -import { CryptoAssetList } from '@app/components/crypto-assets/choose-crypto-asset/crypto-asset-list'; +import { CryptoAssetList } from '@app/features/selectable-asset-list/crypto-asset-list'; import { useToast } from '@app/features/toasts/use-toast'; import { useConfigBitcoinSendEnabled } from '@app/query/common/remote-config/remote-config.query'; import { useCheckLedgerBlockchainAvailable } from '@app/store/accounts/blockchain/utils'; 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 c36e6bf1382..f60b5dd0f94 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 @@ -9,7 +9,7 @@ import { CryptoCurrencies } from '@shared/models/currencies.model'; import { formatMoney } from '@app/common/money/format-money'; import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog'; -import { useNativeSegwitBtcCryptoAssetBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; @@ -38,7 +38,7 @@ export function BtcSendForm() { const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); // TODO: Asset refactor: need asset here - const { btcCryptoAssetBalance } = useNativeSegwitBtcCryptoAssetBalance( + const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit( nativeSegwitSigner.address ); diff --git a/src/app/pages/send/send-crypto-asset-form/form/btc/use-btc-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/btc/use-btc-send-form.tsx index 4e2411489cc..9ee3743d782 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/btc/use-btc-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/btc/use-btc-send-form.tsx @@ -24,7 +24,7 @@ import { } from '@app/common/validation/forms/currency-validators'; import { useUpdatePersistedSendFormValues } from '@app/features/popup-send-form-restoration/use-update-persisted-send-form-values'; import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/utxos-by-address.hooks'; -import { useCurrentNativeSegwitAvailableBalance } from '@app/query/bitcoin/balance/btc-native-segwit-balance.hooks'; +import { useCurrentBtcAvailableBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; @@ -37,7 +37,7 @@ export function useBtcSendForm() { const currentNetwork = useCurrentNetwork(); const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); const { data: utxos = [], refetch } = useCurrentNativeSegwitUtxos(); - const { balance } = useCurrentNativeSegwitAvailableBalance(); + const { balance } = useCurrentBtcAvailableBalanceNativeSegwit(); const sendFormNavigate = useSendFormNavigate(); const calcMaxSpend = useCalculateMaxBitcoinSpend(); const { onFormStateChange } = useUpdatePersistedSendFormValues(); diff --git a/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx index 7da098a7a22..46ecaacaa77 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx @@ -7,7 +7,6 @@ import { STX_DECIMALS } from '@shared/constants'; import { logger } from '@shared/logger'; import { StacksSendFormValues } from '@shared/models/form.model'; -import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance'; import { convertAmountToBaseUnit } from '@app/common/money/calculate-money'; import { stxAmountValidator, @@ -15,6 +14,7 @@ import { } from '@app/common/validation/forms/amount-validators'; import { stxFeeValidator } from '@app/common/validation/forms/fee-validators'; import { useUpdatePersistedSendFormValues } from '@app/features/popup-send-form-restoration/use-update-persisted-send-form-values'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useStacksValidateFeeByNonce } from '@app/query/stacks/mempool/mempool.hooks'; import { @@ -30,31 +30,29 @@ export function useStxSendForm() { const { data: stxFees } = useCalculateStacksTxFees(unsignedTx); const generateTx = useGenerateStxTokenTransferUnsignedTx(); const { onFormStateChange } = useUpdatePersistedSendFormValues(); - const sendFormNavigate = useSendFormNavigate(); const { changeFeeByNonce } = useStacksValidateFeeByNonce(); - - const { availableBalance: availableStxBalance } = useStxBalance(); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); const sendMaxBalance = useMemo( () => convertAmountToBaseUnit( - availableStxBalance.amount.minus(stxFees?.estimates[1].fee.amount || 0), + availableUnlockedBalance.amount.minus(stxFees?.estimates[1].fee.amount || 0), STX_DECIMALS ), - [availableStxBalance, stxFees] + [availableUnlockedBalance.amount, stxFees?.estimates] ); const { initialValues, checkFormValidation, recipient, memo, nonce } = useStacksCommonSendForm({ symbol: 'STX', - availableTokenBalance: availableStxBalance, + availableTokenBalance: availableUnlockedBalance, }); // FIXME - I don't this this is the fee, should be value.fee or something from the form - const fee = stxFeeValidator(availableStxBalance); + const fee = stxFeeValidator(availableUnlockedBalance); return { - availableStxBalance, + availableUnlockedBalance, initialValues, onFormStateChange, sendMaxBalance, @@ -62,10 +60,10 @@ export function useStxSendForm() { fee, validationSchema: yup.object({ - amount: stxAmountValidator(availableStxBalance).concat( - stxAvailableBalanceValidator(availableStxBalance) + amount: stxAmountValidator(availableUnlockedBalance).concat( + stxAvailableBalanceValidator(availableUnlockedBalance) ), - fee: stxFeeValidator(availableStxBalance), + fee: stxFeeValidator(availableUnlockedBalance), recipient, memo, nonce, diff --git a/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx b/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx index 01fec10cac8..d920c347341 100644 --- a/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx +++ b/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx @@ -4,11 +4,11 @@ import { Outlet, Route } from 'react-router-dom'; import { RouteUrls } from '@shared/route-urls'; import { BroadcastErrorDialog } from '@app/components/broadcast-error-dialog/broadcast-error-dialog'; -import { SendBtcDisabled } from '@app/components/crypto-assets/choose-crypto-asset/send-btc-disabled'; import { FullPageWithHeaderLoadingSpinner } from '@app/components/loading-spinner'; import { EditNonceDialog } from '@app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog'; import { ledgerBitcoinTxSigningRoutes } from '@app/features/ledger/flows/bitcoin-tx-signing/ledger-bitcoin-sign-tx-container'; import { ledgerStacksTxSigningRoutes } from '@app/features/ledger/flows/stacks-tx-signing/ledger-sign-stacks-tx-container'; +import { SendBtcDisabled } from '@app/features/selectable-asset-list/send-btc-disabled'; import { AccountGate } from '@app/routes/account-gate'; import { Page } from '@app/ui/layout/page/page.layout'; diff --git a/src/app/pages/transaction-request/transaction-request.tsx b/src/app/pages/transaction-request/transaction-request.tsx index 049cdd81d6f..82a08a0a414 100644 --- a/src/app/pages/transaction-request/transaction-request.tsx +++ b/src/app/pages/transaction-request/transaction-request.tsx @@ -29,7 +29,7 @@ import { PostConditions } from '@app/features/stacks-transaction-request/post-co import { StxTransferDetails } from '@app/features/stacks-transaction-request/stx-transfer-details/stx-transfer-details'; import { SubmitAction } from '@app/features/stacks-transaction-request/submit-action'; import { TransactionError } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStacksAccountBalances } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; @@ -47,7 +47,7 @@ function TransactionRequestBase() { const { data: stxFees } = useCalculateStacksTxFees(unsignedTx.transaction); const analytics = useAnalytics(); const generateUnsignedTx = useGenerateUnsignedStacksTransaction(); - const { data: stacksBalances } = useCurrentStacksAccountBalances(); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); const { data: nextNonce } = useNextNonce(); const navigate = useNavigate(); const { stacksBroadcastTransaction } = useStacksBroadcastTransaction('STX'); @@ -78,7 +78,7 @@ function TransactionRequestBase() { const validationSchema = !transactionRequest.sponsored ? yup.object({ - fee: stxFeeValidator(stacksBalances?.stx.unlockedStx), + fee: stxFeeValidator(availableUnlockedBalance), nonce: nonceValidator, }) : null; diff --git a/src/app/query/bitcoin/balance/btc-native-segwit-balance.hooks.ts b/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts similarity index 78% rename from src/app/query/bitcoin/balance/btc-native-segwit-balance.hooks.ts rename to src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts index d1a5c89b3e8..ca2c0831fd9 100644 --- a/src/app/query/bitcoin/balance/btc-native-segwit-balance.hooks.ts +++ b/src/app/query/bitcoin/balance/btc-balance-native-segwit.hooks.ts @@ -11,14 +11,14 @@ import { useGetBitcoinBalanceByAddress } from './btc-balance.hooks'; function makeBtcCryptoAssetBalance(balance: Money): BtcCryptoAssetBalance { return { availableBalance: balance, - // TODO: Asset refactor: can we determine these here? + // TODO: Asset refactor: can we determine these here or are they nec? protectedBalance: createMoney(0, 'BTC'), uneconomicalBalance: createMoney(0, 'BTC'), }; } // Balance is derived from a single query in address reuse mode -export function useNativeSegwitBtcCryptoAssetBalance(address: string) { +export function useBtcCryptoAssetBalanceNativeSegwit(address: string) { const { balance, isInitialLoading, isLoading, isFetching } = useGetBitcoinBalanceByAddress(address); const btcCryptoAssetBalance = useMemo(() => makeBtcCryptoAssetBalance(balance), [balance]); @@ -31,11 +31,7 @@ export function useNativeSegwitBtcCryptoAssetBalance(address: string) { }; } -export function useNativeSegwitAvailableBalance(address: string) { - return useGetBitcoinBalanceByAddress(address); -} - -export function useCurrentNativeSegwitAvailableBalance() { +export function useCurrentBtcAvailableBalanceNativeSegwit() { const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); return useGetBitcoinBalanceByAddress(nativeSegwitSigner.address); } diff --git a/src/app/query/bitcoin/balance/btc-taproot-balance.hooks.ts b/src/app/query/bitcoin/balance/btc-balance-taproot.hooks.ts similarity index 100% rename from src/app/query/bitcoin/balance/btc-taproot-balance.hooks.ts rename to src/app/query/bitcoin/balance/btc-balance-taproot.hooks.ts diff --git a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts index e7986efa9fc..a47479511de 100644 --- a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts +++ b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts @@ -9,10 +9,10 @@ import { type Money, createMoney } from '@shared/models/money.model'; import { isDefined } from '@shared/utils'; import { sortAssetsByName } from '@app/common/asset-utils'; -import { useStxBalance } from '@app/common/hooks/balance/stx/use-stx-balance'; import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money'; import { pullContractIdFromIdentity } from '@app/common/utils'; import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; +import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { getAvatarFallback } from '@app/ui/components/avatar/avatar'; @@ -55,7 +55,7 @@ function useMakeSwapAsset() { const account = useCurrentStacksAccount(); const { data: prices } = useAlexSdkLatestPricesQuery(); const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); - const { availableBalance: availableStxBalance } = useStxBalance(); + const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); const stacksFtAssetBalances = useTransferableStacksFungibleTokenAssetBalances( account?.address ?? '' ); @@ -85,9 +85,9 @@ function useMakeSwapAsset() { if (currency === Currency.STX) { return { ...swapAsset, - balance: availableStxBalance, + balance: availableUnlockedBalance, displayName: 'Stacks', - marketData: priceAsMarketData(principal, availableStxBalance.symbol), + marketData: priceAsMarketData(principal, availableUnlockedBalance.symbol), }; } @@ -99,7 +99,7 @@ function useMakeSwapAsset() { : priceAsMarketData(principal, tokenInfo.name), }; }, - [availableStxBalance, priceAsMarketData, prices, stacksFtAssetBalances] + [availableUnlockedBalance, priceAsMarketData, prices, stacksFtAssetBalances] ); } diff --git a/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts b/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts index 2316671b187..93f44728262 100644 --- a/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts +++ b/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts @@ -19,19 +19,10 @@ import { isFtAsset } from '../tokens/token-metadata.utils'; import { addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance, convertFtBalancesToStacksFungibleTokenAssetBalanceType, - createStacksCryptoCurrencyAssetTypeWrapper, createStacksFtCryptoAssetBalanceTypeWrapper, } from './stacks-ft-balances.utils'; -import { parseBalanceResponse } from './stx-balance.hooks'; import { useStacksAccountBalanceQuery } from './stx-balance.query'; -export function useStacksCryptoCurrencyAssetBalance(address: string) { - return useStacksAccountBalanceQuery(address, { - select: resp => - createStacksCryptoCurrencyAssetTypeWrapper(parseBalanceResponse(resp).stx.unlockedStx.amount), - }); -} - function useStacksFungibleTokenAssetBalances(address: string) { return useStacksAccountBalanceQuery(address, { select: resp => convertFtBalancesToStacksFungibleTokenAssetBalanceType(resp.fungible_tokens), diff --git a/src/app/query/stacks/balance/stx-balance.hooks.ts b/src/app/query/stacks/balance/stx-balance.hooks.ts index 82736e0cb5c..ec382710468 100644 --- a/src/app/query/stacks/balance/stx-balance.hooks.ts +++ b/src/app/query/stacks/balance/stx-balance.hooks.ts @@ -1,42 +1,59 @@ +import type { StxCryptoAssetBalance } from '@leather-wallet/models'; import BigNumber from 'bignumber.js'; -import { - AccountBalanceStxKeys, - AccountStxBalanceBigNumber, - AddressBalanceResponse, -} from '@shared/models/account.model'; +import { AccountBalanceStxKeys, type AddressBalanceResponse } from '@shared/models/account.model'; import { Money, createMoney } from '@shared/models/money.model'; +import { subtractMoney, sumMoney } from '@app/common/money/calculate-money'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { accountBalanceStxKeys } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; +import { + useMempoolTxsInboundBalance, + useMempoolTxsOutboundBalance, +} from '../mempool/mempool.hooks'; import { useStacksAccountBalanceQuery } from './stx-balance.query'; -export function parseBalanceResponse(balances: AddressBalanceResponse) { - const stxMoney = Object.fromEntries( - accountBalanceStxKeys.map(key => [ - key, - { amount: new BigNumber(balances.stx[key]), symbol: 'STX' }, - ]) +function makeStxMoney(resp: AddressBalanceResponse) { + return Object.fromEntries( + accountBalanceStxKeys.map(key => [key, { amount: new BigNumber(resp.stx[key]), symbol: 'STX' }]) ) as Record; +} + +function makeStxCryptoAssetBalance( + stxMoney: Record, + inboundBalance: Money, + outboundBalance: Money +): StxCryptoAssetBalance { + const totalBalance = createMoney(stxMoney.balance.amount, 'STX'); + const unlockedBalance = subtractMoney(stxMoney.balance, stxMoney.locked); - const stx: AccountStxBalanceBigNumber & { unlockedStx: Money } = { - ...balances.stx, - ...stxMoney, - balance: createMoney(stxMoney.balance.amount, 'STX'), - locked: createMoney(stxMoney.locked.amount, 'STX'), - unlockedStx: createMoney(stxMoney.balance.amount.minus(stxMoney.locked.amount), 'STX'), + return { + // TODO: Asset refactor: are inbound/outbound necessary? + // - Make sure to track changes to effectiveBalance, now availableBalance + // - And, previous availableBalance is now availableUnlockedBalance + availableBalance: subtractMoney(totalBalance, outboundBalance), + availableUnlockedBalance: subtractMoney(unlockedBalance, outboundBalance), + inboundBalance, + lockedBalance: createMoney(stxMoney.locked.amount, 'STX'), + outboundBalance, + pendingBalance: subtractMoney(sumMoney([totalBalance, inboundBalance]), outboundBalance), + totalBalance, + unlockedBalance, }; - return { ...balances, stx }; } -export function useStacksAccountBalances(address: string) { +export function useStxCryptoAssetBalance(address: string) { + const inboundBalance = useMempoolTxsInboundBalance(address); + const outboundBalance = useMempoolTxsOutboundBalance(address); return useStacksAccountBalanceQuery(address, { - select: resp => parseBalanceResponse(resp), + select: resp => makeStxCryptoAssetBalance(makeStxMoney(resp), inboundBalance, outboundBalance), }); } -export function useCurrentStacksAccountBalances() { +export function useCurrentStcAvailableUnlockedBalance() { const account = useCurrentStacksAccount(); - return useStacksAccountBalances(account?.address ?? ''); + return ( + useStxCryptoAssetBalance(account?.address ?? '').data?.unlockedBalance ?? createMoney(0, 'STX') + ); } diff --git a/src/app/query/stacks/mempool/mempool.hooks.ts b/src/app/query/stacks/mempool/mempool.hooks.ts index 1557b6ab0c6..5e28aaf8138 100644 --- a/src/app/query/stacks/mempool/mempool.hooks.ts +++ b/src/app/query/stacks/mempool/mempool.hooks.ts @@ -63,8 +63,31 @@ export function useCurrentAccountMempool() { return useAccountMempoolQuery(address ?? ''); } -export function useCurrentAccountMempoolTransactionsBalance() { - const address = useCurrentAccountStxAddressState(); +// TODO: Asset refactor: combine inbound/outbound into one function? +export function useMempoolTxsInboundBalance(address: string) { + const { transactions: pendingTransactions } = useStacksPendingTransactions(); + const confirmedTxs = useStacksConfirmedTransactions(); + + const pendingInboundTxs = pendingTransactions.filter(tx => { + if (confirmedTxs.some(confirmedTx => confirmedTx.nonce === tx.nonce)) { + return false; + } + return tx.tx_type === 'token_transfer' && tx.token_transfer.recipient_address === address; + }) as unknown as MempoolTokenTransferTransaction[]; + + const tokenTransferTxsBalance = pendingInboundTxs.reduce( + (acc, tx) => acc.plus(tx.token_transfer.amount), + new BigNumber(0) + ); + const pendingTxsFeesBalance = pendingInboundTxs.reduce( + (acc, tx) => acc.plus(tx.fee_rate), + new BigNumber(0) + ); + + return createMoney(tokenTransferTxsBalance.plus(pendingTxsFeesBalance), 'STX'); +} + +export function useMempoolTxsOutboundBalance(address: string) { const { transactions: pendingTransactions } = useStacksPendingTransactions(); const confirmedTxs = useStacksConfirmedTransactions(); From 84a15cb8a819a537df4b485aba170625df493c97 Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Fri, 10 May 2024 14:34:17 -0500 Subject: [PATCH 3/3] refactor: assets and asset list --- package.json | 2 +- pnpm-lock.yaml | 11 +- .../stacks-crypto-asset.utils.spec.ts | 72 ---------- .../stacks-crypto-asset.utils.ts | 19 --- .../use-stx-crypto-currency-asset-balance.ts | 8 -- .../hooks/balance/use-total-balance.tsx | 2 +- .../use-transferable-asset-balances.hooks.ts | 25 ---- src/app/common/stacks-utils.spec.ts | 16 ++- src/app/common/stacks-utils.ts | 12 ++ .../components/account/account-addresses.tsx | 2 +- .../account/bitcoin-account-loader.tsx | 47 ------- src/app/components/balance/btc-balance.tsx | 10 +- src/app/components/balance/stx-balance.tsx | 2 +- .../crypto-asset-item.layout.tsx | 36 +++-- .../crypto-asset-item.layout.utils.ts | 21 +++ .../brc20-token-asset-item.layout.tsx | 43 ------ .../brc20-token-asset-list.tsx | 41 ------ .../crypto-asset-item.utils.ts | 44 ------ .../fungible-token-asset.utils.ts | 47 ------- ...tacks-fungible-token-asset-item.layout.tsx | 60 -------- .../loaders/bitcoin-account-loader.tsx | 59 ++++++-- .../loaders/brc20-tokens-loader.tsx | 8 +- .../components/loaders/btc-balance-loader.tsx | 12 -- .../loaders/btc-crypto-asset-loader.tsx | 11 ++ .../loaders/current-bitcoin-signer-loader.tsx | 17 +++ .../loaders/stx-crypto-asset-loader.tsx | 11 ++ .../components => }/stacks-asset-avatar.tsx | 0 .../transaction/token-transfer-icon.tsx | 4 +- src/app/components/tx-asset-item.tsx | 2 +- .../stacks-transaction/ft-transfer-item.tsx | 10 +- src/app/features/asset-list/asset-list.tsx | 131 ++++++++++-------- .../brc20-token-asset-list.tsx | 47 +++++++ .../btc-crypto-asset-item-fallback.tsx | 23 +++ .../btc-crypto-asset-item.tsx} | 34 ++--- .../runes-asset-item.layout.tsx | 0 .../runes-asset-list/runes-asset-list.tsx | 0 .../src20-token-asset-item.layout.tsx | 0 .../src20-token-asset-list.tsx | 0 .../add-stacks-ledger-keys-item.tsx | 17 --- .../connect-ledger-asset-button.tsx | 8 +- ...tacks-fungible-token-asset-list.layout.tsx | 24 ---- .../stacks-fungible-token-asset-list.tsx | 15 -- .../stx-balance-list-item.layout.stories.tsx | 64 --------- .../stx-balance-list-item.layout.tsx | 52 ------- .../sip10-token-asset-item.tsx | 36 +++++ .../sip10-token-asset-item.utils.ts | 34 +++++ .../sip10-token-asset-list-unsupported.tsx} | 22 +-- .../sip10-token-asset-list.tsx | 29 ++++ .../stx-crypto-asset-item-fallback.tsx | 23 +++ .../stx-crypto-asset-item.stories.tsx | 81 +++++++++++ .../stx-crypto-asset-item.tsx} | 34 ++--- .../stx20-token-asset-item.layout.tsx | 0 .../stx20-token-asset-list.tsx | 0 .../features/collectibles/collectibles.tsx | 2 +- .../stacks/stacks-crypto-assets.tsx | 2 +- .../increase-stx-fee-dialog.tsx | 4 +- .../crypto-asset-list-item.tsx | 29 ---- .../crypto-asset-list.tsx | 70 ---------- .../crypto-currency-asset-icon.tsx | 15 -- .../fungible-token-asset-item.tsx | 20 --- .../contract-deploy-details.tsx | 6 +- .../hooks/use-transaction-error.ts | 4 +- .../fungible-post-condition-item.tsx | 5 +- .../stacks-transaction-signer.tsx | 4 +- .../transaction-error/error-messages.tsx | 4 +- .../choose-asset-to-fund.tsx | 81 ++++++----- src/app/pages/fund/fund.tsx | 19 +-- .../pages/home/components/account-actions.tsx | 1 + src/app/pages/home/components/assets.tsx | 19 +++ src/app/pages/home/components/send-button.tsx | 20 ++- src/app/pages/home/home.tsx | 4 +- src/app/pages/receive/receive-dialog.tsx | 4 +- .../choose-crypto-asset.tsx | 59 +++----- .../send-btc-disabled.tsx | 0 .../brc20-choose-fee.tsx} | 0 .../brc20-send-form-confirmation.tsx | 0 .../{brc-20 => brc20}/brc20-send-form.tsx | 0 .../{brc-20 => brc20}/use-brc20-send-form.tsx | 0 .../form/btc/btc-send-form.tsx | 24 ++-- .../sip10-token-send-form-container.tsx | 14 +- .../sip10-token-send-form.tsx | 31 +++-- .../use-sip10-send-form.tsx | 36 ++--- .../stacks/stacks-send-form-confirmation.tsx | 2 +- .../stacks/use-stacks-common-send-form.tsx | 4 +- .../form/stx/stx-send-form.tsx | 11 +- .../form/stx/use-stx-send-form.tsx | 4 +- .../send-crypto-asset-form.routes.tsx | 10 +- .../components/swap-asset-item.tsx | 8 +- .../transaction-request.tsx | 4 +- .../btc-balance-native-segwit.hooks.ts | 4 +- src/app/query/bitcoin/bitcoin-client.ts | 8 -- .../bitcoin/btc/btc-crypto-asset.hooks.ts | 48 +++++++ .../ordinals/brc20/brc20-tokens.hooks.ts | 55 ++++---- .../ordinals/brc20/brc20-tokens.query.ts | 10 +- .../query/common/alex-sdk/alex-sdk.hooks.ts | 35 +++-- src/app/query/models/crypto-asset.model.ts | 59 ++++++++ ...ance.hooks.ts => account-balance.hooks.ts} | 28 ++-- ...ance.query.ts => account-balance.query.ts} | 0 .../balance/stacks-ft-balances.hooks.ts | 131 ------------------ .../balance/stacks-ft-balances.utils.ts | 102 -------------- src/app/query/stacks/bns/bns.hooks.ts | 4 +- src/app/query/stacks/mempool/mempool.hooks.ts | 62 +++------ src/app/query/stacks/mempool/mempool.utils.ts | 62 +++++++++ .../stacks/nonce/account-nonces.query.ts | 4 +- .../query/stacks/sip10/sip10-tokens.hooks.ts | 124 +++++++++++++++++ .../query/stacks/sip10/sip10-tokens.spec.ts | 32 +++++ .../query/stacks/sip10/sip10-tokens.utils.ts | 22 +++ .../stacks/stx/stx-crypto-asset.hooks.ts | 58 ++++++++ .../query/stacks/stx20/stx20-tokens.hooks.ts | 4 +- .../fungible-token-metadata.hooks.ts | 12 ++ .../fungible-token-metadata.query.ts | 0 .../non-fungible-token-holdings.query.ts | 0 .../non-fungible-token-metadata.hooks.ts | 0 .../non-fungible-token-metadata.query.ts | 0 .../token-metadata.utils.ts | 0 .../transactions-with-transfers.query.ts | 4 +- .../blockchain/bitcoin/bitcoin.ledger.ts | 2 + .../blockchain/stacks/stacks-account.hooks.ts | 5 +- src/app/store/accounts/blockchain/utils.ts | 10 +- .../transactions/post-conditions.hooks.ts | 8 +- .../transactions/token-transfer.hooks.ts | 80 ++++------- .../store/transactions/transaction.hooks.ts | 4 +- src/shared/models/account.model.ts | 16 --- .../models/crypto-asset-balance.model.ts | 46 ------ src/shared/models/crypto-asset.model.ts | 37 ----- tests/selectors/home.selectors.ts | 2 +- .../rpc-get-addresses/get-addresses.spec.ts | 1 - 127 files changed, 1323 insertions(+), 1604 deletions(-) delete mode 100644 src/app/common/crypto-assets/stacks-crypto-asset.utils.spec.ts delete mode 100644 src/app/common/crypto-assets/stacks-crypto-asset.utils.ts delete mode 100644 src/app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance.ts delete mode 100644 src/app/common/hooks/use-transferable-asset-balances.hooks.ts delete mode 100644 src/app/components/account/bitcoin-account-loader.tsx rename src/app/components/{crypto-assets => }/crypto-asset-item/crypto-asset-item.layout.tsx (70%) create mode 100644 src/app/components/crypto-asset-item/crypto-asset-item.layout.utils.ts delete mode 100644 src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-item.layout.tsx delete mode 100644 src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx delete mode 100644 src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils.ts delete mode 100644 src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts delete mode 100644 src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx delete mode 100644 src/app/components/loaders/btc-balance-loader.tsx create mode 100644 src/app/components/loaders/btc-crypto-asset-loader.tsx create mode 100644 src/app/components/loaders/current-bitcoin-signer-loader.tsx create mode 100644 src/app/components/loaders/stx-crypto-asset-loader.tsx rename src/app/components/{crypto-assets/stacks/components => }/stacks-asset-avatar.tsx (100%) create mode 100644 src/app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx create mode 100644 src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback.tsx rename src/app/features/asset-list/{components/btc-balance-list-item.tsx => bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx} (54%) rename src/app/{components/crypto-assets => features/asset-list}/bitcoin/runes-asset-list/runes-asset-item.layout.tsx (100%) rename src/app/{components/crypto-assets => features/asset-list}/bitcoin/runes-asset-list/runes-asset-list.tsx (100%) rename src/app/{components/crypto-assets => features/asset-list}/bitcoin/src20-token-asset-list/src20-token-asset-item.layout.tsx (100%) rename src/app/{components/crypto-assets => features/asset-list}/bitcoin/src20-token-asset-list/src20-token-asset-list.tsx (100%) delete mode 100644 src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx delete mode 100644 src/app/features/asset-list/components/stacks-fungible-token-asset-list.layout.tsx delete mode 100644 src/app/features/asset-list/components/stacks-fungible-token-asset-list.tsx delete mode 100644 src/app/features/asset-list/components/stx-balance-list-item.layout.stories.tsx delete mode 100644 src/app/features/asset-list/components/stx-balance-list-item.layout.tsx create mode 100644 src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx create mode 100644 src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.utils.ts rename src/app/features/asset-list/{components/stacks-unsupported-token-asset-list.tsx => stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx} (56%) create mode 100644 src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx create mode 100644 src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback.tsx create mode 100644 src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.stories.tsx rename src/app/features/asset-list/{components/stx-balance-list-item.tsx => stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx} (63%) rename src/app/{components/crypto-assets => features/asset-list}/stacks/stx20-token-asset-list/stx20-token-asset-item.layout.tsx (100%) rename src/app/{components/crypto-assets => features/asset-list}/stacks/stx20-token-asset-list/stx20-token-asset-list.tsx (100%) delete mode 100644 src/app/features/selectable-asset-list/crypto-asset-list-item.tsx delete mode 100644 src/app/features/selectable-asset-list/crypto-asset-list.tsx delete mode 100644 src/app/features/selectable-asset-list/crypto-currency-asset-icon.tsx delete mode 100644 src/app/features/selectable-asset-list/fungible-token-asset-item.tsx create mode 100644 src/app/pages/home/components/assets.tsx rename src/app/{features/selectable-asset-list => pages/send/choose-crypto-asset}/send-btc-disabled.tsx (100%) rename src/app/pages/send/send-crypto-asset-form/form/{brc-20/brc-20-choose-fee.tsx => brc20/brc20-choose-fee.tsx} (100%) rename src/app/pages/send/send-crypto-asset-form/form/{brc-20 => brc20}/brc20-send-form-confirmation.tsx (100%) rename src/app/pages/send/send-crypto-asset-form/form/{brc-20 => brc20}/brc20-send-form.tsx (100%) rename src/app/pages/send/send-crypto-asset-form/form/{brc-20 => brc20}/use-brc20-send-form.tsx (100%) rename src/app/pages/send/send-crypto-asset-form/form/{stacks-sip10 => sip10}/sip10-token-send-form-container.tsx (83%) rename src/app/pages/send/send-crypto-asset-form/form/{stacks-sip10 => sip10}/sip10-token-send-form.tsx (52%) rename src/app/pages/send/send-crypto-asset-form/form/{stacks-sip10 => sip10}/use-sip10-send-form.tsx (64%) create mode 100644 src/app/query/bitcoin/btc/btc-crypto-asset.hooks.ts create mode 100644 src/app/query/models/crypto-asset.model.ts rename src/app/query/stacks/balance/{stx-balance.hooks.ts => account-balance.hooks.ts} (67%) rename src/app/query/stacks/balance/{stx-balance.query.ts => account-balance.query.ts} (100%) delete mode 100644 src/app/query/stacks/balance/stacks-ft-balances.hooks.ts delete mode 100644 src/app/query/stacks/balance/stacks-ft-balances.utils.ts create mode 100644 src/app/query/stacks/mempool/mempool.utils.ts create mode 100644 src/app/query/stacks/sip10/sip10-tokens.hooks.ts create mode 100644 src/app/query/stacks/sip10/sip10-tokens.spec.ts create mode 100644 src/app/query/stacks/sip10/sip10-tokens.utils.ts create mode 100644 src/app/query/stacks/stx/stx-crypto-asset.hooks.ts create mode 100644 src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.hooks.ts rename src/app/query/stacks/{tokens => token-metadata}/fungible-tokens/fungible-token-metadata.query.ts (100%) rename src/app/query/stacks/{tokens => token-metadata}/non-fungible-tokens/non-fungible-token-holdings.query.ts (100%) rename src/app/query/stacks/{tokens => token-metadata}/non-fungible-tokens/non-fungible-token-metadata.hooks.ts (100%) rename src/app/query/stacks/{tokens => token-metadata}/non-fungible-tokens/non-fungible-token-metadata.query.ts (100%) rename src/app/query/stacks/{tokens => token-metadata}/token-metadata.utils.ts (100%) delete mode 100644 src/shared/models/crypto-asset-balance.model.ts delete mode 100644 src/shared/models/crypto-asset.model.ts diff --git a/package.json b/package.json index 005499c7139..54e47a4f896 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "@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.4.0", + "@leather-wallet/models": "0.4.4", "@leather-wallet/tokens": "0.0.14", "@ledgerhq/hw-transport-webusb": "6.27.19", "@noble/hashes": "1.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3cb84a9116d..0935a38655b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,8 +30,8 @@ dependencies: specifier: 1.2.0 version: 1.2.0 '@leather-wallet/models': - specifier: 0.4.0 - version: 0.4.0 + specifier: 0.4.4 + version: 0.4.4 '@leather-wallet/tokens': specifier: 0.0.14 version: 0.0.14 @@ -3700,6 +3700,13 @@ packages: resolution: {integrity: sha512-6uwhdJxEhIpCI/29TPQIXAzfcz4niVIIujkGuwRFSyUiT/E8IEvjM30A3p9pMtEPCzO2UnpVERGLiLSIk48fgg==} dependencies: bignumber.js: 9.1.2 + dev: true + + /@leather-wallet/models@0.4.4: + resolution: {integrity: sha512-dqUTUR0EMM9WFwkeWRahlpixK+Ct2vEpDBhQdvO83tK4GhC/JlopI9hmpaSKyX9+35Ts2LpgycUiCh59kmZbMA==} + dependencies: + bignumber.js: 9.1.2 + dev: false /@leather-wallet/prettier-config@0.0.5: resolution: {integrity: sha512-cK44e4XqYghcbd5ow8AKzK0NyNQ0r0V/PGVm940PN17tDxZ7JykBcEmSI1iGRkCnTCy/RE/qHkgGBW5gYXhM7g==} diff --git a/src/app/common/crypto-assets/stacks-crypto-asset.utils.spec.ts b/src/app/common/crypto-assets/stacks-crypto-asset.utils.spec.ts deleted file mode 100644 index d8812ce8e34..00000000000 --- a/src/app/common/crypto-assets/stacks-crypto-asset.utils.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { StacksFungibleTokenAsset } from '@shared/models/crypto-asset.model'; - -import { - isFtNameLikeStx, - isTransferableStacksFungibleTokenAsset, -} from './stacks-crypto-asset.utils'; - -describe(isFtNameLikeStx.name, () => { - it('detect impersonating token names', () => { - expect(isFtNameLikeStx('STX')).toBeTruthy(); - expect(isFtNameLikeStx('stx')).toBeTruthy(); - expect(isFtNameLikeStx('stacks')).toBeTruthy(); - expect(isFtNameLikeStx('Stäcks')).toBeTruthy(); - expect(isFtNameLikeStx('Stácks')).toBeTruthy(); - expect(isFtNameLikeStx('Stáçks')).toBeTruthy(); - expect(isFtNameLikeStx('stocks')).toBeFalsy(); - expect(isFtNameLikeStx('miamicoin')).toBeFalsy(); - expect(isFtNameLikeStx('')).toBeFalsy(); - }); -}); - -describe(isTransferableStacksFungibleTokenAsset.name, () => { - test('assets with a name, symbol and decimals are allowed to be transferred', () => { - const asset: StacksFungibleTokenAsset = { - contractId: '', - contractAddress: 'ST6G7N19FKNW24XH5JQ5P5WR1DN10QWMKQSPSTK7', - contractAssetName: 'stella-token', - contractName: 'stella-the-cat', - decimals: 9, - name: 'SteLLa the Cat', - canTransfer: true, - hasMemo: true, - imageCanonicalUri: '', - marketData: null, - symbol: 'CAT', - }; - expect(isTransferableStacksFungibleTokenAsset(asset)).toBeTruthy(); - }); - - test('a token with no decimals is transferable', () => { - const asset: StacksFungibleTokenAsset = { - contractId: '', - contractAddress: 'ST6G7N19FKNW24XH5JQ5P5WR1DN10QWMKQSPSTK7', - contractAssetName: 'stella-token', - contractName: 'stella-the-cat', - decimals: 0, - name: 'SteLLa the Cat', - canTransfer: true, - hasMemo: true, - imageCanonicalUri: '', - marketData: null, - symbol: 'CAT', - }; - expect(isTransferableStacksFungibleTokenAsset(asset)).toBeTruthy(); - }); - - test('assets missing either name, symbol or decimals may not be transferred', () => { - const asset = { - name: 'Test token', - symbol: 'TEST', - decimals: undefined, - type: 'fungible-token', - } as unknown as StacksFungibleTokenAsset; - expect(isTransferableStacksFungibleTokenAsset(asset)).toBeFalsy(); - }); - - test('NFTs cannot be sent', () => { - const asset = { type: 'non-fungible-token' } as unknown as StacksFungibleTokenAsset; - - expect(isTransferableStacksFungibleTokenAsset(asset)).toBeFalsy(); - }); -}); diff --git a/src/app/common/crypto-assets/stacks-crypto-asset.utils.ts b/src/app/common/crypto-assets/stacks-crypto-asset.utils.ts deleted file mode 100644 index 88ca3269097..00000000000 --- a/src/app/common/crypto-assets/stacks-crypto-asset.utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { StacksFungibleTokenAsset } from '@shared/models/crypto-asset.model'; -import { isUndefined } from '@shared/utils'; -import { isValidUrl } from '@shared/utils/validate-url'; - -import { convertUnicodeToAscii } from '../string-utils'; - -export function isFtNameLikeStx(name: string) { - return ['stx', 'stack', 'stacks'].includes(convertUnicodeToAscii(name).toLocaleLowerCase()); -} - -export function getImageCanonicalUri(imageCanonicalUri: string, name: string) { - return imageCanonicalUri && isValidUrl(imageCanonicalUri) && !isFtNameLikeStx(name) - ? imageCanonicalUri - : ''; -} - -export function isTransferableStacksFungibleTokenAsset(asset: StacksFungibleTokenAsset) { - return !isUndefined(asset.decimals) && !isUndefined(asset.name) && !isUndefined(asset.symbol); -} diff --git a/src/app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance.ts b/src/app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance.ts deleted file mode 100644 index cb9e60203d4..00000000000 --- a/src/app/common/hooks/balance/stx/use-stx-crypto-currency-asset-balance.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; - -// TODO: Asset refactor: remove wrapper here -export function useStxCryptoCurrencyAssetBalance() { - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); - return createStacksCryptoCurrencyAssetTypeWrapper(availableUnlockedBalance.amount); -} diff --git a/src/app/common/hooks/balance/use-total-balance.tsx b/src/app/common/hooks/balance/use-total-balance.tsx index 043bff0b394..2126e3046a0 100644 --- a/src/app/common/hooks/balance/use-total-balance.tsx +++ b/src/app/common/hooks/balance/use-total-balance.tsx @@ -6,7 +6,7 @@ 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/stx-balance.hooks'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; interface UseTotalBalanceArgs { btcAddress: string; diff --git a/src/app/common/hooks/use-transferable-asset-balances.hooks.ts b/src/app/common/hooks/use-transferable-asset-balances.hooks.ts deleted file mode 100644 index 8f43c43ded2..00000000000 --- a/src/app/common/hooks/use-transferable-asset-balances.hooks.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useMemo } from 'react'; - -import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model'; - -import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; -import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; -import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; - -// TODO: Asset refactor: remove wrapper here -export function useAllTransferableCryptoAssetBalances(): AllTransferableCryptoAssetBalances[] { - const account = useCurrentStacksAccount(); - - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); - const stxCryptoCurrencyAssetBalance = createStacksCryptoCurrencyAssetTypeWrapper( - availableUnlockedBalance.amount - ); - const stacksFtAssetBalances = useTransferableStacksFungibleTokenAssetBalances( - account?.address ?? '' - ); - - return useMemo(() => { - return [stxCryptoCurrencyAssetBalance, ...stacksFtAssetBalances]; - }, [stacksFtAssetBalances, stxCryptoCurrencyAssetBalance]); -} diff --git a/src/app/common/stacks-utils.spec.ts b/src/app/common/stacks-utils.spec.ts index e9def73faef..17767b47f13 100644 --- a/src/app/common/stacks-utils.spec.ts +++ b/src/app/common/stacks-utils.spec.ts @@ -1,4 +1,4 @@ -import { stacksValue } from '@app/common/stacks-utils'; +import { isFtNameLikeStx, stacksValue } from '@app/common/stacks-utils'; const uSTX_AMOUNT = 10000480064; // 10,000.480064 @@ -31,3 +31,17 @@ describe('stacksValue tests', () => { expect(value).toEqual('10K STX'); }); }); + +describe(isFtNameLikeStx.name, () => { + it('detect impersonating token names', () => { + expect(isFtNameLikeStx('STX')).toBeTruthy(); + expect(isFtNameLikeStx('stx')).toBeTruthy(); + expect(isFtNameLikeStx('stacks')).toBeTruthy(); + expect(isFtNameLikeStx('Stäcks')).toBeTruthy(); + expect(isFtNameLikeStx('Stácks')).toBeTruthy(); + expect(isFtNameLikeStx('Stáçks')).toBeTruthy(); + expect(isFtNameLikeStx('stocks')).toBeFalsy(); + expect(isFtNameLikeStx('miamicoin')).toBeFalsy(); + expect(isFtNameLikeStx('')).toBeFalsy(); + }); +}); diff --git a/src/app/common/stacks-utils.ts b/src/app/common/stacks-utils.ts index 141d49d35e0..9d4b9ab2004 100644 --- a/src/app/common/stacks-utils.ts +++ b/src/app/common/stacks-utils.ts @@ -3,11 +3,13 @@ import BigNumber from 'bignumber.js'; import { c32addressDecode } from 'c32check'; import { NetworkConfiguration, STX_DECIMALS } from '@shared/constants'; +import { isValidUrl } from '@shared/utils/validate-url'; import { abbreviateNumber } from '@app/common/utils'; import { initBigNumber } from './math/helpers'; import { microStxToStx } from './money/unit-conversion'; +import { convertUnicodeToAscii } from './string-utils'; export const stacksValue = ({ value, @@ -64,3 +66,13 @@ export function validateAddressChain(address: string, currentNetwork: NetworkCon return false; } } + +export function isFtNameLikeStx(name: string) { + return ['stx', 'stack', 'stacks'].includes(convertUnicodeToAscii(name).toLocaleLowerCase()); +} + +export function getSafeImageCanonicalUri(imageCanonicalUri: string, name: string) { + return imageCanonicalUri && isValidUrl(imageCanonicalUri) && !isFtNameLikeStx(name) + ? imageCanonicalUri + : ''; +} diff --git a/src/app/components/account/account-addresses.tsx b/src/app/components/account/account-addresses.tsx index f8feff13b97..28eb7164ab6 100644 --- a/src/app/components/account/account-addresses.tsx +++ b/src/app/components/account/account-addresses.tsx @@ -4,8 +4,8 @@ import { BulletSeparator } from '@app/ui/components/bullet-separator/bullet-sepa import { Caption } from '@app/ui/components/typography/caption'; import { truncateMiddle } from '@app/ui/utils/truncate-middle'; +import { BitcoinNativeSegwitAccountLoader } from '../loaders/bitcoin-account-loader'; import { StacksAccountLoader } from '../loaders/stacks-account-loader'; -import { BitcoinNativeSegwitAccountLoader } from './bitcoin-account-loader'; interface AccountAddressesProps { index: number; diff --git a/src/app/components/account/bitcoin-account-loader.tsx b/src/app/components/account/bitcoin-account-loader.tsx deleted file mode 100644 index 18774dc3680..00000000000 --- a/src/app/components/account/bitcoin-account-loader.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { P2Ret } from '@scure/btc-signer'; - -import { useConfigBitcoinEnabled } from '@app/query/common/remote-config/remote-config.query'; -import { useCurrentAccountIndex } from '@app/store/accounts/account'; -import { Signer } from '@app/store/accounts/blockchain/bitcoin/bitcoin-signer'; -import { useNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { useTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; -import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; - -interface BitcoinAccountLoaderBaseProps { - children(account: Signer): React.ReactNode; -} -interface BtcAccountLoaderCurrentProps extends BitcoinAccountLoaderBaseProps { - current: true; -} -interface BtcAccountLoaderIndexProps extends BitcoinAccountLoaderBaseProps { - index: number; -} - -type BtcAccountLoaderProps = BtcAccountLoaderCurrentProps | BtcAccountLoaderIndexProps; - -export function BitcoinNativeSegwitAccountLoader({ children, ...props }: BtcAccountLoaderProps) { - const isBitcoinEnabled = useConfigBitcoinEnabled(); - - const currentAccountIndex = useCurrentAccountIndex(); - - const properIndex = 'current' in props ? currentAccountIndex : props.index; - - const signer = useNativeSegwitSigner(properIndex); - - if (!signer || !isBitcoinEnabled) return null; - return children(signer(0)); -} - -export function BitcoinTaprootAccountLoader({ children, ...props }: BtcAccountLoaderProps) { - const isBitcoinEnabled = useConfigBitcoinEnabled(); - const network = useCurrentNetwork(); - - const currentAccountIndex = useCurrentAccountIndex(); - - const properIndex = 'current' in props ? currentAccountIndex : props.index; - - const signer = useTaprootSigner(properIndex, network.chain.bitcoin.bitcoinNetwork); - - if (!signer || !isBitcoinEnabled) return null; - return children(signer(0)); -} diff --git a/src/app/components/balance/btc-balance.tsx b/src/app/components/balance/btc-balance.tsx index 43feb74cb75..a35c5af4ec3 100644 --- a/src/app/components/balance/btc-balance.tsx +++ b/src/app/components/balance/btc-balance.tsx @@ -1,16 +1,16 @@ import { formatMoney } from '@app/common/money/format-money'; import { Caption } from '@app/ui/components/typography/caption'; -import { BitcoinNativeSegwitAccountLoader } from '../account/bitcoin-account-loader'; -import { BtcBalanceLoader } from '../loaders/btc-balance-loader'; +import { BitcoinNativeSegwitAccountLoader } from '../loaders/bitcoin-account-loader'; +import { BtcCryptoAssetLoader } from '../loaders/btc-crypto-asset-loader'; export function BtcBalance() { return ( {signer => ( - - {balance => {formatMoney(balance.availableBalance)}} - + + {asset => {formatMoney(asset.balance.availableBalance)}} + )} ); diff --git a/src/app/components/balance/stx-balance.tsx b/src/app/components/balance/stx-balance.tsx index afde5293dc6..9ea9e4e826d 100644 --- a/src/app/components/balance/stx-balance.tsx +++ b/src/app/components/balance/stx-balance.tsx @@ -1,7 +1,7 @@ import { useMemo } from 'react'; import { stacksValue } from '@app/common/stacks-utils'; -import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; import { Caption } from '@app/ui/components/typography/caption'; interface StxBalanceProps { diff --git a/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx b/src/app/components/crypto-asset-item/crypto-asset-item.layout.tsx similarity index 70% rename from src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx rename to src/app/components/crypto-asset-item/crypto-asset-item.layout.tsx index 53a2ab24b50..efe8e0bd245 100644 --- a/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout.tsx +++ b/src/app/components/crypto-asset-item/crypto-asset-item.layout.tsx @@ -1,10 +1,9 @@ import { ReactNode } from 'react'; -import type { CryptoAssetBalances } from '@leather-wallet/models'; import { Box, Flex, styled } from 'leather-styles/jsx'; -import { capitalize } from '@app/common/utils'; import { spamFilter } from '@app/common/utils/spam-filter'; +import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { BulletSeparator } from '@app/ui/components/bullet-separator/bullet-separator'; import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; import { SkeletonLoader } from '@app/ui/components/skeleton-loader/skeleton-loader'; @@ -12,34 +11,35 @@ import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; import { Caption } from '@app/ui/components/typography/caption'; import { Pressable } from '@app/ui/pressable/pressable'; -import { parseCryptoAssetBalance } from './crypto-asset-item.utils'; +import { parseCryptoAssetBalance } from './crypto-asset-item.layout.utils'; interface CryptoAssetItemLayoutProps { additionalBalanceInfo?: ReactNode; additionalBalanceInfoAsFiat?: ReactNode; - address?: string; - assetBalance: CryptoAssetBalances; - balanceAsFiat?: string; + asset: AccountCryptoAssetWithDetails; + caption?: string; + fiatBalance?: string; icon: React.ReactNode; isLoading?: boolean; name: string; - onClick?(): void; + onClick?(asset: AccountCryptoAssetWithDetails): void; rightElement?: React.ReactNode; } export function CryptoAssetItemLayout({ additionalBalanceInfo, additionalBalanceInfoAsFiat, - address = '', - assetBalance, - balanceAsFiat, + asset, + caption, + fiatBalance, icon, isLoading = false, name, onClick, rightElement, }: CryptoAssetItemLayoutProps) { - const { balance, dataTestId, formattedBalance } = parseCryptoAssetBalance(assetBalance); - const title = spamFilter(capitalize(name)); + const { dataTestId, formattedBalance } = parseCryptoAssetBalance(asset.balance); + const { availableBalance } = asset.balance; + const title = spamFilter(name); const titleRight = ( @@ -48,9 +48,7 @@ export function CryptoAssetItemLayout({ ) : ( @@ -67,9 +65,7 @@ export function CryptoAssetItemLayout({ - - {balance.availableBalance.amount.toNumber() > 0 && address ? balanceAsFiat : null} - + {availableBalance.amount.toNumber() > 0 ? fiatBalance : null} {additionalBalanceInfoAsFiat} @@ -84,7 +80,7 @@ export function CryptoAssetItemLayout({ @@ -92,7 +88,7 @@ export function CryptoAssetItemLayout({ if (isInteractive) return ( - + onClick(asset)} my="space.02"> {content} ); diff --git a/src/app/components/crypto-asset-item/crypto-asset-item.layout.utils.ts b/src/app/components/crypto-asset-item/crypto-asset-item.layout.utils.ts new file mode 100644 index 00000000000..6cc1c622b67 --- /dev/null +++ b/src/app/components/crypto-asset-item/crypto-asset-item.layout.utils.ts @@ -0,0 +1,21 @@ +import type { CryptoAssetBalance } from '@leather-wallet/models'; +import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; + +import { formatBalance } from '@app/common/format-balance'; +import { ftDecimals } from '@app/common/stacks-utils'; + +export function parseCryptoAssetBalance(balance: CryptoAssetBalance) { + const { availableBalance } = balance; + + const amount = ftDecimals(availableBalance.amount, availableBalance.decimals); + const dataTestId = CryptoAssetSelectors.CryptoAssetListItem.replace( + '{symbol}', + availableBalance.symbol.toLowerCase() + ); + const formattedBalance = formatBalance(amount); + + return { + dataTestId, + formattedBalance, + }; +} diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-item.layout.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-item.layout.tsx deleted file mode 100644 index e83635a03c3..00000000000 --- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-item.layout.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import BigNumber from 'bignumber.js'; -import { styled } from 'leather-styles/jsx'; - -import { convertAssetBalanceToFiat } from '@app/common/asset-utils'; -import { formatBalance } from '@app/common/format-balance'; -import { convertAmountToBaseUnit } from '@app/common/money/calculate-money'; -import { Brc20Token } from '@app/query/bitcoin/bitcoin-client'; -import { Brc20AvatarIcon } from '@app/ui/components/avatar/brc20-avatar-icon'; -import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; -import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; -import { Pressable } from '@app/ui/pressable/pressable'; - -interface Brc20TokenAssetItemLayoutProps { - token: Brc20Token; - onClick?(): void; -} -export function Brc20TokenAssetItemLayout({ onClick, token }: Brc20TokenAssetItemLayoutProps) { - const balanceAsString = convertAmountToBaseUnit(token.balance ?? new BigNumber(0)).toString(); - const formattedBalance = formatBalance(balanceAsString); - const balanceAsFiat = convertAssetBalanceToFiat(token); - - return ( - - } - titleLeft={token.tokenData.ticker} - captionLeft="BRC-20" - titleRight={ - - - {formattedBalance.value} - - - } - captionRight={balanceAsFiat} - /> - - ); -} diff --git a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx b/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx deleted file mode 100644 index ff4c6aa7fb1..00000000000 --- a/src/app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useNavigate } from 'react-router-dom'; - -import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; -import { Stack } from 'leather-styles/jsx'; - -import { RouteUrls } from '@shared/route-urls'; - -import { useCurrentBtcAvailableBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; -import { Brc20Token } from '@app/query/bitcoin/bitcoin-client'; - -import { Brc20TokenAssetItemLayout } from './brc20-token-asset-item.layout'; - -interface Brc20TokenAssetListProps { - tokens: Brc20Token[]; - variant?: string; -} -export function Brc20TokenAssetList({ tokens, variant }: Brc20TokenAssetListProps) { - const navigate = useNavigate(); - const { balance } = useCurrentBtcAvailableBalanceNativeSegwit(); - - const hasPositiveBtcBalanceForFees = variant === 'send' && balance.amount.isGreaterThan(0); - - function navigateToBrc20SendForm(token: Brc20Token) { - const { balance, holderAddress, marketData, tokenData } = token; - navigate(RouteUrls.SendBrc20SendForm.replace(':ticker', tokenData.ticker), { - state: { balance, ticker: tokenData.ticker, holderAddress, marketData }, - }); - } - - return ( - - {tokens.map(token => ( - navigateToBrc20SendForm(token) : undefined} - /> - ))} - - ); -} diff --git a/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils.ts b/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils.ts deleted file mode 100644 index 955a07cb671..00000000000 --- a/src/app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { - BtcCryptoAssetInfo, - CryptoAssetBalances, - StxCryptoAssetInfo, -} from '@leather-wallet/models'; -import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; - -import { BTC_DECIMALS, STX_DECIMALS } from '@shared/constants'; - -import { formatBalance } from '@app/common/format-balance'; -import { ftDecimals } from '@app/common/stacks-utils'; - -export const btcCryptoAssetInfo: BtcCryptoAssetInfo = { - decimals: BTC_DECIMALS, - hasMemo: true, - name: 'bitcoin', - symbol: 'BTC', -}; - -export const stxCryptoAssetInfo: StxCryptoAssetInfo = { - decimals: STX_DECIMALS, - hasMemo: true, - name: 'stacks', - symbol: 'STX', -}; - -export function parseCryptoAssetBalance(balance: CryptoAssetBalances) { - const { availableBalance } = balance; - - const amount = availableBalance.decimals - ? ftDecimals(availableBalance.amount, availableBalance.decimals) - : availableBalance.amount.toString(); - const dataTestId = CryptoAssetSelectors.CryptoAssetListItem.replace( - '{symbol}', - availableBalance.symbol.toLowerCase() - ); - const formattedBalance = formatBalance(amount); - - return { - balance, - dataTestId, - formattedBalance, - }; -} diff --git a/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts b/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts deleted file mode 100644 index 78414d38f51..00000000000 --- a/src/app/components/crypto-assets/stacks/fungible-token-asset/fungible-token-asset.utils.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; - -import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; - -import { convertAssetBalanceToFiat } from '@app/common/asset-utils'; -import { getImageCanonicalUri } from '@app/common/crypto-assets/stacks-crypto-asset.utils'; -import { formatBalance } from '@app/common/format-balance'; -import { ftDecimals } from '@app/common/stacks-utils'; -import { formatContractId, getTicker } from '@app/common/utils'; -import { spamFilter } from '@app/common/utils/spam-filter'; -import { getAssetName } from '@app/ui/utils/get-asset-name'; - -export function parseStacksFungibleTokenAssetBalance( - assetBalance: StacksFungibleTokenAssetBalance -) { - const { asset, balance } = assetBalance; - const { contractAddress, contractAssetName, contractName, name, symbol } = asset; - - const amount = balance.decimals - ? ftDecimals(balance.amount, balance.decimals || 0) - : balance.amount.toString(); - const avatar = `${formatContractId(contractAddress, contractName)}::${contractAssetName}`; - const dataTestId = - symbol && CryptoAssetSelectors.CryptoAssetListItem.replace('{symbol}', symbol.toLowerCase()); - const formattedBalance = formatBalance(amount); - const friendlyName = - name || - (contractAssetName.includes('::') ? getAssetName(contractAssetName) : contractAssetName); - const imageCanonicalUri = getImageCanonicalUri(asset.imageCanonicalUri, asset.name); - const caption = symbol || getTicker(friendlyName); - const title = spamFilter(friendlyName); - const balanceAsFiat = convertAssetBalanceToFiat({ - ...assetBalance.asset, - balance: assetBalance.balance, - }); - - return { - amount, - avatar, - balanceAsFiat, - caption, - dataTestId, - formattedBalance, - imageCanonicalUri, - title, - }; -} diff --git a/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx b/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx deleted file mode 100644 index cf66b4cced8..00000000000 --- a/src/app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { styled } from 'leather-styles/jsx'; - -import { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; - -import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar'; -import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; -import { BasicTooltip } from '@app/ui/components/tooltip/basic-tooltip'; -import { Pressable } from '@app/ui/pressable/pressable'; - -import { parseStacksFungibleTokenAssetBalance } from './fungible-token-asset.utils'; - -interface StacksFungibleTokenAssetItemLayoutProps { - assetBalance: StacksFungibleTokenAssetBalance; - onClick?(): void; -} -export function StacksFungibleTokenAssetItemLayout({ - assetBalance, - onClick, -}: StacksFungibleTokenAssetItemLayoutProps) { - const { - amount, - avatar, - balanceAsFiat, - caption, - dataTestId, - formattedBalance, - imageCanonicalUri, - title, - } = parseStacksFungibleTokenAssetBalance(assetBalance); - - return ( - - - {title[0]} - - } - titleLeft={title} - captionLeft={caption} - titleRight={ - - - {formattedBalance.value} - - - } - captionRight={balanceAsFiat} - /> - - ); -} diff --git a/src/app/components/loaders/bitcoin-account-loader.tsx b/src/app/components/loaders/bitcoin-account-loader.tsx index d97252f68a2..42fdf1e9de0 100644 --- a/src/app/components/loaders/bitcoin-account-loader.tsx +++ b/src/app/components/loaders/bitcoin-account-loader.tsx @@ -1,17 +1,52 @@ -import type { P2Ret, P2TROut } from '@scure/btc-signer'; +import { P2Ret } from '@scure/btc-signer'; -import { ZERO_INDEX } from '@shared/constants'; +import { useConfigBitcoinEnabled } from '@app/query/common/remote-config/remote-config.query'; +import { useCurrentAccountIndex } from '@app/store/accounts/account'; +import { Signer } from '@app/store/accounts/blockchain/bitcoin/bitcoin-signer'; +import { useNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; +import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; -import type { Signer } from '@app/store/accounts/blockchain/bitcoin/bitcoin-signer'; -import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; +interface BitcoinAccountLoaderBaseProps { + children(account: Signer): React.ReactNode; + fallback?: React.ReactNode; +} +interface BtcAccountLoaderCurrentProps extends BitcoinAccountLoaderBaseProps { + current: true; +} +interface BtcAccountLoaderIndexProps extends BitcoinAccountLoaderBaseProps { + index: number; +} + +type BtcAccountLoaderProps = BtcAccountLoaderCurrentProps | BtcAccountLoaderIndexProps; + +export function BitcoinNativeSegwitAccountLoader({ + children, + fallback, + ...props +}: BtcAccountLoaderProps) { + const isBitcoinEnabled = useConfigBitcoinEnabled(); + + const currentAccountIndex = useCurrentAccountIndex(); + + const properIndex = 'current' in props ? currentAccountIndex : props.index; -interface CurrentBitcoinSignerLoaderProps { - children(data: { nativeSegwit: Signer; taproot: Signer }): React.ReactNode; + const signer = useNativeSegwitSigner(properIndex); + + if (!signer || !isBitcoinEnabled) return fallback; + return children(signer(0)); } -export function CurrentBitcoinSignerLoader({ children }: CurrentBitcoinSignerLoaderProps) { - const nativeSegwit = useCurrentAccountNativeSegwitSigner()?.(ZERO_INDEX); - const taproot = useCurrentAccountTaprootSigner()?.(ZERO_INDEX); - if (!taproot || !nativeSegwit) return null; - return children({ nativeSegwit, taproot }); + +export function BitcoinTaprootAccountLoader({ children, ...props }: BtcAccountLoaderProps) { + const isBitcoinEnabled = useConfigBitcoinEnabled(); + const network = useCurrentNetwork(); + + const currentAccountIndex = useCurrentAccountIndex(); + + const properIndex = 'current' in props ? currentAccountIndex : props.index; + + const signer = useTaprootSigner(properIndex, network.chain.bitcoin.bitcoinNetwork); + + if (!signer || !isBitcoinEnabled) return null; + return children(signer(0)); } diff --git a/src/app/components/loaders/brc20-tokens-loader.tsx b/src/app/components/loaders/brc20-tokens-loader.tsx index cbc9af4a770..b7f152d2e61 100644 --- a/src/app/components/loaders/brc20-tokens-loader.tsx +++ b/src/app/components/loaders/brc20-tokens-loader.tsx @@ -1,10 +1,10 @@ -import { Brc20Token } from '@app/query/bitcoin/bitcoin-client'; -import { useBrc20Tokens } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks'; +import { useBrc20AccountCryptoAssetsWithDetails } from '@app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks'; +import type { Brc20AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; interface Brc20TokensLoaderProps { - children(tokens: Brc20Token[]): React.ReactNode; + children(tokens: Brc20AccountCryptoAssetWithDetails[]): React.ReactNode; } export function Brc20TokensLoader({ children }: Brc20TokensLoaderProps) { - const tokens = useBrc20Tokens(); + const tokens = useBrc20AccountCryptoAssetsWithDetails(); return children(tokens); } diff --git a/src/app/components/loaders/btc-balance-loader.tsx b/src/app/components/loaders/btc-balance-loader.tsx deleted file mode 100644 index 09969971af1..00000000000 --- a/src/app/components/loaders/btc-balance-loader.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { BtcCryptoAssetBalance } from '@leather-wallet/models'; - -import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; - -interface BtcBalanceLoaderProps { - address: string; - children(balance: BtcCryptoAssetBalance, isInitialLoading: boolean): React.ReactNode; -} -export function BtcBalanceLoader({ address, children }: BtcBalanceLoaderProps) { - const { btcCryptoAssetBalance, isInitialLoading } = useBtcCryptoAssetBalanceNativeSegwit(address); - return children(btcCryptoAssetBalance, isInitialLoading); -} diff --git a/src/app/components/loaders/btc-crypto-asset-loader.tsx b/src/app/components/loaders/btc-crypto-asset-loader.tsx new file mode 100644 index 00000000000..3ed06ddb8bf --- /dev/null +++ b/src/app/components/loaders/btc-crypto-asset-loader.tsx @@ -0,0 +1,11 @@ +import { useBtcAccountCryptoAssetWithDetails } from '@app/query/bitcoin/btc/btc-crypto-asset.hooks'; +import type { BtcAccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; + +interface BtcCryptoAssetLoaderProps { + address: string; + children(asset: BtcAccountCryptoAssetWithDetails, isInitialLoading: boolean): React.ReactNode; +} +export function BtcCryptoAssetLoader({ address, children }: BtcCryptoAssetLoaderProps) { + const { asset, isInitialLoading } = useBtcAccountCryptoAssetWithDetails(address); + return children(asset, isInitialLoading); +} diff --git a/src/app/components/loaders/current-bitcoin-signer-loader.tsx b/src/app/components/loaders/current-bitcoin-signer-loader.tsx new file mode 100644 index 00000000000..d97252f68a2 --- /dev/null +++ b/src/app/components/loaders/current-bitcoin-signer-loader.tsx @@ -0,0 +1,17 @@ +import type { P2Ret, P2TROut } from '@scure/btc-signer'; + +import { ZERO_INDEX } from '@shared/constants'; + +import type { Signer } from '@app/store/accounts/blockchain/bitcoin/bitcoin-signer'; +import { useCurrentAccountNativeSegwitSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/bitcoin/taproot-account.hooks'; + +interface CurrentBitcoinSignerLoaderProps { + children(data: { nativeSegwit: Signer; taproot: Signer }): React.ReactNode; +} +export function CurrentBitcoinSignerLoader({ children }: CurrentBitcoinSignerLoaderProps) { + const nativeSegwit = useCurrentAccountNativeSegwitSigner()?.(ZERO_INDEX); + const taproot = useCurrentAccountTaprootSigner()?.(ZERO_INDEX); + if (!taproot || !nativeSegwit) return null; + return children({ nativeSegwit, taproot }); +} diff --git a/src/app/components/loaders/stx-crypto-asset-loader.tsx b/src/app/components/loaders/stx-crypto-asset-loader.tsx new file mode 100644 index 00000000000..72824eb4770 --- /dev/null +++ b/src/app/components/loaders/stx-crypto-asset-loader.tsx @@ -0,0 +1,11 @@ +import type { StxAccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; +import { useStxAccountCryptoAssetWithDetails } from '@app/query/stacks/stx/stx-crypto-asset.hooks'; + +interface StxCryptoAssetLoaderProps { + address: string; + children(asset: StxAccountCryptoAssetWithDetails, isInitialLoading: boolean): React.ReactNode; +} +export function StxCryptoAssetLoader({ address, children }: StxCryptoAssetLoaderProps) { + const { asset, isInitialLoading } = useStxAccountCryptoAssetWithDetails(address); + return children(asset, isInitialLoading); +} diff --git a/src/app/components/crypto-assets/stacks/components/stacks-asset-avatar.tsx b/src/app/components/stacks-asset-avatar.tsx similarity index 100% rename from src/app/components/crypto-assets/stacks/components/stacks-asset-avatar.tsx rename to src/app/components/stacks-asset-avatar.tsx diff --git a/src/app/components/transaction/token-transfer-icon.tsx b/src/app/components/transaction/token-transfer-icon.tsx index 3785f97c668..bcb1375623b 100644 --- a/src/app/components/transaction/token-transfer-icon.tsx +++ b/src/app/components/transaction/token-transfer-icon.tsx @@ -1,12 +1,12 @@ import { StacksTx } from '@shared/models/transactions/stacks-transaction.model'; -import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { ArrowDownIcon } from '@app/ui/icons/arrow-down-icon'; import { ArrowUpIcon } from '@app/ui/icons/arrow-up-icon'; export function TokenTransferIcon(props: { tx: StacksTx }) { const { tx } = props; - const currentAccountStxAddress = useCurrentAccountStxAddressState(); + const currentAccountStxAddress = useCurrentStacksAccountAddress(); const isSent = tx.sender_address === currentAccountStxAddress; if (isSent) return ; diff --git a/src/app/components/tx-asset-item.tsx b/src/app/components/tx-asset-item.tsx index dfc6602366b..13a06f2f713 100644 --- a/src/app/components/tx-asset-item.tsx +++ b/src/app/components/tx-asset-item.tsx @@ -2,7 +2,7 @@ import { HStack, HstackProps, styled } from 'leather-styles/jsx'; import { isValidUrl } from '@shared/utils/validate-url'; -import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar'; +import { StacksAssetAvatar } from '@app/components/stacks-asset-avatar'; interface TxAssetItemProps extends HstackProps { iconString: string; diff --git a/src/app/features/activity-list/components/transaction-list/stacks-transaction/ft-transfer-item.tsx b/src/app/features/activity-list/components/transaction-list/stacks-transaction/ft-transfer-item.tsx index 592017f8c93..e9413e82f4b 100644 --- a/src/app/features/activity-list/components/transaction-list/stacks-transaction/ft-transfer-item.tsx +++ b/src/app/features/activity-list/components/transaction-list/stacks-transaction/ft-transfer-item.tsx @@ -6,16 +6,16 @@ import { TxTransferDetails, } from '@shared/models/transactions/stacks-transaction.model'; -import { getImageCanonicalUri } from '@app/common/crypto-assets/stacks-crypto-asset.utils'; +import { getSafeImageCanonicalUri } from '@app/common/stacks-utils'; import { calculateTokenTransferAmount, getTxCaption, } from '@app/common/transactions/stacks/transaction.utils'; import { pullContractIdFromIdentity } from '@app/common/utils'; -import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar'; +import { StacksAssetAvatar } from '@app/components/stacks-asset-avatar'; import { StacksTransactionItem } from '@app/components/stacks-transaction-item/stacks-transaction-item'; -import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query'; -import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils'; +import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query'; +import { isFtAsset } from '@app/query/stacks/token-metadata/token-metadata.utils'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { ArrowDownIcon } from '@app/ui/icons/arrow-down-icon'; import { ArrowUpIcon } from '@app/ui/icons/arrow-up-icon'; @@ -48,7 +48,7 @@ export function FtTransferItem({ ftTransfer, parentTx }: FtTransferItemProps) { const ftImageCanonicalUri = assetMetadata.image_canonical_uri && assetMetadata.name && - getImageCanonicalUri(assetMetadata.image_canonical_uri, assetMetadata.name); + getSafeImageCanonicalUri(assetMetadata.image_canonical_uri, assetMetadata.name); const icon = isOriginator ? : ; const title = `${assetMetadata.name || 'Token'} Transfer`; const value = `${isOriginator ? '-' : ''}${displayAmount.toFormat()}`; diff --git a/src/app/features/asset-list/asset-list.tsx b/src/app/features/asset-list/asset-list.tsx index c526d7e2e2c..570f48e8161 100644 --- a/src/app/features/asset-list/asset-list.tsx +++ b/src/app/features/asset-list/asset-list.tsx @@ -1,94 +1,104 @@ -import { Outlet } from 'react-router-dom'; - -import { HomePageSelectors } from '@tests/selectors/home.selectors'; import { Stack } from 'leather-styles/jsx'; import { useWalletType } from '@app/common/use-wallet-type'; +import { BitcoinContractEntryPoint } from '@app/components/bitcoin-contract-entry-point/bitcoin-contract-entry-point'; import { BitcoinNativeSegwitAccountLoader, BitcoinTaprootAccountLoader, -} from '@app/components/account/bitcoin-account-loader'; -import { BitcoinContractEntryPoint } from '@app/components/bitcoin-contract-entry-point/bitcoin-contract-entry-point'; -import { Brc20TokenAssetList } from '@app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list'; -import { RunesAssetList } from '@app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-list'; -import { Src20TokenAssetList } from '@app/components/crypto-assets/bitcoin/src20-token-asset-list/src20-token-asset-list'; -import { Stx20TokenAssetList } from '@app/components/crypto-assets/stacks/stx20-token-asset-list/stx20-token-asset-list'; +} from '@app/components/loaders/bitcoin-account-loader'; import { Brc20TokensLoader } from '@app/components/loaders/brc20-tokens-loader'; -import { BtcBalanceLoader } from '@app/components/loaders/btc-balance-loader'; +import { BtcCryptoAssetLoader } from '@app/components/loaders/btc-crypto-asset-loader'; import { RunesLoader } from '@app/components/loaders/runes-loader'; import { Src20TokensLoader } from '@app/components/loaders/src20-tokens-loader'; import { CurrentStacksAccountLoader } from '@app/components/loaders/stacks-account-loader'; import { Stx20TokensLoader } from '@app/components/loaders/stx20-tokens-loader'; -import { useHasBitcoinLedgerKeychain } from '@app/store/accounts/blockchain/bitcoin/bitcoin.ledger'; +import { StxCryptoAssetLoader } from '@app/components/loaders/stx-crypto-asset-loader'; +import { Brc20TokenAssetList } from '@app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list'; +import { RunesAssetList } from '@app/features/asset-list/bitcoin/runes-asset-list/runes-asset-list'; +import { Src20TokenAssetList } from '@app/features/asset-list/bitcoin/src20-token-asset-list/src20-token-asset-list'; +import { Stx20TokenAssetList } from '@app/features/asset-list/stacks/stx20-token-asset-list/stx20-token-asset-list'; +import { StxCryptoAssetItem } from '@app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item'; +import { StxCryptoAssetItemFallback } from '@app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback'; +import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; -import { Collectibles } from '../collectibles/collectibles'; -import { PendingBrc20TransferList } from '../pending-brc-20-transfers/pending-brc-20-transfers'; -import { AddStacksLedgerKeysItem } from './components/add-stacks-ledger-keys-item'; -import { BtcBalanceListItem } from './components/btc-balance-list-item'; -import { ConnectLedgerAssetBtn } from './components/connect-ledger-asset-button'; -import { StacksFungibleTokenAssetList } from './components/stacks-fungible-token-asset-list'; -import { StacksUnsupportedTokenAssetList } from './components/stacks-unsupported-token-asset-list'; -import { StxBalanceListItem } from './components/stx-balance-list-item'; +import { BtcCryptoAssetItem } from './bitcoin/btc-crypto-asset-item/btc-crypto-asset-item'; +import { BtcCryptoAssetItemFallback } from './bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback'; +import { Sip10TokenAssetList } from './stacks/sip10-token-asset-list/sip10-token-asset-list'; +import { Sip10TokenAssetListUnsupported } from './stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported'; -export function AssetList() { - const hasBitcoinLedgerKeys = useHasBitcoinLedgerKeychain(); +export type AssetListVariant = 'interactive' | 'read-only'; + +interface AssetListProps { + onClick?(asset: AccountCryptoAssetWithDetails): void; + variant?: AssetListVariant; +} +export function AssetList({ onClick, variant = 'read-only' }: AssetListProps) { const network = useCurrentNetwork(); const { whenWallet } = useWalletType(); + const isReadOnly = variant === 'read-only'; + return ( - + {whenWallet({ software: ( {nativeSegwitAccount => ( - - {(balance, isInitialLoading) => ( - + {(asset, isInitialLoading) => ( + )} - + )} ), ledger: ( - + } + > {nativeSegwitAccount => ( - - {(balance, isInitialLoading) => ( - + {(asset, isInitialLoading) => ( + - } + onClick={onClick} /> )} - + )} ), })} {/* Temporary duplication during Ledger Bitcoin feature dev */} - {['testnet', 'regtest'].includes(network.chain.bitcoin.bitcoinNetwork) && + {isReadOnly && + ['testnet', 'regtest'].includes(network.chain.bitcoin.bitcoinNetwork) && whenWallet({ software: , ledger: null, })} - }> + }> {account => ( <> - - - - {tokens => } - + + {(asset, isInitialLoading) => ( + + )} + + + {isReadOnly && ( + + {tokens => } + + )} )} @@ -99,28 +109,29 @@ export function AssetList() { {taprootAccount => ( <> - {tokens => } + {tokens => } - - {tokens => } - - - {runes => } - + {isReadOnly && ( + <> + + {tokens => } + + + {runes => } + + + )} )} )} - - {account => } - - - - - - + {isReadOnly && ( + + {account => } + + )} ); } diff --git a/src/app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx b/src/app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx new file mode 100644 index 00000000000..875048d35af --- /dev/null +++ b/src/app/features/asset-list/bitcoin/brc20-token-asset-list/brc20-token-asset-list.tsx @@ -0,0 +1,47 @@ +import { useNavigate } from 'react-router-dom'; + +import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; +import { Stack } from 'leather-styles/jsx'; + +import { RouteUrls } from '@shared/route-urls'; + +import { capitalize } from '@app/common/utils'; +import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; +import type { AssetListVariant } from '@app/features/asset-list/asset-list'; +import { useCurrentBtcAvailableBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; +import type { Brc20AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; +import { Brc20AvatarIcon } from '@app/ui/components/avatar/brc20-avatar-icon'; + +interface Brc20TokenAssetListProps { + assets: Brc20AccountCryptoAssetWithDetails[]; + variant?: AssetListVariant; +} +export function Brc20TokenAssetList({ assets, variant }: Brc20TokenAssetListProps) { + const navigate = useNavigate(); + const { balance, isInitialLoading } = useCurrentBtcAvailableBalanceNativeSegwit(); + + const hasPositiveBtcBalanceForFees = variant === 'interactive' && balance.amount.isGreaterThan(0); + + function navigateToBrc20SendForm(asset: Brc20AccountCryptoAssetWithDetails) { + const { balance, holderAddress, info, marketData } = asset; + navigate(RouteUrls.SendBrc20SendForm.replace(':ticker', info.symbol), { + state: { balance: balance.availableBalance, holderAddress, marketData, ticker: info.symbol }, + }); + } + + return ( + + {assets.map(asset => ( + } + isLoading={isInitialLoading} + key={asset.info.symbol} + name={asset.info.symbol} + onClick={hasPositiveBtcBalanceForFees ? () => navigateToBrc20SendForm(asset) : undefined} + /> + ))} + + ); +} diff --git a/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback.tsx b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback.tsx new file mode 100644 index 00000000000..f7e7c71e797 --- /dev/null +++ b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item-fallback.tsx @@ -0,0 +1,23 @@ +import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; +import { btcCryptoAssetPlaceholder } from '@app/query/bitcoin/btc/btc-crypto-asset.hooks'; +import { useCheckLedgerBlockchainAvailable } from '@app/store/accounts/blockchain/utils'; +import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; + +import type { AssetListVariant } from '../../asset-list'; +import { ConnectLedgerButton } from '../../components/connect-ledger-asset-button'; + +interface StxCryptoAssetItemFallbackProps { + variant: AssetListVariant; +} +export function BtcCryptoAssetItemFallback({ variant }: StxCryptoAssetItemFallbackProps) { + const checkBlockchainAvailable = useCheckLedgerBlockchainAvailable(); + if (variant === 'interactive' && !checkBlockchainAvailable('bitcoin')) return null; + return ( + } + name={btcCryptoAssetPlaceholder.info.name} + rightElement={} + /> + ); +} diff --git a/src/app/features/asset-list/components/btc-balance-list-item.tsx b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx similarity index 54% rename from src/app/features/asset-list/components/btc-balance-list-item.tsx rename to src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx index 0832542ef1f..1128f53945e 100644 --- a/src/app/features/asset-list/components/btc-balance-list-item.tsx +++ b/src/app/features/asset-list/bitcoin/btc-crypto-asset-item/btc-crypto-asset-item.tsx @@ -1,37 +1,39 @@ -import type { BtcCryptoAssetBalance } from '@leather-wallet/models'; - import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; -import { CryptoAssetItemLayout } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; -import { btcCryptoAssetInfo } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils'; +import { capitalize } from '@app/common/utils'; +import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; +import type { + AccountCryptoAssetWithDetails, + BtcAccountCryptoAssetWithDetails, +} from '@app/query/models/crypto-asset.model'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; -interface BtcBalanceListItemProps { - address: string; - balance: BtcCryptoAssetBalance; +interface BtcCryptoAssetItemProps { + asset: BtcAccountCryptoAssetWithDetails; isLoading: boolean; + onClick?(asset: AccountCryptoAssetWithDetails): void; rightElement?: React.ReactNode; } -export function BtcBalanceListItem({ - address, - balance, +export function BtcCryptoAssetItem({ + asset, isLoading, + onClick, rightElement, -}: BtcBalanceListItemProps) { +}: BtcCryptoAssetItemProps) { const marketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); const availableBalanceAsFiat = i18nFormatCurrency( - baseCurrencyAmountInQuote(balance.availableBalance, marketData) + baseCurrencyAmountInQuote(asset.balance.availableBalance, marketData) ); return ( } isLoading={isLoading} - name={btcCryptoAssetInfo.name} + name={capitalize(asset.info.name)} + onClick={onClick} rightElement={rightElement} /> ); diff --git a/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-item.layout.tsx b/src/app/features/asset-list/bitcoin/runes-asset-list/runes-asset-item.layout.tsx similarity index 100% rename from src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-item.layout.tsx rename to src/app/features/asset-list/bitcoin/runes-asset-list/runes-asset-item.layout.tsx diff --git a/src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-list.tsx b/src/app/features/asset-list/bitcoin/runes-asset-list/runes-asset-list.tsx similarity index 100% rename from src/app/components/crypto-assets/bitcoin/runes-asset-list/runes-asset-list.tsx rename to src/app/features/asset-list/bitcoin/runes-asset-list/runes-asset-list.tsx diff --git a/src/app/components/crypto-assets/bitcoin/src20-token-asset-list/src20-token-asset-item.layout.tsx b/src/app/features/asset-list/bitcoin/src20-token-asset-list/src20-token-asset-item.layout.tsx similarity index 100% rename from src/app/components/crypto-assets/bitcoin/src20-token-asset-list/src20-token-asset-item.layout.tsx rename to src/app/features/asset-list/bitcoin/src20-token-asset-list/src20-token-asset-item.layout.tsx diff --git a/src/app/components/crypto-assets/bitcoin/src20-token-asset-list/src20-token-asset-list.tsx b/src/app/features/asset-list/bitcoin/src20-token-asset-list/src20-token-asset-list.tsx similarity index 100% rename from src/app/components/crypto-assets/bitcoin/src20-token-asset-list/src20-token-asset-list.tsx rename to src/app/features/asset-list/bitcoin/src20-token-asset-list/src20-token-asset-list.tsx diff --git a/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx b/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx deleted file mode 100644 index e3540cb461e..00000000000 --- a/src/app/features/asset-list/components/add-stacks-ledger-keys-item.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import BigNumber from 'bignumber.js'; - -import { CryptoAssetItemLayout } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; -import { createStacksCryptoCurrencyAssetTypeWrapper } from '@app/query/stacks/balance/stacks-ft-balances.utils'; -import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; - -import { ConnectLedgerAssetBtn } from './connect-ledger-asset-button'; - -export function AddStacksLedgerKeysItem() { - return ( - } - rightElement={} - /> - ); -} diff --git a/src/app/features/asset-list/components/connect-ledger-asset-button.tsx b/src/app/features/asset-list/components/connect-ledger-asset-button.tsx index bc08367fa05..9e596ba057a 100644 --- a/src/app/features/asset-list/components/connect-ledger-asset-button.tsx +++ b/src/app/features/asset-list/components/connect-ledger-asset-button.tsx @@ -1,8 +1,8 @@ import { useNavigate } from 'react-router-dom'; +import type { Blockchains } from '@leather-wallet/models'; import { styled } from 'leather-styles/jsx'; -import { SupportedBlockchains } from '@shared/constants'; import { RouteUrls } from '@shared/route-urls'; import { capitalize } from '@app/common/utils'; @@ -10,10 +10,10 @@ import { immediatelyAttemptLedgerConnection } from '@app/features/ledger/hooks/u import { Button } from '@app/ui/components/button/button'; import { LedgerIcon } from '@app/ui/icons/ledger-icon'; -interface ConnectLedgerAssetBtnProps { - chain: SupportedBlockchains; +interface ConnectLedgerButtonProps { + chain: Blockchains; } -export function ConnectLedgerAssetBtn({ chain }: ConnectLedgerAssetBtnProps) { +export function ConnectLedgerButton({ chain }: ConnectLedgerButtonProps) { const navigate = useNavigate(); const onClick = () => { diff --git a/src/app/features/asset-list/components/stacks-fungible-token-asset-list.layout.tsx b/src/app/features/asset-list/components/stacks-fungible-token-asset-list.layout.tsx deleted file mode 100644 index a13708304f5..00000000000 --- a/src/app/features/asset-list/components/stacks-fungible-token-asset-list.layout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Stack } from 'leather-styles/jsx'; - -import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; - -import { StacksFungibleTokenAssetItemLayout } from '@app/components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout'; - -interface StacksFungibleTokenAssetListLayoutProps { - assetBalances: StacksFungibleTokenAssetBalance[]; -} -export function StacksFungibleTokenAssetListLayout({ - assetBalances, -}: StacksFungibleTokenAssetListLayoutProps) { - if (assetBalances.length === 0) return null; - return ( - - {assetBalances.map(assetBalance => ( - - ))} - - ); -} diff --git a/src/app/features/asset-list/components/stacks-fungible-token-asset-list.tsx b/src/app/features/asset-list/components/stacks-fungible-token-asset-list.tsx deleted file mode 100644 index 99e82df34a2..00000000000 --- a/src/app/features/asset-list/components/stacks-fungible-token-asset-list.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { useFilteredStacksFungibleTokenList } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; - -import { StacksFungibleTokenAssetListLayout } from './stacks-fungible-token-asset-list.layout'; - -interface StacksFungibleTokenAssetListProps { - address: string; -} -export function StacksFungibleTokenAssetList({ address }: StacksFungibleTokenAssetListProps) { - const stacksFilteredFtAssetBalances = useFilteredStacksFungibleTokenList({ - address, - filter: 'supported', - }); - - return ; -} diff --git a/src/app/features/asset-list/components/stx-balance-list-item.layout.stories.tsx b/src/app/features/asset-list/components/stx-balance-list-item.layout.stories.tsx deleted file mode 100644 index f7d19d348de..00000000000 --- a/src/app/features/asset-list/components/stx-balance-list-item.layout.stories.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { TooltipProvider } from '@radix-ui/react-tooltip'; -import { Meta, StoryObj } from '@storybook/react'; -import BigNumber from 'bignumber.js'; - -import { StacksBalanceListItemLayout } from './stx-balance-list-item.layout'; - -const meta: Meta = { - component: StacksBalanceListItemLayout, - tags: ['autodocs'], - title: 'Feature/StxBalanceListItem', - argTypes: {}, - parameters: {}, - decorators: [ - Story => ( - - - - ), - ], -}; - -export default meta; - -type Story = StoryObj; - -const symbol = 'STX'; - -export const StacksBalanceItem: Story = { - args: { - address: 'ST1PQHQKV0YX2K1Z0V2VQZGZGZGZGZGZGZGZGZGZG', - stxEffectiveBalance: { - balance: { decimals: 8, amount: new BigNumber(100000000000), symbol }, - blockchain: 'stacks', - type: 'crypto-currency', - asset: { - decimals: 8, - hasMemo: true, - name: 'Stacks', - symbol, - } as const, - }, - stxEffectiveUsdBalance: '$100,000', - }, -}; - -export const StacksBalanceItemWithLockedBalance: Story = { - args: { - address: 'ST1PQHQKV0YX2K1Z0V2VQZGZGZGZGZGZGZGZGZGZG', - stxEffectiveBalance: { - balance: { decimals: 8, amount: new BigNumber(100000000000), symbol }, - blockchain: 'stacks', - type: 'crypto-currency', - asset: { - decimals: 8, - hasMemo: true, - name: 'Stacks', - symbol, - } as const, - }, - stxEffectiveUsdBalance: '$100,000', - stxUsdLockedBalance: '$1,000', - stxLockedBalance: { decimals: 8, amount: new BigNumber(1000000000), symbol }, - }, -}; diff --git a/src/app/features/asset-list/components/stx-balance-list-item.layout.tsx b/src/app/features/asset-list/components/stx-balance-list-item.layout.tsx deleted file mode 100644 index c96f31df9fc..00000000000 --- a/src/app/features/asset-list/components/stx-balance-list-item.layout.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { styled } from 'leather-styles/jsx'; - -import type { StacksCryptoCurrencyAssetBalance } from '@shared/models/crypto-asset-balance.model'; -import type { Money } from '@shared/models/money.model'; - -import { ftDecimals } from '@app/common/stacks-utils'; -import { CryptoAssetItemLayout } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; -import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; -import { BulletOperator } from '@app/ui/components/bullet-separator/bullet-separator'; -import { Caption } from '@app/ui/components/typography/caption'; - -interface StacksBalanceListItemLayoutProps { - address: string; - stxEffectiveBalance: StacksCryptoCurrencyAssetBalance; - stxEffectiveUsdBalance?: string; - stxLockedBalance?: Money; - stxUsdLockedBalance?: string; - isInitialLoading?: boolean; -} -export function StacksBalanceListItemLayout(props: StacksBalanceListItemLayoutProps) { - const { - address, - stxEffectiveBalance, - stxEffectiveUsdBalance, - stxLockedBalance, - stxUsdLockedBalance, - isInitialLoading, - } = props; - - const stxAdditionalBalanceInfo = stxLockedBalance?.amount.isGreaterThan(0) ? ( - - - {ftDecimals(stxLockedBalance.amount, stxLockedBalance.decimals || 0)} locked - - ) : undefined; - - const stxAdditionalUsdBalanceInfo = stxLockedBalance?.amount.isGreaterThan(0) ? ( - {stxUsdLockedBalance} locked - ) : undefined; - - return ( - } - isLoading={isInitialLoading} - /> - ); -} diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx new file mode 100644 index 00000000000..e7b421e56ac --- /dev/null +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.tsx @@ -0,0 +1,36 @@ +import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; +import { StacksAssetAvatar } from '@app/components/stacks-asset-avatar'; +import type { + AccountCryptoAssetWithDetails, + Sip10AccountCryptoAssetWithDetails, +} from '@app/query/models/crypto-asset.model'; + +import { parseSip10TokenCryptoAssetBalance } from './sip10-token-asset-item.utils'; + +interface Sip10TokenAssetItemProps { + asset: Sip10AccountCryptoAssetWithDetails; + onClick?(asset: AccountCryptoAssetWithDetails): void; +} +export function Sip10TokenAssetItem({ asset, onClick }: Sip10TokenAssetItemProps) { + const { avatar, fiatBalance, imageCanonicalUri, title } = + parseSip10TokenCryptoAssetBalance(asset); + + return ( + + {title[0]} + + } + name={asset.info.name} + onClick={onClick} + /> + ); +} diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.utils.ts b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.utils.ts new file mode 100644 index 00000000000..e1b09175109 --- /dev/null +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-item.utils.ts @@ -0,0 +1,34 @@ +import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; + +import { convertAssetBalanceToFiat } from '@app/common/asset-utils'; +import { formatBalance } from '@app/common/format-balance'; +import { ftDecimals, getSafeImageCanonicalUri } from '@app/common/stacks-utils'; +import { spamFilter } from '@app/common/utils/spam-filter'; +import type { Sip10AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; + +export function parseSip10TokenCryptoAssetBalance(asset: Sip10AccountCryptoAssetWithDetails) { + const { balance, info } = asset; + const { contractId, decimals, imageCanonicalUri, name, symbol } = info; + + const amount = ftDecimals(balance.availableBalance.amount, decimals); + const avatar = contractId; + const dataTestId = + symbol && CryptoAssetSelectors.CryptoAssetListItem.replace('{symbol}', symbol.toLowerCase()); + const formattedBalance = formatBalance(amount); + const safeImageCanonicalUri = getSafeImageCanonicalUri(imageCanonicalUri, name); + const title = spamFilter(name); + const fiatBalance = convertAssetBalanceToFiat({ + ...asset, + balance: asset.balance.availableBalance, + }); + + return { + amount, + avatar, + fiatBalance, + dataTestId, + formattedBalance, + imageCanonicalUri: safeImageCanonicalUri, + title, + }; +} diff --git a/src/app/features/asset-list/components/stacks-unsupported-token-asset-list.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx similarity index 56% rename from src/app/features/asset-list/components/stacks-unsupported-token-asset-list.tsx rename to src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx index ce10286792a..72b52332198 100644 --- a/src/app/features/asset-list/components/stacks-unsupported-token-asset-list.tsx +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list-unsupported.tsx @@ -1,28 +1,26 @@ import { useState } from 'react'; -import { styled } from 'leather-styles/jsx'; +import { Stack, styled } from 'leather-styles/jsx'; -import { useFilteredStacksFungibleTokenList } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; +import { useFilteredSip10AccountCryptoAssetsWithDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; import { Accordion } from '@app/ui/components/accordion/accordion'; -import { StacksFungibleTokenAssetListLayout } from './stacks-fungible-token-asset-list.layout'; +import { Sip10TokenAssetItem } from './sip10-token-asset-item'; const accordionValue = 'accordion-unsupported-token-asset-list'; -export function StacksUnsupportedTokenAssetList({ address }: { address: string }) { - const stacksFilteredFtAssetBalances = useFilteredStacksFungibleTokenList({ +export function Sip10TokenAssetListUnsupported({ address }: { address: string }) { + const [isOpen, setIsOpen] = useState(false); + const assets = useFilteredSip10AccountCryptoAssetsWithDetails({ address, filter: 'unsupported', }); - const [isOpen, setIsOpen] = useState(false); function onValueChange(value: string) { setIsOpen(value === accordionValue); } - if (stacksFilteredFtAssetBalances.length === 0) { - return null; - } + if (!assets.length) return null; return ( @@ -31,7 +29,11 @@ export function StacksUnsupportedTokenAssetList({ address }: { address: string } View {isOpen ? 'fewer' : 'more'} - + + {assets.map(asset => ( + + ))} + diff --git a/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx new file mode 100644 index 00000000000..28e2b49c71e --- /dev/null +++ b/src/app/features/asset-list/stacks/sip10-token-asset-list/sip10-token-asset-list.tsx @@ -0,0 +1,29 @@ +import { Stack } from 'leather-styles/jsx'; + +import { isDefined } from '@shared/utils'; + +import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; +import { useFilteredSip10AccountCryptoAssetsWithDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; + +import { Sip10TokenAssetItem } from './sip10-token-asset-item'; + +interface Sip10TokenAssetListProps { + address: string; + onClick?(asset: AccountCryptoAssetWithDetails): void; +} +export function Sip10TokenAssetList({ address, onClick }: Sip10TokenAssetListProps) { + const assets = useFilteredSip10AccountCryptoAssetsWithDetails({ + address, + filter: isDefined(onClick) ? 'all' : 'supported', + }); + + if (!assets.length) return null; + + return ( + + {assets.map(asset => ( + + ))} + + ); +} diff --git a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback.tsx b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback.tsx new file mode 100644 index 00000000000..1eb4c256dcd --- /dev/null +++ b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item-fallback.tsx @@ -0,0 +1,23 @@ +import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; +import { stxCryptoAssetPlaceholder } from '@app/query/stacks/stx/stx-crypto-asset.hooks'; +import { useCheckLedgerBlockchainAvailable } from '@app/store/accounts/blockchain/utils'; +import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; + +import type { AssetListVariant } from '../../asset-list'; +import { ConnectLedgerButton } from '../../components/connect-ledger-asset-button'; + +interface StxCryptoAssetItemFallbackProps { + variant: AssetListVariant; +} +export function StxCryptoAssetItemFallback({ variant }: StxCryptoAssetItemFallbackProps) { + const checkBlockchainAvailable = useCheckLedgerBlockchainAvailable(); + if (variant === 'interactive' && !checkBlockchainAvailable('stacks')) return null; + return ( + } + name={stxCryptoAssetPlaceholder.info.name} + rightElement={} + /> + ); +} diff --git a/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.stories.tsx b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.stories.tsx new file mode 100644 index 00000000000..7fe04fcfecb --- /dev/null +++ b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.stories.tsx @@ -0,0 +1,81 @@ +import { TooltipProvider } from '@radix-ui/react-tooltip'; +import { Meta, StoryObj } from '@storybook/react'; +import { QueryClientProvider } from '@tanstack/react-query'; +import BigNumber from 'bignumber.js'; + +import { queryClient } from '@app/common/persistence'; + +import { StxCryptoAssetItem as Component } from './stx-crypto-asset-item'; + +const meta: Meta = { + component: Component, + tags: ['autodocs'], + title: 'Feature/StxCryptoAssetItem', + argTypes: {}, + parameters: {}, + decorators: [ + Story => ( + + + + + + ), + ], +}; + +export default meta; + +type Story = StoryObj; + +const symbol = 'STX'; + +const stxCryptoAssetBalance = { + availableBalance: { amount: new BigNumber(100000000000), decimals: 6, symbol }, + availableUnlockedBalance: { amount: new BigNumber(100000000000), decimals: 6, symbol }, + inboundBalance: { amount: new BigNumber(0), decimals: 6, symbol }, + outboundBalance: { amount: new BigNumber(0), decimals: 6, symbol }, + pendingBalance: { amount: new BigNumber(0), decimals: 6, symbol }, + totalBalance: { amount: new BigNumber(0), decimals: 6, symbol }, + unlockedBalance: { amount: new BigNumber(0), decimals: 6, symbol }, +}; + +export const StxCryptoAssetItem: Story = { + args: { + asset: { + info: { + decimals: 6, + hasMemo: true, + name: 'stacks', + symbol: 'STX', + }, + balance: { + ...stxCryptoAssetBalance, + lockedBalance: { amount: new BigNumber(0), decimals: 6, symbol }, + }, + chain: 'stacks', + marketData: null, + type: 'stx', + }, + }, +}; + +export const StxCryptoAssetItemWithLockedBalance: Story = { + args: { + asset: { + info: { + decimals: 6, + hasMemo: true, + name: 'stacks', + symbol: 'STX', + }, + balance: { + ...stxCryptoAssetBalance, + lockedBalance: { amount: new BigNumber(1000000000), decimals: 6, symbol }, + }, + chain: 'stacks', + marketData: null, + type: 'stx', + }, + }, +}; diff --git a/src/app/features/asset-list/components/stx-balance-list-item.tsx b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx similarity index 63% rename from src/app/features/asset-list/components/stx-balance-list-item.tsx rename to src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx index afb80bd5f64..55092bab5f1 100644 --- a/src/app/features/asset-list/components/stx-balance-list-item.tsx +++ b/src/app/features/asset-list/stacks/stx-crypo-asset-item/stx-crypto-asset-item.tsx @@ -3,24 +3,26 @@ import { styled } from 'leather-styles/jsx'; import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money'; import { i18nFormatCurrency } from '@app/common/money/format-money'; import { ftDecimals } from '@app/common/stacks-utils'; -import { CryptoAssetItemLayout } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; -import { stxCryptoAssetInfo } from '@app/components/crypto-assets/crypto-asset-item/crypto-asset-item.utils'; +import { capitalize } from '@app/common/utils'; +import { CryptoAssetItemLayout } from '@app/components/crypto-asset-item/crypto-asset-item.layout'; import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; -import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import type { + AccountCryptoAssetWithDetails, + StxAccountCryptoAssetWithDetails, +} from '@app/query/models/crypto-asset.model'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; import { BulletOperator } from '@app/ui/components/bullet-separator/bullet-separator'; import { Caption } from '@app/ui/components/typography/caption'; -interface StxBalanceListItemProps { - address: string; +interface StxCryptoAssetItemProps { + asset: StxAccountCryptoAssetWithDetails; + isLoading: boolean; + onClick?(asset: AccountCryptoAssetWithDetails): void; } -export function StxBalanceListItem({ address }: StxBalanceListItemProps) { +export function StxCryptoAssetItem({ asset, isLoading, onClick }: StxCryptoAssetItemProps) { const marketData = useCryptoCurrencyMarketDataMeanAverage('STX'); - const { data: stxCryptoAssetBalance, isInitialLoading } = useStxCryptoAssetBalance(address); - if (!stxCryptoAssetBalance) return null; - - const { availableBalance, lockedBalance } = stxCryptoAssetBalance; + const { availableBalance, lockedBalance } = asset.balance; const showAdditionalInfo = lockedBalance.amount.isGreaterThan(0); const lockedBalanceAsFiat = i18nFormatCurrency( @@ -32,21 +34,21 @@ export function StxBalanceListItem({ address }: StxBalanceListItemProps) { const additionalBalanceInfo = ( - {ftDecimals(lockedBalance.amount, lockedBalance.decimals || 0)} locked + {ftDecimals(lockedBalance.amount, lockedBalance.decimals)} locked ); const additionalBalanceInfoAsFiat = {lockedBalanceAsFiat} locked; return ( } - isLoading={isInitialLoading} - name={stxCryptoAssetInfo.name} + isLoading={isLoading} + name={capitalize(asset.info.name)} + onClick={onClick} /> ); } diff --git a/src/app/components/crypto-assets/stacks/stx20-token-asset-list/stx20-token-asset-item.layout.tsx b/src/app/features/asset-list/stacks/stx20-token-asset-list/stx20-token-asset-item.layout.tsx similarity index 100% rename from src/app/components/crypto-assets/stacks/stx20-token-asset-list/stx20-token-asset-item.layout.tsx rename to src/app/features/asset-list/stacks/stx20-token-asset-list/stx20-token-asset-item.layout.tsx diff --git a/src/app/components/crypto-assets/stacks/stx20-token-asset-list/stx20-token-asset-list.tsx b/src/app/features/asset-list/stacks/stx20-token-asset-list/stx20-token-asset-list.tsx similarity index 100% rename from src/app/components/crypto-assets/stacks/stx20-token-asset-list/stx20-token-asset-list.tsx rename to src/app/features/asset-list/stacks/stx20-token-asset-list/stx20-token-asset-list.tsx diff --git a/src/app/features/collectibles/collectibles.tsx b/src/app/features/collectibles/collectibles.tsx index 4a134de63c1..e6a34ce1c2f 100644 --- a/src/app/features/collectibles/collectibles.tsx +++ b/src/app/features/collectibles/collectibles.tsx @@ -6,7 +6,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { RouteUrls } from '@shared/route-urls'; import { useWalletType } from '@app/common/use-wallet-type'; -import { CurrentBitcoinSignerLoader } from '@app/components/loaders/bitcoin-account-loader'; +import { CurrentBitcoinSignerLoader } from '@app/components/loaders/current-bitcoin-signer-loader'; import { CurrentStacksAccountLoader } from '@app/components/loaders/stacks-account-loader'; import { useConfigNftMetadataEnabled } from '@app/query/common/remote-config/remote-config.query'; diff --git a/src/app/features/collectibles/components/stacks/stacks-crypto-assets.tsx b/src/app/features/collectibles/components/stacks/stacks-crypto-assets.tsx index ffbb6691fe7..265871ee7d5 100644 --- a/src/app/features/collectibles/components/stacks/stacks-crypto-assets.tsx +++ b/src/app/features/collectibles/components/stacks/stacks-crypto-assets.tsx @@ -3,7 +3,7 @@ import { useEffect } from 'react'; import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; import { parseIfValidPunycode } from '@app/common/utils'; import { useCurrentAccountNames } from '@app/query/stacks/bns/bns.hooks'; -import { useStacksNonFungibleTokensMetadata } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.hooks'; +import { useStacksNonFungibleTokensMetadata } from '@app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.hooks'; import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; import { StacksBnsName } from './stacks-bns-name'; diff --git a/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx b/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx index 7bb2cb0c810..3142219c944 100644 --- a/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx +++ b/src/app/features/dialogs/increase-fee-dialog/increase-stx-fee-dialog.tsx @@ -18,7 +18,7 @@ import { LoadingSpinner } from '@app/components/loading-spinner'; import { StacksTransactionItem } from '@app/components/stacks-transaction-item/stacks-transaction-item'; import { useStacksBroadcastTransaction } from '@app/features/stacks-transaction-request/hooks/use-stacks-broadcast-transaction'; import { useToast } from '@app/features/toasts/use-toast'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; import { useSubmittedTransactionsActions } from '@app/store/submitted-transactions/submitted-transactions.hooks'; import { useRawDeserializedTxState, useRawTxIdState } from '@app/store/transactions/raw.hooks'; import { Dialog } from '@app/ui/components/containers/dialog/dialog'; @@ -42,7 +42,7 @@ export function IncreaseStxFeeDialog() { const refreshAccountData = useRefreshAllAccountData(); const tx = useSelectedTx(); const [, setTxId] = useRawTxIdState(); - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); + const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); const submittedTransactionsActions = useSubmittedTransactionsActions(); const rawTx = useRawDeserializedTxState(); const { stacksBroadcastTransaction } = useStacksBroadcastTransaction('STX'); diff --git a/src/app/features/selectable-asset-list/crypto-asset-list-item.tsx b/src/app/features/selectable-asset-list/crypto-asset-list-item.tsx deleted file mode 100644 index 733af8c68b5..00000000000 --- a/src/app/features/selectable-asset-list/crypto-asset-list-item.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model'; - -import { CryptoAssetItemLayout } from '../../components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; -import { CryptoCurrencyAssetIcon } from './crypto-currency-asset-icon'; -import { FungibleTokenAssetItem } from './fungible-token-asset-item'; - -interface CryptoAssetListItemProps { - assetBalance: AllTransferableCryptoAssetBalances; - onClick(): void; -} -export function CryptoAssetListItem(props: CryptoAssetListItemProps) { - const { assetBalance, onClick } = props; - const { blockchain, type } = assetBalance; - - switch (type) { - case 'crypto-currency': - return ( - } - onClick={onClick} - /> - ); - case 'fungible-token': - return ; - default: - return null; - } -} diff --git a/src/app/features/selectable-asset-list/crypto-asset-list.tsx b/src/app/features/selectable-asset-list/crypto-asset-list.tsx deleted file mode 100644 index 5a2c4a9d3d0..00000000000 --- a/src/app/features/selectable-asset-list/crypto-asset-list.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { CryptoAssetSelectors } from '@tests/selectors/crypto-asset.selectors'; -import { Stack } from 'leather-styles/jsx'; - -import type { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model'; -import { StacksFungibleTokenAsset } from '@shared/models/crypto-asset.model'; - -import { useWalletType } from '@app/common/use-wallet-type'; -import { BitcoinNativeSegwitAccountLoader } from '@app/components/account/bitcoin-account-loader'; -import { Brc20TokenAssetList } from '@app/components/crypto-assets/bitcoin/brc20-token-asset-list/brc20-token-asset-list'; -import { Brc20TokensLoader } from '@app/components/loaders/brc20-tokens-loader'; -import { BtcBalanceLoader } from '@app/components/loaders/btc-balance-loader'; -import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; - -import { CryptoAssetItemLayout } from '../../components/crypto-assets/crypto-asset-item/crypto-asset-item.layout'; -import { CryptoAssetListItem } from './crypto-asset-list-item'; - -interface CryptoAssetListProps { - cryptoAssetBalances: AllTransferableCryptoAssetBalances[]; - onItemClick(cryptoAssetBalance: AllTransferableCryptoAssetBalances): void; - variant: 'send' | 'fund'; -} -export function CryptoAssetList({ - cryptoAssetBalances, - onItemClick, - variant, -}: CryptoAssetListProps) { - const { whenWallet } = useWalletType(); - - return ( - - - {signer => ( - - {(balance, isLoading) => ( - } - onClick={() => onItemClick(balance)} - isLoading={isLoading} - /> - )} - - )} - - {cryptoAssetBalances.map(cryptoAssetBalance => ( - onItemClick(cryptoAssetBalance)} - assetBalance={cryptoAssetBalance} - key={ - cryptoAssetBalance.asset.name ?? - (cryptoAssetBalance.asset as StacksFungibleTokenAsset).contractAssetName - } - /> - ))} - {variant === 'send' && - whenWallet({ - software: ( - - {() => ( - - {brc20Tokens => } - - )} - - ), - ledger: null, - })} - - ); -} diff --git a/src/app/features/selectable-asset-list/crypto-currency-asset-icon.tsx b/src/app/features/selectable-asset-list/crypto-currency-asset-icon.tsx deleted file mode 100644 index 340164184aa..00000000000 --- a/src/app/features/selectable-asset-list/crypto-currency-asset-icon.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import type { Blockchains } from '@shared/models/blockchain.model'; - -import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; -import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; - -export function CryptoCurrencyAssetIcon(props: { blockchain: Blockchains }) { - switch (props.blockchain) { - case 'bitcoin': - return ; - case 'stacks': - return ; - default: - return <>; - } -} diff --git a/src/app/features/selectable-asset-list/fungible-token-asset-item.tsx b/src/app/features/selectable-asset-list/fungible-token-asset-item.tsx deleted file mode 100644 index 61876fe5430..00000000000 --- a/src/app/features/selectable-asset-list/fungible-token-asset-item.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { FlexProps } from 'leather-styles/jsx'; - -import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; - -import { StacksFungibleTokenAssetItemLayout } from '../../components/crypto-assets/stacks/fungible-token-asset/stacks-fungible-token-asset-item.layout'; - -interface FungibleTokenAssetItemProps extends FlexProps { - assetBalance: StacksFungibleTokenAssetBalance; - onClick(): void; -} -export function FungibleTokenAssetItem({ assetBalance, onClick }: FungibleTokenAssetItemProps) { - const { blockchain } = assetBalance; - - switch (blockchain) { - case 'stacks': - return ; - default: - return null; - } -} diff --git a/src/app/features/stacks-transaction-request/contract-deploy-details/contract-deploy-details.tsx b/src/app/features/stacks-transaction-request/contract-deploy-details/contract-deploy-details.tsx index 8530574c866..f795222bddd 100644 --- a/src/app/features/stacks-transaction-request/contract-deploy-details/contract-deploy-details.tsx +++ b/src/app/features/stacks-transaction-request/contract-deploy-details/contract-deploy-details.tsx @@ -7,8 +7,8 @@ import { AttachmentRow } from '@app/features/stacks-transaction-request/attachme import { ContractPreviewLayout } from '@app/features/stacks-transaction-request/contract-preview'; import { Row } from '@app/features/stacks-transaction-request/row'; import { - useCurrentAccountStxAddressState, useCurrentStacksAccount, + useCurrentStacksAccountAddress, } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; import { CodeBlock } from '@app/ui/components/codeblock'; @@ -18,7 +18,7 @@ function ContractCodeSection() { const transactionRequest = useTransactionRequestState(); const currentAccount = useCurrentStacksAccount(); - const currentAccountStxAddress = useCurrentAccountStxAddressState(); + const currentAccountStxAddress = useCurrentStacksAccountAddress(); if ( !transactionRequest || @@ -63,7 +63,7 @@ function TabButton(props: TabButtonProps) { export function ContractDeployDetails() { const transactionRequest = useTransactionRequestState(); const currentAccount = useCurrentStacksAccount(); - const currentAccountStxAddress = useCurrentAccountStxAddressState(); + const currentAccountStxAddress = useCurrentStacksAccountAddress(); const [tab, setTab] = useState<'details' | 'code'>('details'); if ( diff --git a/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts b/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts index b4fb9e0f94f..cc0ce3116bb 100644 --- a/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts +++ b/src/app/features/stacks-transaction-request/hooks/use-transaction-error.ts @@ -11,7 +11,7 @@ import { initialSearchParams } from '@app/common/initial-search-params'; import { stxToMicroStx } from '@app/common/money/unit-conversion'; import { validateStacksAddress } from '@app/common/stacks-utils'; import { TransactionErrorReason } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; import { useContractInterface } from '@app/query/stacks/contract/contract.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; @@ -27,7 +27,7 @@ export function useTransactionError() { const { values } = useFormikContext(); const currentAccount = useCurrentStacksAccount(); - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); + const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); return useMemo(() => { if (!origin) return TransactionErrorReason.ExpiredRequest; diff --git a/src/app/features/stacks-transaction-request/post-conditions/fungible-post-condition-item.tsx b/src/app/features/stacks-transaction-request/post-conditions/fungible-post-condition-item.tsx index 3010f23edc2..7ed1c68b3f0 100644 --- a/src/app/features/stacks-transaction-request/post-conditions/fungible-post-condition-item.tsx +++ b/src/app/features/stacks-transaction-request/post-conditions/fungible-post-condition-item.tsx @@ -3,8 +3,7 @@ import { Suspense } from 'react'; import { TransactionTypes } from '@stacks/connect'; import { FungiblePostCondition, addressToString } from '@stacks/transactions'; -import { getImageCanonicalUri } from '@app/common/crypto-assets/stacks-crypto-asset.utils'; -import { ftDecimals } from '@app/common/stacks-utils'; +import { ftDecimals, getSafeImageCanonicalUri } from '@app/common/stacks-utils'; import { getAmountFromPostCondition, getIconStringFromPostCondition, @@ -36,7 +35,7 @@ function FungiblePostConditionItemSuspense( const imageCanonicalUri = asset?.image_canonical_uri && asset.name && - getImageCanonicalUri(asset.image_canonical_uri, asset.name); + getSafeImageCanonicalUri(asset.image_canonical_uri, asset.name); const title = getPostConditionTitle(pc); const iconString = imageCanonicalUri ?? getIconStringFromPostCondition(pc); diff --git a/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx b/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx index eb2d2958fb5..bb0fad7f8fa 100644 --- a/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx +++ b/src/app/features/stacks-transaction-request/stacks-transaction-signer.tsx @@ -26,7 +26,7 @@ import { PostConditionModeWarning } from '@app/features/stacks-transaction-reque import { PostConditions } from '@app/features/stacks-transaction-request/post-conditions/post-conditions'; import { StxTransferDetails } from '@app/features/stacks-transaction-request/stx-transfer-details/stx-transfer-details'; import { TransactionError } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; @@ -55,7 +55,7 @@ export function StacksTransactionSigner({ const transactionRequest = useTransactionRequestState(); const { data: stxFees } = useCalculateStacksTxFees(stacksTransaction); const analytics = useAnalytics(); - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); + const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); const navigate = useNavigate(); const { data: nextNonce } = useNextNonce(); const { search } = useLocation(); diff --git a/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx b/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx index 0860c486752..d799820ac05 100644 --- a/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx +++ b/src/app/features/stacks-transaction-request/transaction-error/error-messages.tsx @@ -12,7 +12,7 @@ import { useScrollLock } from '@app/common/hooks/use-scroll-lock'; import { stacksValue } from '@app/common/stacks-utils'; import { SwitchAccountDialog } from '@app/features/dialogs/switch-account-dialog/switch-account-dialog'; import { ErrorMessage } from '@app/features/stacks-transaction-request/transaction-error/error-message'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; import { Button } from '@app/ui/components/button/button'; @@ -59,7 +59,7 @@ export const FeeInsufficientFundsErrorMessage = memo(props => { export const StxTransferInsufficientFundsErrorMessage = memo(props => { const pendingTransaction = useTransactionRequestState(); - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); + const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); return ( - [stxCryptoCurrencyAssetBalance].filter(isDefined).filter(assetBalance => - whenWallet({ - ledger: checkBlockchainAvailable(assetBalance?.blockchain), - software: true, - }) - ), - [stxCryptoCurrencyAssetBalance, checkBlockchainAvailable, whenWallet] - ); - const navigateToFund = useCallback( - (cryptoAssetBalance: AllTransferableCryptoAssetBalances) => { - const { asset } = cryptoAssetBalance; - - const symbol = asset.symbol === '' ? asset.contractAssetName : asset.symbol; - navigate(RouteUrls.Fund.replace(':currency', symbol.toUpperCase())); - }, + (asset: AccountCryptoAssetWithDetails) => + navigate(RouteUrls.Fund.replace(':currency', asset.info.symbol)), [navigate] ); @@ -53,13 +36,37 @@ export function ChooseCryptoAssetToFund() { } > - - - + + + {signer => ( + + {(asset, isLoading) => ( + } + isLoading={isLoading} + name={capitalize(asset.info.name)} + onClick={() => navigateToFund(asset)} + /> + )} + + )} + + + + {account => ( + + {(asset, isInitialLoading) => ( + navigateToFund(asset)} + /> + )} + + )} + + diff --git a/src/app/pages/fund/fund.tsx b/src/app/pages/fund/fund.tsx index 438103dcf1a..d2d7f7ef77d 100644 --- a/src/app/pages/fund/fund.tsx +++ b/src/app/pages/fund/fund.tsx @@ -7,8 +7,6 @@ import type { CryptoCurrencies } from '@shared/models/currencies.model'; import { RouteUrls } from '@shared/route-urls'; import { FullPageLoadingSpinner } from '@app/components/loading-spinner'; -import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; -import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSignerNullable } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; @@ -26,36 +24,27 @@ interface FundCryptoCurrencyInfo { export function FundPage() { const currentStxAccount = useCurrentStacksAccount(); const bitcoinSigner = useCurrentAccountNativeSegwitIndexZeroSignerNullable(); - const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit( - bitcoinSigner?.address ?? '' - ); - const { data: stxCryptoAssetBalance } = useStxCryptoAssetBalance( - currentStxAccount?.address ?? '' - ); const { currency = 'STX' } = useParams(); const fundCryptoCurrencyMap: Record = { BTC: { address: bitcoinSigner?.address, - balance: btcCryptoAssetBalance, - blockchain: 'Bitcoin', + blockchain: 'bitcoin', route: RouteUrls.ReceiveBtc, symbol: currency, }, STX: { address: currentStxAccount?.address, - balance: stxCryptoAssetBalance, - blockchain: 'Stacks', + blockchain: 'stacks', route: RouteUrls.ReceiveStx, symbol: currency, }, }; - const { address, balance, blockchain, route, symbol } = + const { address, blockchain, route, symbol } = fundCryptoCurrencyMap[currency as CryptoCurrencies]; - // TODO: Asset refactor: Why is the balance needed here? - if (!address || !balance) return ; + if (!address) return ; return ( <> diff --git a/src/app/pages/home/components/account-actions.tsx b/src/app/pages/home/components/account-actions.tsx index 9fbc32d8e0c..50d5eebda05 100644 --- a/src/app/pages/home/components/account-actions.tsx +++ b/src/app/pages/home/components/account-actions.tsx @@ -51,6 +51,7 @@ export function AccountActions() { [ChainID.Mainnet]: ( } label="Swap" onClick={() => navigate(RouteUrls.Swap.replace(':base', 'STX').replace(':quote', ''))} diff --git a/src/app/pages/home/components/assets.tsx b/src/app/pages/home/components/assets.tsx new file mode 100644 index 00000000000..2568d5e08f0 --- /dev/null +++ b/src/app/pages/home/components/assets.tsx @@ -0,0 +1,19 @@ +import { Outlet } from 'react-router-dom'; + +import { HomePageSelectors } from '@tests/selectors/home.selectors'; +import { Stack } from 'leather-styles/jsx'; + +import { AssetList } from '@app/features/asset-list/asset-list'; +import { Collectibles } from '@app/features/collectibles/collectibles'; +import { PendingBrc20TransferList } from '@app/features/pending-brc-20-transfers/pending-brc-20-transfers'; + +export function Assets() { + return ( + + + + + + + ); +} diff --git a/src/app/pages/home/components/send-button.tsx b/src/app/pages/home/components/send-button.tsx index 60fbf605d12..33da083cc91 100644 --- a/src/app/pages/home/components/send-button.tsx +++ b/src/app/pages/home/components/send-button.tsx @@ -8,19 +8,25 @@ import { RouteUrls } from '@shared/route-urls'; import { useWalletType } from '@app/common/use-wallet-type'; import { whenPageMode } from '@app/common/utils'; import { openIndexPageInNewTab } from '@app/common/utils/open-in-new-tab'; -import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; -import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/stx-balance.hooks'; -import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; +import { useStxCryptoAssetBalance } from '@app/query/stacks/balance/account-balance.hooks'; +import { useTransferableSip10CryptoAssetsWithDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; +import { useCurrentAccountNativeSegwitIndexZeroSignerNullable } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { IconButton } from '@app/ui/components/icon-button/icon-button'; import { SendIcon } from '@app/ui/icons'; function SendButtonSuspense() { const navigate = useNavigate(); const { whenWallet } = useWalletType(); - const account = useCurrentStacksAccount(); - const { data: stxCryptoAssetBalance } = useStxCryptoAssetBalance(account?.address ?? ''); - const ftAssets = useTransferableStacksFungibleTokenAssetBalances(account?.address ?? ''); - const isDisabled = !stxCryptoAssetBalance && ftAssets?.length === 0; + const address = useCurrentStacksAccountAddress(); + const btcAddress = useCurrentAccountNativeSegwitIndexZeroSignerNullable()?.address; + const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit(btcAddress ?? ''); + const { data: stxCryptoAssetBalance } = useStxCryptoAssetBalance(address); + const stacksFtAssets = useTransferableSip10CryptoAssetsWithDetails(address); + + const isDisabled = + !btcCryptoAssetBalance && !stxCryptoAssetBalance && stacksFtAssets?.length === 0; return ( - } /> + } /> }> {homePageModalRoutes} diff --git a/src/app/pages/receive/receive-dialog.tsx b/src/app/pages/receive/receive-dialog.tsx index c3d189dce5a..390f7463b99 100644 --- a/src/app/pages/receive/receive-dialog.tsx +++ b/src/app/pages/receive/receive-dialog.tsx @@ -11,7 +11,7 @@ import { useLocationState } from '@app/common/hooks/use-location-state'; import { useBackgroundLocationRedirect } from '@app/routes/hooks/use-background-location-redirect'; import { useZeroIndexTaprootAddress } from '@app/store/accounts/blockchain/bitcoin/bitcoin.hooks'; import { useCurrentAccountNativeSegwitAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; -import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { Dialog } from '@app/ui/components/containers/dialog/dialog'; import { Header } from '@app/ui/components/containers/headers/header'; import { Tabs } from '@app/ui/components/tabs/tabs'; @@ -39,7 +39,7 @@ export function ReceiveDialog({ type = 'full' }: ReceiveDialogProps) { const navigate = useNavigate(); const location = useLocation(); const btcAddressNativeSegwit = useCurrentAccountNativeSegwitAddressIndexZero(); - const stxAddress = useCurrentAccountStxAddressState(); + const stxAddress = useCurrentStacksAccountAddress(); const accountIndex = get(location.state, 'accountIndex', undefined); const btcAddressTaproot = useZeroIndexTaprootAddress(accountIndex); diff --git a/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx b/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx index 0b7c3caa9ca..2cb7f107d19 100644 --- a/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx +++ b/src/app/pages/send/choose-crypto-asset/choose-crypto-asset.tsx @@ -2,44 +2,38 @@ import { useNavigate } from 'react-router-dom'; import { Box, styled } from 'leather-styles/jsx'; -import { AllTransferableCryptoAssetBalances } from '@shared/models/crypto-asset-balance.model'; import { RouteUrls } from '@shared/route-urls'; -import { useAllTransferableCryptoAssetBalances } from '@app/common/hooks/use-transferable-asset-balances.hooks'; -import { useWalletType } from '@app/common/use-wallet-type'; -import { CryptoAssetList } from '@app/features/selectable-asset-list/crypto-asset-list'; +import { AssetList } from '@app/features/asset-list/asset-list'; import { useToast } from '@app/features/toasts/use-toast'; import { useConfigBitcoinSendEnabled } from '@app/query/common/remote-config/remote-config.query'; -import { useCheckLedgerBlockchainAvailable } from '@app/store/accounts/blockchain/utils'; +import type { AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; +import { getSip10InfoFromAsset } from '@app/query/stacks/sip10/sip10-tokens.hooks'; import { Card } from '@app/ui/layout/card/card'; +import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; export function ChooseCryptoAsset() { - const toast = useToast(); - const allTransferableCryptoAssetBalances = useAllTransferableCryptoAssetBalances(); - - const { whenWallet } = useWalletType(); const navigate = useNavigate(); const isBitcoinSendEnabled = useConfigBitcoinSendEnabled(); + const toast = useToast(); - const checkBlockchainAvailable = useCheckLedgerBlockchainAvailable(); - - function navigateToSendForm(cryptoAssetBalance: AllTransferableCryptoAssetBalances) { - const { asset } = cryptoAssetBalance; - if (asset.symbol === 'BTC' && !isBitcoinSendEnabled) { - return navigate(RouteUrls.SendBtcDisabled); - } - const symbol = asset.symbol === '' ? asset.contractAssetName : asset.symbol.toLowerCase(); - - if (cryptoAssetBalance.type === 'fungible-token') { - const asset = cryptoAssetBalance.asset; - if (!asset.contractId) { - toast.error('Unable to find contract id'); + function navigateToSendForm(asset: AccountCryptoAssetWithDetails) { + switch (asset.type) { + case 'btc': + if (!isBitcoinSendEnabled) return navigate(RouteUrls.SendBtcDisabled); + return navigate(`${RouteUrls.SendCryptoAsset}/${asset.info.symbol.toLowerCase()}`); + case 'sip-10': + const info = getSip10InfoFromAsset(asset); + if (info) { + const { assetName } = getAssetStringParts(info.contractId); + const symbol = !info.symbol ? assetName : info.symbol.toLowerCase(); + return navigate(`${RouteUrls.SendCryptoAsset}/${symbol}/${info.contractId}`); + } + toast.error('No contract id'); return navigate('..'); - } - const contractId = `${asset.contractId.split('::')[0]}`; - return navigate(`${RouteUrls.SendCryptoAsset}/${symbol}/${contractId}`); + default: + return navigate(`${RouteUrls.SendCryptoAsset}/${asset.info.symbol.toLowerCase()}`); } - navigate(`${RouteUrls.SendCryptoAsset}/${symbol}`); } return ( @@ -50,17 +44,8 @@ export function ChooseCryptoAsset() { } > - - navigateToSendForm(cryptoAssetBalance)} - cryptoAssetBalances={allTransferableCryptoAssetBalances.filter(asset => - whenWallet({ - ledger: checkBlockchainAvailable(asset.blockchain), - software: true, - }) - )} - variant="send" - /> + + ); diff --git a/src/app/features/selectable-asset-list/send-btc-disabled.tsx b/src/app/pages/send/choose-crypto-asset/send-btc-disabled.tsx similarity index 100% rename from src/app/features/selectable-asset-list/send-btc-disabled.tsx rename to src/app/pages/send/choose-crypto-asset/send-btc-disabled.tsx diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc-20/brc-20-choose-fee.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx similarity index 100% rename from src/app/pages/send/send-crypto-asset-form/form/brc-20/brc-20-choose-fee.tsx rename to src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-choose-fee.tsx diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc-20/brc20-send-form-confirmation.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx similarity index 100% rename from src/app/pages/send/send-crypto-asset-form/form/brc-20/brc20-send-form-confirmation.tsx rename to src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form-confirmation.tsx diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc-20/brc20-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form.tsx similarity index 100% rename from src/app/pages/send/send-crypto-asset-form/form/brc-20/brc20-send-form.tsx rename to src/app/pages/send/send-crypto-asset-form/form/brc20/brc20-send-form.tsx diff --git a/src/app/pages/send/send-crypto-asset-form/form/brc-20/use-brc20-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/brc20/use-brc20-send-form.tsx similarity index 100% rename from src/app/pages/send/send-crypto-asset-form/form/brc-20/use-brc20-send-form.tsx rename to src/app/pages/send/send-crypto-asset-form/form/brc20/use-brc20-send-form.tsx 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 f60b5dd0f94..57eec8dabd2 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 @@ -9,7 +9,7 @@ import { CryptoCurrencies } from '@shared/models/currencies.model'; import { formatMoney } from '@app/common/money/format-money'; import { HighFeeDialog } from '@app/features/dialogs/high-fee-dialog/high-fee-dialog'; -import { useBtcCryptoAssetBalanceNativeSegwit } from '@app/query/bitcoin/balance/btc-balance-native-segwit.hooks'; +import { useBtcAccountCryptoAssetWithDetails } from '@app/query/bitcoin/btc/btc-crypto-asset.hooks'; import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; import { BtcAvatarIcon } from '@app/ui/components/avatar/btc-avatar-icon'; @@ -36,11 +36,9 @@ export function BtcSendForm() { const routeState = useSendFormRouteState(); const btcMarketData = useCryptoCurrencyMarketDataMeanAverage(symbol); - const nativeSegwitSigner = useCurrentAccountNativeSegwitIndexZeroSigner(); - // TODO: Asset refactor: need asset here - const { btcCryptoAssetBalance } = useBtcCryptoAssetBalanceNativeSegwit( - nativeSegwitSigner.address - ); + const { address } = useCurrentAccountNativeSegwitIndexZeroSigner(); + const { asset } = useBtcAccountCryptoAssetWithDetails(address); + const { balance, info } = asset; const { calcMaxSpend, @@ -83,19 +81,17 @@ export function BtcSendForm() { > Continue - + } > } /> - } - name={btcBalance.asset.name} - symbol={symbol} - /> + } name={info.name} symbol={symbol} /> {currentNetwork.chain.bitcoin.bitcoinNetwork === 'testnet' && ( diff --git a/src/app/pages/send/send-crypto-asset-form/form/stacks-sip10/sip10-token-send-form-container.tsx b/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form-container.tsx similarity index 83% rename from src/app/pages/send/send-crypto-asset-form/form/stacks-sip10/sip10-token-send-form-container.tsx rename to src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form-container.tsx index 643935c0717..fbee8ba9b37 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stacks-sip10/sip10-token-send-form-container.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/sip10/sip10-token-send-form-container.tsx @@ -1,4 +1,5 @@ -import { StacksAssetAvatar } from '@app/components/crypto-assets/stacks/components/stacks-asset-avatar'; +import { StacksAssetAvatar } from '@app/components/stacks-asset-avatar'; +import type { Sip10AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { StxAvatarIcon } from '@app/ui/components/avatar/stx-avatar-icon'; import { AmountField } from '../../components/amount-field'; @@ -9,13 +10,9 @@ import { StacksCommonSendForm } from '../stacks/stacks-common-send-form'; import { useSip10SendForm } from './use-sip10-send-form'; interface Sip10TokenSendFormContainerProps { - symbol: string; - contractId: string; + asset: Sip10AccountCryptoAssetWithDetails; } -export function Sip10TokenSendFormContainer({ - symbol, - contractId, -}: Sip10TokenSendFormContainerProps) { +export function Sip10TokenSendFormContainer({ asset }: Sip10TokenSendFormContainerProps) { const { availableTokenBalance, initialValues, @@ -26,7 +23,8 @@ export function Sip10TokenSendFormContainer({ avatar, marketData, decimals, - } = useSip10SendForm({ symbol, contractId }); + symbol, + } = useSip10SendForm({ asset }); const amountField = ( - {({ contractId, symbol }) => ( - - )} - - ); -} - interface Sip10TokenSendFormLoaderProps { - children(data: { symbol: string; contractId: string }): React.JSX.Element; + children(data: { asset: Sip10AccountCryptoAssetWithDetails }): React.ReactNode; } function Sip10TokenSendFormLoader({ children }: Sip10TokenSendFormLoaderProps) { - const { symbol, contractId } = useParams(); + const { contractId } = useParams(); + const asset = useSip10CryptoAssetWithDetails(contractId ?? ''); const toast = useToast(); - if (!symbol || !contractId) { - toast.error('Symbol or contract id not found'); + if (!asset) { + toast.error('Asset not found'); return ; } - return children({ symbol, contractId }); + return children({ asset }); +} + +export function Sip10TokenSendForm() { + return ( + + {({ asset }) => } + + ); } diff --git a/src/app/pages/send/send-crypto-asset-form/form/stacks-sip10/use-sip10-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx similarity index 64% rename from src/app/pages/send/send-crypto-asset-form/form/stacks-sip10/use-sip10-send-form.tsx rename to src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx index 31d63e80522..41c3b66c600 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stacks-sip10/use-sip10-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/sip10/use-sip10-send-form.tsx @@ -6,11 +6,10 @@ import * as yup from 'yup'; import { logger } from '@shared/logger'; import { StacksSendFormValues } from '@shared/models/form.model'; -import { getImageCanonicalUri } from '@app/common/crypto-assets/stacks-crypto-asset.utils'; import { convertAmountToBaseUnit } from '@app/common/money/calculate-money'; -import { formatContractId } from '@app/common/utils'; +import { getSafeImageCanonicalUri } from '@app/common/stacks-utils'; import { stacksFungibleTokenAmountValidator } from '@app/common/validation/forms/amount-validators'; -import { useStacksFungibleTokenAssetBalance } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; +import type { Sip10AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useFtTokenTransferUnsignedTx, @@ -21,36 +20,31 @@ import { useSendFormNavigate } from '../../hooks/use-send-form-navigate'; import { useStacksCommonSendForm } from '../stacks/use-stacks-common-send-form'; interface UseSip10SendFormArgs { - symbol: string; - contractId: string; + asset: Sip10AccountCryptoAssetWithDetails; } -export function useSip10SendForm({ symbol, contractId }: UseSip10SendFormArgs) { - const assetBalance = useStacksFungibleTokenAssetBalance(contractId); - const generateTx = useGenerateFtTokenTransferUnsignedTx(assetBalance); +export function useSip10SendForm({ asset }: UseSip10SendFormArgs) { + const generateTx = useGenerateFtTokenTransferUnsignedTx(asset.info); const sendFormNavigate = useSendFormNavigate(); - const unsignedTx = useFtTokenTransferUnsignedTx(assetBalance); + const unsignedTx = useFtTokenTransferUnsignedTx(asset.info); const { data: stacksFtFees } = useCalculateStacksTxFees(unsignedTx); - const availableTokenBalance = assetBalance.balance; + const availableTokenBalance = asset.balance.availableBalance; const sendMaxBalance = useMemo( () => convertAmountToBaseUnit(availableTokenBalance), [availableTokenBalance] ); const { initialValues, checkFormValidation, recipient, memo, nonce } = useStacksCommonSendForm({ - symbol, + symbol: asset.info.symbol, availableTokenBalance, }); function createFtAvatar() { - const asset = assetBalance.asset; - - const { contractAddress, contractAssetName, contractName } = asset; return { - avatar: `${formatContractId(contractAddress, contractName)}::${contractAssetName}`, - imageCanonicalUri: getImageCanonicalUri(asset.imageCanonicalUri, asset.name), + avatar: asset.info.contractId, + imageCanonicalUri: getSafeImageCanonicalUri(asset.info.imageCanonicalUri, asset.info.name), }; } @@ -59,9 +53,9 @@ export function useSip10SendForm({ symbol, contractId }: UseSip10SendFormArgs) { initialValues, sendMaxBalance, stacksFtFees, - symbol, - decimals: assetBalance.asset.decimals, - marketData: assetBalance.asset.marketData, + symbol: asset.info.symbol, + decimals: asset.info.decimals, + marketData: asset.marketData, avatar: createFtAvatar(), validationSchema: yup.object({ amount: stacksFungibleTokenAmountValidator(availableTokenBalance), @@ -81,8 +75,8 @@ export function useSip10SendForm({ symbol, contractId }: UseSip10SendFormArgs) { if (!tx) return logger.error('Attempted to generate unsigned tx, but tx is undefined'); sendFormNavigate.toConfirmAndSignStacksSip10Transaction({ - decimals: assetBalance.balance.decimals, - name: assetBalance.asset.name, + decimals: asset.info.decimals, + name: asset.info.name, tx, }); }, diff --git a/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-send-form-confirmation.tsx b/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-send-form-confirmation.tsx index d91454baed6..8e0c9367c0f 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-send-form-confirmation.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stacks/stacks-send-form-confirmation.tsx @@ -57,7 +57,7 @@ export function StacksSendFormConfirmation() { > - + diff --git a/src/app/pages/send/send-crypto-asset-form/form/stacks/use-stacks-common-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stacks/use-stacks-common-send-form.tsx index de916bb9069..0b8eecbe88b 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stacks/use-stacks-common-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stacks/use-stacks-common-send-form.tsx @@ -12,7 +12,7 @@ import { stxMemoValidator } from '@app/common/validation/forms/memo-validators'; import { stxRecipientValidator } from '@app/common/validation/forms/recipient-validators'; import { nonceValidator } from '@app/common/validation/nonce-validators'; import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; -import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; import { useSendFormRouteState } from '../../hooks/use-send-form-route-state'; @@ -29,7 +29,7 @@ export function useStacksCommonSendForm({ }: UseStacksCommonSendFormArgs) { const routeState = useSendFormRouteState(); const { data: nextNonce } = useNextNonce(); - const currentAccountStxAddress = useCurrentAccountStxAddressState(); + const currentAccountStxAddress = useCurrentStacksAccountAddress(); const currentNetwork = useCurrentNetworkState(); const initialValues: StacksSendFormValues = createDefaultInitialFormValues({ 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 f663c70878a..c13dfedc82a 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 @@ -16,7 +16,7 @@ export function StxSendForm() { const stxMarketData = useCryptoCurrencyMarketDataMeanAverage(symbol); const { - availableStxBalance, + availableUnlockedBalance, initialValues, previewTransaction, sendMaxBalance, @@ -27,10 +27,13 @@ export function StxSendForm() { const amountField = ( } bottomInputOverlay={ - + } autoComplete="off" /> @@ -51,7 +54,7 @@ export function StxSendForm() { // FIXME 4370 - need to fix this as fee is actually NumberSchema; in FeeValidatorFactoryArgs // this needs to be the STX fee so it can be validated against HIGH_FEE_AMOUNT_STX fee={fee as unknown as string} - availableTokenBalance={availableStxBalance} + availableTokenBalance={availableUnlockedBalance} /> ); } diff --git a/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx b/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx index 46ecaacaa77..d69d206590b 100644 --- a/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx +++ b/src/app/pages/send/send-crypto-asset-form/form/stx/use-stx-send-form.tsx @@ -14,7 +14,7 @@ import { } from '@app/common/validation/forms/amount-validators'; import { stxFeeValidator } from '@app/common/validation/forms/fee-validators'; import { useUpdatePersistedSendFormValues } from '@app/features/popup-send-form-restoration/use-update-persisted-send-form-values'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useStacksValidateFeeByNonce } from '@app/query/stacks/mempool/mempool.hooks'; import { @@ -32,7 +32,7 @@ export function useStxSendForm() { const { onFormStateChange } = useUpdatePersistedSendFormValues(); const sendFormNavigate = useSendFormNavigate(); const { changeFeeByNonce } = useStacksValidateFeeByNonce(); - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); + const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); const sendMaxBalance = useMemo( () => diff --git a/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx b/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx index d920c347341..4fdc961f637 100644 --- a/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx +++ b/src/app/pages/send/send-crypto-asset-form/send-crypto-asset-form.routes.tsx @@ -8,7 +8,7 @@ import { FullPageWithHeaderLoadingSpinner } from '@app/components/loading-spinne import { EditNonceDialog } from '@app/features/dialogs/edit-nonce-dialog/edit-nonce-dialog'; import { ledgerBitcoinTxSigningRoutes } from '@app/features/ledger/flows/bitcoin-tx-signing/ledger-bitcoin-sign-tx-container'; import { ledgerStacksTxSigningRoutes } from '@app/features/ledger/flows/stacks-tx-signing/ledger-sign-stacks-tx-container'; -import { SendBtcDisabled } from '@app/features/selectable-asset-list/send-btc-disabled'; +import { SendBtcDisabled } from '@app/pages/send/choose-crypto-asset/send-btc-disabled'; import { AccountGate } from '@app/routes/account-gate'; import { Page } from '@app/ui/layout/page/page.layout'; @@ -19,13 +19,13 @@ import { BtcSentSummary } from '../sent-summary/btc-sent-summary'; import { StxSentSummary } from '../sent-summary/stx-sent-summary'; import { RecipientAccountsDialog } from './components/recipient-accounts-dialog/recipient-accounts-dialog'; import { SendBitcoinAssetContainer } from './family/bitcoin/components/send-bitcoin-asset-container'; -import { Brc20SendForm } from './form/brc-20/brc20-send-form'; -import { Brc20SendFormConfirmation } from './form/brc-20/brc20-send-form-confirmation'; -import { BrcChooseFee } from './form/brc-20/brc-20-choose-fee'; +import { BrcChooseFee } from './form/brc20/brc20-choose-fee'; +import { Brc20SendForm } from './form/brc20/brc20-send-form'; +import { Brc20SendFormConfirmation } from './form/brc20/brc20-send-form-confirmation'; import { BtcChooseFee } from './form/btc/btc-choose-fee'; import { BtcSendForm } from './form/btc/btc-send-form'; import { BtcSendFormConfirmation } from './form/btc/btc-send-form-confirmation'; -import { Sip10TokenSendForm } from './form/stacks-sip10/sip10-token-send-form'; +import { Sip10TokenSendForm } from './form/sip10/sip10-token-send-form'; import { StacksSendFormConfirmation } from './form/stacks/stacks-send-form-confirmation'; import { StxSendForm } from './form/stx/stx-send-form'; diff --git a/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx b/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx index 5ae67e717e5..83ccef5e333 100644 --- a/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx +++ b/src/app/pages/swap/components/swap-asset-dialog/components/swap-asset-item.tsx @@ -3,8 +3,8 @@ import { SwapSelectors } from '@tests/selectors/swap.selectors'; import { convertAssetBalanceToFiat } from '@app/common/asset-utils'; import { formatMoneyWithoutSymbol } from '@app/common/money/format-money'; import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query'; -import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils'; +import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query'; +import { isFtAsset } from '@app/query/stacks/token-metadata/token-metadata.utils'; import { Avatar, defaultFallbackDelay, getAvatarFallback } from '@app/ui/components/avatar/avatar'; import { ItemLayout } from '@app/ui/components/item-layout/item-layout'; import { Pressable } from '@app/ui/pressable/pressable'; @@ -19,7 +19,7 @@ export function SwapAssetItem({ asset, onClick }: SwapAssetItemProps) { const ftMetadataName = ftMetadata && isFtAsset(ftMetadata) ? ftMetadata.name : asset.name; const displayName = asset.displayName ?? ftMetadataName; const fallback = getAvatarFallback(asset.name); - const balanceAsFiat = convertAssetBalanceToFiat(asset); + const fiatBalance = convertAssetBalanceToFiat(asset); return ( @@ -33,7 +33,7 @@ export function SwapAssetItem({ asset, onClick }: SwapAssetItemProps) { titleLeft={displayName} captionLeft={asset.name} titleRight={formatMoneyWithoutSymbol(asset.balance)} - captionRight={balanceAsFiat} + captionRight={fiatBalance} /> ); diff --git a/src/app/pages/transaction-request/transaction-request.tsx b/src/app/pages/transaction-request/transaction-request.tsx index 82a08a0a414..356818bec10 100644 --- a/src/app/pages/transaction-request/transaction-request.tsx +++ b/src/app/pages/transaction-request/transaction-request.tsx @@ -29,7 +29,7 @@ import { PostConditions } from '@app/features/stacks-transaction-request/post-co import { StxTransferDetails } from '@app/features/stacks-transaction-request/stx-transfer-details/stx-transfer-details'; import { SubmitAction } from '@app/features/stacks-transaction-request/submit-action'; import { TransactionError } from '@app/features/stacks-transaction-request/transaction-error/transaction-error'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; +import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; import { useCalculateStacksTxFees } from '@app/query/stacks/fees/fees.hooks'; import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useTransactionRequestState } from '@app/store/transactions/requests.hooks'; @@ -47,7 +47,7 @@ function TransactionRequestBase() { const { data: stxFees } = useCalculateStacksTxFees(unsignedTx.transaction); const analytics = useAnalytics(); const generateUnsignedTx = useGenerateUnsignedStacksTransaction(); - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); + const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); const { data: nextNonce } = useNextNonce(); const navigate = useNavigate(); const { stacksBroadcastTransaction } = useStacksBroadcastTransaction('STX'); 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 ca2c0831fd9..84f43815ec3 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 @@ -8,7 +8,7 @@ import { useCurrentAccountNativeSegwitIndexZeroSigner } from '@app/store/account import { useGetBitcoinBalanceByAddress } from './btc-balance.hooks'; -function makeBtcCryptoAssetBalance(balance: Money): BtcCryptoAssetBalance { +function createBtcCryptoAssetBalance(balance: Money): BtcCryptoAssetBalance { return { availableBalance: balance, // TODO: Asset refactor: can we determine these here or are they nec? @@ -21,7 +21,7 @@ function makeBtcCryptoAssetBalance(balance: Money): BtcCryptoAssetBalance { export function useBtcCryptoAssetBalanceNativeSegwit(address: string) { const { balance, isInitialLoading, isLoading, isFetching } = useGetBitcoinBalanceByAddress(address); - const btcCryptoAssetBalance = useMemo(() => makeBtcCryptoAssetBalance(balance), [balance]); + const btcCryptoAssetBalance = useMemo(() => createBtcCryptoAssetBalance(balance), [balance]); return { btcCryptoAssetBalance, diff --git a/src/app/query/bitcoin/bitcoin-client.ts b/src/app/query/bitcoin/bitcoin-client.ts index dc729f086f5..8b9f127bed5 100644 --- a/src/app/query/bitcoin/bitcoin-client.ts +++ b/src/app/query/bitcoin/bitcoin-client.ts @@ -6,7 +6,6 @@ import { BESTINSLOT_API_BASE_URL_TESTNET, type BitcoinNetworkModes, } from '@shared/constants'; -import type { MarketData } from '@shared/models/market.model'; import type { Money } from '@shared/models/money.model'; import type { BitcoinTx } from '@shared/models/transactions/bitcoin-transaction.model'; @@ -98,13 +97,6 @@ interface Brc20WalletBalancesResponse { data: Brc20Balance[]; } -export interface Brc20Token { - balance: Money | null; - holderAddress: string; - marketData: MarketData | null; - tokenData: Brc20Balance & Brc20TickerInfo; -} - /* RUNES */ export interface RuneBalance { pkscript: string; diff --git a/src/app/query/bitcoin/btc/btc-crypto-asset.hooks.ts b/src/app/query/bitcoin/btc/btc-crypto-asset.hooks.ts new file mode 100644 index 00000000000..52cc752989d --- /dev/null +++ b/src/app/query/bitcoin/btc/btc-crypto-asset.hooks.ts @@ -0,0 +1,48 @@ +import type { BtcCryptoAssetInfo } from '@leather-wallet/models'; + +import { BTC_DECIMALS } from '@shared/constants'; +import { createMoney } from '@shared/models/money.model'; + +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; +import { createAccountCryptoAssetWithDetailsFactory } from '@app/query/models/crypto-asset.model'; + +import type { BtcAccountCryptoAssetWithDetails } from '../../models/crypto-asset.model'; +import { useBtcCryptoAssetBalanceNativeSegwit } from '../balance/btc-balance-native-segwit.hooks'; + +const btcCryptoAssetInfo: BtcCryptoAssetInfo = { + decimals: BTC_DECIMALS, + hasMemo: false, + name: 'bitcoin', + symbol: 'BTC', +}; + +const btcCryptoAssetBalancePlaceholder = { + availableBalance: createMoney(0, 'BTC'), + protectedBalance: createMoney(0, 'BTC'), + uneconomicalBalance: createMoney(0, 'BTC'), +}; + +export const btcCryptoAssetPlaceholder = + createAccountCryptoAssetWithDetailsFactory({ + balance: btcCryptoAssetBalancePlaceholder, + chain: 'bitcoin', + info: btcCryptoAssetInfo, + marketData: null, + type: 'btc', + }); + +export function useBtcAccountCryptoAssetWithDetails(address: string) { + const { btcCryptoAssetBalance, isInitialLoading } = useBtcCryptoAssetBalanceNativeSegwit(address); + const marketData = useCryptoCurrencyMarketDataMeanAverage('BTC'); + + return { + asset: createAccountCryptoAssetWithDetailsFactory({ + balance: btcCryptoAssetBalance, + chain: 'bitcoin', + info: btcCryptoAssetInfo, + marketData, + type: 'btc', + }), + isInitialLoading, + }; +} 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 6e7bd527808..d0dff8e26b9 100644 --- a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts +++ b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.hooks.ts @@ -1,18 +1,21 @@ import BigNumber from 'bignumber.js'; import { createMarketData, createMarketPair } from '@shared/models/market.model'; -import { type Money, createMoney } from '@shared/models/money.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 { useConfigOrdinalsbot } from '@app/query/common/remote-config/remote-config.query'; +import { + type Brc20AccountCryptoAssetWithDetails, + createAccountCryptoAssetWithDetailsFactory, +} from '@app/query/models/crypto-asset.model'; import { useAppDispatch } from '@app/store'; import { useCurrentAccountIndex } from '@app/store/accounts/account'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; import { brc20TransferInitiated } from '@app/store/ordinals/ordinals.slice'; -import type { Brc20Token } from '../../bitcoin-client'; import { useAverageBitcoinFeeRates } from '../../fees/fee-estimates.hooks'; import { useOrdinalsbotClient } from '../../ordinalsbot-client'; import { @@ -85,26 +88,7 @@ export function useBrc20Transfers(holderAddress: string) { }; } -function makeBrc20Token(priceAsFiat: Money, token: Brc20Token) { - return { - ...token, - balance: createMoney( - unitToFractionalUnit(token.tokenData.decimals)( - new BigNumber(token.tokenData.overall_balance) - ), - token.tokenData.ticker, - token.tokenData.decimals - ), - marketData: token.tokenData.min_listed_unit_price - ? createMarketData( - createMarketPair(token.tokenData.ticker, 'USD'), - createMoney(priceAsFiat.amount, 'USD') - ) - : null, - }; -} - -export function useBrc20Tokens() { +export function useBrc20AccountCryptoAssetsWithDetails() { const calculateBitcoinFiatValue = useCalculateBitcoinFiatValue(); const { data: allBrc20TokensResponse } = useGetBrc20TokensQuery(); @@ -116,9 +100,32 @@ export function useBrc20Tokens() { return ( tokens?.map(token => { const priceAsFiat = calculateBitcoinFiatValue( - createMoney(new BigNumber(token.tokenData.min_listed_unit_price ?? 0), 'BTC') + createMoney(new BigNumber(token.balance.min_listed_unit_price ?? 0), 'BTC') ); - return makeBrc20Token(priceAsFiat, token); + return createAccountCryptoAssetWithDetailsFactory({ + balance: { + availableBalance: createMoney( + unitToFractionalUnit(token.info.decimals)(new BigNumber(token.balance.overall_balance)), + token.balance.ticker, + token.info.decimals + ), + }, + chain: 'bitcoin', + holderAddress: token.holderAddress, + info: { + decimals: token.info.decimals, + hasMemo: false, + name: 'brc-20', + symbol: token.info.ticker, + }, + marketData: token.balance.min_listed_unit_price + ? createMarketData( + createMarketPair(token.balance.ticker, 'USD'), + createMoney(priceAsFiat.amount, 'USD') + ) + : null, + type: 'brc-20', + }); }) ?? [] ); } diff --git a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts index 6440524a788..36d685caac1 100644 --- a/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts +++ b/src/app/query/bitcoin/ordinals/brc20/brc20-tokens.query.ts @@ -10,8 +10,6 @@ import { useCurrentAccountTaprootSigner } from '@app/store/accounts/blockchain/b import { useBitcoinClient } from '@app/store/common/api-clients.hooks'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; -import { Brc20Token } from '../../bitcoin-client'; - const addressesSimultaneousFetchLimit = 3; const stopSearchAfterNumberAddressesWithoutBrc20Tokens = 3; @@ -59,18 +57,16 @@ export function useGetBrc20TokensQuery() { }) ); - // Initialize token with token data return brc20Tokens.data.map((token, index) => { return { - balance: null, + balance: token, holderAddress: address, - marketData: null, - tokenData: { ...token, ...tickerPromises[index].data }, + info: tickerPromises[index].data, }; }); }); - const brc20Tokens: Brc20Token[][] = await Promise.all(brc20TokensPromises); + const brc20Tokens = await Promise.all(brc20TokensPromises); addressesWithoutTokens += brc20Tokens.filter(tokens => tokens.length === 0).length; return { diff --git a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts index a47479511de..6d3b95fcb92 100644 --- a/src/app/query/common/alex-sdk/alex-sdk.hooks.ts +++ b/src/app/query/common/alex-sdk/alex-sdk.hooks.ts @@ -11,9 +11,9 @@ import { isDefined } from '@shared/utils'; import { sortAssetsByName } from '@app/common/asset-utils'; import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money'; import { pullContractIdFromIdentity } from '@app/common/utils'; -import { useTransferableStacksFungibleTokenAssetBalances } from '@app/query/stacks/balance/stacks-ft-balances.hooks'; -import { useCurrentStcAvailableUnlockedBalance } from '@app/query/stacks/balance/stx-balance.hooks'; -import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStxAvailableUnlockedBalance } from '@app/query/stacks/balance/account-balance.hooks'; +import { useTransferableSip10CryptoAssetsWithDetails } from '@app/query/stacks/sip10/sip10-tokens.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { getAvatarFallback } from '@app/ui/components/avatar/avatar'; import { useAlexSdkLatestPricesQuery } from './alex-sdk-latest-prices.query'; @@ -51,14 +51,12 @@ export function useAlexCurrencyPriceAsMarketData() { ); } -function useMakeSwapAsset() { - const account = useCurrentStacksAccount(); +function useCreateSwapAsset() { + const address = useCurrentStacksAccountAddress(); const { data: prices } = useAlexSdkLatestPricesQuery(); const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); - const availableUnlockedBalance = useCurrentStcAvailableUnlockedBalance(); - const stacksFtAssetBalances = useTransferableStacksFungibleTokenAssetBalances( - account?.address ?? '' - ); + const availableUnlockedBalance = useCurrentStxAvailableUnlockedBalance(); + const assets = useTransferableSip10CryptoAssetsWithDetails(address); return useCallback( (tokenInfo?: TokenInfo): SwapAsset | undefined => { @@ -69,17 +67,16 @@ function useMakeSwapAsset() { } const currency = tokenInfo.id as Currency; - const fungibleTokenBalance = stacksFtAssetBalances.find( - balance => tokenInfo.contractAddress === balance.asset.contractId - )?.balance; const principal = pullContractIdFromIdentity(tokenInfo.contractAddress); + const availableBalance = assets.find(a => a.info.contractId === principal)?.balance + .availableBalance; const swapAsset = { currency, fallback: getAvatarFallback(tokenInfo.name), icon: tokenInfo.icon, name: tokenInfo.name, - principal: pullContractIdFromIdentity(tokenInfo.contractAddress), + principal, }; if (currency === Currency.STX) { @@ -93,19 +90,19 @@ function useMakeSwapAsset() { return { ...swapAsset, - balance: fungibleTokenBalance ?? createMoney(0, tokenInfo.name, tokenInfo.decimals), - marketData: fungibleTokenBalance - ? priceAsMarketData(principal, fungibleTokenBalance.symbol) + balance: availableBalance ?? createMoney(0, tokenInfo.name, tokenInfo.decimals), + marketData: availableBalance + ? priceAsMarketData(principal, availableBalance.symbol) : priceAsMarketData(principal, tokenInfo.name), }; }, - [availableUnlockedBalance, priceAsMarketData, prices, stacksFtAssetBalances] + [assets, availableUnlockedBalance, priceAsMarketData, prices] ); } export function useAlexSwappableAssets() { - const makeSwapAsset = useMakeSwapAsset(); + const createSwapAsset = useCreateSwapAsset(); return useAlexSdkSwappableCurrencyQuery({ - select: resp => sortAssetsByName(resp.map(makeSwapAsset).filter(isDefined)), + select: resp => sortAssetsByName(resp.map(createSwapAsset).filter(isDefined)), }); } diff --git a/src/app/query/models/crypto-asset.model.ts b/src/app/query/models/crypto-asset.model.ts new file mode 100644 index 00000000000..ac2ecaa1389 --- /dev/null +++ b/src/app/query/models/crypto-asset.model.ts @@ -0,0 +1,59 @@ +import type { + BaseCryptoAssetInfo, + Blockchains, + Brc20CryptoAssetInfo, + BtcCryptoAssetBalance, + BtcCryptoAssetInfo, + CryptoAssetBalance, + CryptoAssetType, + MarketData, + Sip10CryptoAssetInfo, + StxCryptoAssetBalance, + StxCryptoAssetInfo, +} from '@leather-wallet/models'; + +interface AccountCryptoAssetDetails { + balance: CryptoAssetBalance; + chain: Blockchains; + info: BaseCryptoAssetInfo; + marketData: MarketData | null; + type: CryptoAssetType; +} + +export function createAccountCryptoAssetWithDetailsFactory( + args: T +): T { + const { balance, chain, info, marketData, type } = args; + return { + balance, + chain, + info, + marketData, + type, + } as T; +} + +export interface BtcAccountCryptoAssetWithDetails extends AccountCryptoAssetDetails { + balance: BtcCryptoAssetBalance; + info: BtcCryptoAssetInfo; +} + +export interface StxAccountCryptoAssetWithDetails extends AccountCryptoAssetDetails { + balance: StxCryptoAssetBalance; + info: StxCryptoAssetInfo; +} + +export interface Brc20AccountCryptoAssetWithDetails extends AccountCryptoAssetDetails { + holderAddress: string; + info: Brc20CryptoAssetInfo; +} + +export interface Sip10AccountCryptoAssetWithDetails extends AccountCryptoAssetDetails { + info: Sip10CryptoAssetInfo; +} + +export type AccountCryptoAssetWithDetails = + | BtcAccountCryptoAssetWithDetails + | StxAccountCryptoAssetWithDetails + | Brc20AccountCryptoAssetWithDetails + | Sip10AccountCryptoAssetWithDetails; diff --git a/src/app/query/stacks/balance/stx-balance.hooks.ts b/src/app/query/stacks/balance/account-balance.hooks.ts similarity index 67% rename from src/app/query/stacks/balance/stx-balance.hooks.ts rename to src/app/query/stacks/balance/account-balance.hooks.ts index ec382710468..0c85fa37011 100644 --- a/src/app/query/stacks/balance/stx-balance.hooks.ts +++ b/src/app/query/stacks/balance/account-balance.hooks.ts @@ -5,22 +5,22 @@ import { AccountBalanceStxKeys, type AddressBalanceResponse } from '@shared/mode import { Money, createMoney } from '@shared/models/money.model'; import { subtractMoney, sumMoney } from '@app/common/money/calculate-money'; -import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { accountBalanceStxKeys } from '@app/store/accounts/blockchain/stacks/stacks-account.models'; import { useMempoolTxsInboundBalance, useMempoolTxsOutboundBalance, } from '../mempool/mempool.hooks'; -import { useStacksAccountBalanceQuery } from './stx-balance.query'; +import { useStacksAccountBalanceQuery } from './account-balance.query'; -function makeStxMoney(resp: AddressBalanceResponse) { +function createStxMoney(resp: AddressBalanceResponse) { return Object.fromEntries( accountBalanceStxKeys.map(key => [key, { amount: new BigNumber(resp.stx[key]), symbol: 'STX' }]) ) as Record; } -function makeStxCryptoAssetBalance( +function createStxCryptoAssetBalance( stxMoney: Record, inboundBalance: Money, outboundBalance: Money @@ -29,9 +29,6 @@ function makeStxCryptoAssetBalance( const unlockedBalance = subtractMoney(stxMoney.balance, stxMoney.locked); return { - // TODO: Asset refactor: are inbound/outbound necessary? - // - Make sure to track changes to effectiveBalance, now availableBalance - // - And, previous availableBalance is now availableUnlockedBalance availableBalance: subtractMoney(totalBalance, outboundBalance), availableUnlockedBalance: subtractMoney(unlockedBalance, outboundBalance), inboundBalance, @@ -47,13 +44,18 @@ export function useStxCryptoAssetBalance(address: string) { const inboundBalance = useMempoolTxsInboundBalance(address); const outboundBalance = useMempoolTxsOutboundBalance(address); return useStacksAccountBalanceQuery(address, { - select: resp => makeStxCryptoAssetBalance(makeStxMoney(resp), inboundBalance, outboundBalance), + select: resp => + createStxCryptoAssetBalance(createStxMoney(resp), inboundBalance, outboundBalance), }); } -export function useCurrentStcAvailableUnlockedBalance() { - const account = useCurrentStacksAccount(); - return ( - useStxCryptoAssetBalance(account?.address ?? '').data?.unlockedBalance ?? createMoney(0, 'STX') - ); +export function useCurrentStxAvailableUnlockedBalance() { + const address = useCurrentStacksAccountAddress(); + return useStxCryptoAssetBalance(address).data?.unlockedBalance ?? createMoney(0, 'STX'); +} + +export function useStacksAccountBalanceFungibleTokens(address: string) { + return useStacksAccountBalanceQuery(address, { + select: resp => resp.fungible_tokens, + }); } diff --git a/src/app/query/stacks/balance/stx-balance.query.ts b/src/app/query/stacks/balance/account-balance.query.ts similarity index 100% rename from src/app/query/stacks/balance/stx-balance.query.ts rename to src/app/query/stacks/balance/account-balance.query.ts diff --git a/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts b/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts deleted file mode 100644 index 93f44728262..00000000000 --- a/src/app/query/stacks/balance/stacks-ft-balances.hooks.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { useMemo } from 'react'; -import { useNavigate } from 'react-router-dom'; - -import BigNumber from 'bignumber.js'; - -import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; - -import { formatContractId } from '@app/common/utils'; -import { useToast } from '@app/features/toasts/use-toast'; -import { - type SwapAsset, - useAlexCurrencyPriceAsMarketData, - useAlexSwappableAssets, -} from '@app/query/common/alex-sdk/alex-sdk.hooks'; -import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; - -import { useGetFungibleTokenMetadataListQuery } from '../tokens/fungible-tokens/fungible-token-metadata.query'; -import { isFtAsset } from '../tokens/token-metadata.utils'; -import { - addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance, - convertFtBalancesToStacksFungibleTokenAssetBalanceType, - createStacksFtCryptoAssetBalanceTypeWrapper, -} from './stacks-ft-balances.utils'; -import { useStacksAccountBalanceQuery } from './stx-balance.query'; - -function useStacksFungibleTokenAssetBalances(address: string) { - return useStacksAccountBalanceQuery(address, { - select: resp => convertFtBalancesToStacksFungibleTokenAssetBalanceType(resp.fungible_tokens), - }); -} - -function useStacksFungibleTokenAssetBalancesWithMetadata(address: string) { - const { data: initializedAssetBalances = [] } = useStacksFungibleTokenAssetBalances(address); - const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); - - const ftAssetsMetadata = useGetFungibleTokenMetadataListQuery( - initializedAssetBalances.map(assetBalance => - formatContractId(assetBalance.asset.contractAddress, assetBalance.asset.contractName) - ) - ); - - return useMemo( - () => - initializedAssetBalances.map((assetBalance, i) => { - const metadata = ftAssetsMetadata[i].data; - if (!(metadata && isFtAsset(metadata))) return assetBalance; - return addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance( - assetBalance, - metadata, - priceAsMarketData( - formatContractId(assetBalance.asset.contractAddress, assetBalance.asset.contractName), - metadata.symbol - ) - ); - }), - // eslint-disable-next-line react-hooks/exhaustive-deps - [initializedAssetBalances] - ); -} - -export function useStacksFungibleTokenAssetBalance(contractId: string) { - const toast = useToast(); - const account = useCurrentStacksAccount(); - const navigate = useNavigate(); - const assetBalances = useStacksFungibleTokenAssetBalancesWithMetadata(account?.address ?? ''); - - return useMemo(() => { - const balance = assetBalances.find(assetBalance => - assetBalance.asset.contractId.includes(contractId) - ); - if (!balance) { - toast.error('Unable to find balance by contract id'); - navigate('..'); - } - return balance ?? createStacksFtCryptoAssetBalanceTypeWrapper(new BigNumber(0), contractId); - }, [assetBalances, contractId, navigate, toast]); -} - -export function useTransferableStacksFungibleTokenAssetBalances( - address: string -): StacksFungibleTokenAssetBalance[] { - const assetBalances = useStacksFungibleTokenAssetBalancesWithMetadata(address); - return useMemo( - () => assetBalances.filter(assetBalance => assetBalance.asset.canTransfer), - [assetBalances] - ); -} - -function filterStacksFungibleTokens( - assetBalances: StacksFungibleTokenAssetBalance[], - swapAssets: SwapAsset[], - filter: StacksFtTokensFilter -) { - if (filter === 'supported') { - return assetBalances.filter(assetBalance => - swapAssets.some(swapAsset => swapAsset.principal.includes(assetBalance.asset.contractAddress)) - ); - } - - if (filter === 'unsupported') { - return assetBalances.filter( - assetBalance => - !swapAssets.some(swapAsset => - swapAsset.principal.includes(assetBalance.asset.contractAddress) - ) - ); - } - - return assetBalances; -} - -/** - * @see https://github.com/leather-wallet/issues/issues/16 - */ -type StacksFtTokensFilter = 'all' | 'supported' | 'unsupported'; - -interface useFilteredStacksFungibleTokenListArgs { - address: string; - filter?: StacksFtTokensFilter; -} -export function useFilteredStacksFungibleTokenList({ - address, - filter = 'all', -}: useFilteredStacksFungibleTokenListArgs) { - const stacksFtAssetBalances = useStacksFungibleTokenAssetBalancesWithMetadata(address); - const { data: swapAssets = [] } = useAlexSwappableAssets(); - - return useMemo(() => { - return filterStacksFungibleTokens(stacksFtAssetBalances, swapAssets, filter); - }, [stacksFtAssetBalances, swapAssets, filter]); -} diff --git a/src/app/query/stacks/balance/stacks-ft-balances.utils.ts b/src/app/query/stacks/balance/stacks-ft-balances.utils.ts deleted file mode 100644 index 6d88699c766..00000000000 --- a/src/app/query/stacks/balance/stacks-ft-balances.utils.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { FtMetadataResponse } from '@hirosystems/token-metadata-api-client'; -import BigNumber from 'bignumber.js'; - -import { STX_DECIMALS } from '@shared/constants'; -import type { AccountBalanceResponseBigNumber } from '@shared/models/account.model'; -import type { - StacksCryptoCurrencyAssetBalance, - StacksFungibleTokenAssetBalance, -} from '@shared/models/crypto-asset-balance.model'; -import { type MarketData } from '@shared/models/market.model'; -import { createMoney } from '@shared/models/money.model'; - -import { isTransferableStacksFungibleTokenAsset } from '@app/common/crypto-assets/stacks-crypto-asset.utils'; -import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; - -export function createStacksCryptoCurrencyAssetTypeWrapper( - balance: BigNumber -): StacksCryptoCurrencyAssetBalance { - return { - blockchain: 'stacks', - type: 'crypto-currency', - balance: createMoney(balance, 'STX'), - asset: { - decimals: STX_DECIMALS, - hasMemo: true, - name: 'Stacks', - symbol: 'STX', - }, - }; -} - -export function createStacksFtCryptoAssetBalanceTypeWrapper( - balance: BigNumber, - contractId: string -): StacksFungibleTokenAssetBalance { - const { address, contractName, assetName } = getAssetStringParts(contractId); - return { - blockchain: 'stacks', - type: 'fungible-token', - balance: createMoney(balance, '', 0), - asset: { - contractId, - canTransfer: false, - contractAddress: address, - contractAssetName: assetName, - contractName, - decimals: 0, - hasMemo: false, - imageCanonicalUri: '', - name: '', - marketData: null, - symbol: '', - }, - }; -} - -export function convertFtBalancesToStacksFungibleTokenAssetBalanceType( - ftBalances: AccountBalanceResponseBigNumber['fungible_tokens'] -) { - return ( - Object.entries(ftBalances) - .map(([key, value]) => { - const balance = new BigNumber(value.balance); - return createStacksFtCryptoAssetBalanceTypeWrapper(balance, key); - }) - // Assets users have traded will persist in the api response - .filter(assetBalance => !assetBalance?.balance.amount.isEqualTo(0)) - ); -} - -export function addQueriedMetadataToInitializedStacksFungibleTokenAssetBalance( - assetBalance: StacksFungibleTokenAssetBalance, - metadata: FtMetadataResponse, - marketData: MarketData | null -) { - return { - ...assetBalance, - balance: createMoney( - assetBalance.balance.amount, - metadata.symbol ?? '', - metadata.decimals ?? 0 - ), - asset: { - ...assetBalance.asset, - canTransfer: isTransferableStacksFungibleTokenAsset(assetBalance.asset), - decimals: metadata.decimals ?? 0, - hasMemo: isTransferableStacksFungibleTokenAsset(assetBalance.asset), - imageCanonicalUri: metadata.image_canonical_uri ?? '', - name: metadata.name ?? '', - marketData, - symbol: metadata.symbol ?? '', - }, - }; -} - -export function getStacksFungibleTokenCurrencyAssetBalance( - selectedAssetBalance?: StacksCryptoCurrencyAssetBalance | StacksFungibleTokenAssetBalance -) { - return selectedAssetBalance?.type === 'fungible-token' && selectedAssetBalance.asset.canTransfer - ? selectedAssetBalance - : undefined; -} diff --git a/src/app/query/stacks/bns/bns.hooks.ts b/src/app/query/stacks/bns/bns.hooks.ts index d0aea0e3fc6..1e428ee51cf 100644 --- a/src/app/query/stacks/bns/bns.hooks.ts +++ b/src/app/query/stacks/bns/bns.hooks.ts @@ -1,11 +1,11 @@ import { logger } from '@shared/logger'; -import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useGetBnsNamesOwnedByAddress } from './bns.query'; export function useCurrentAccountNames() { - const principal = useCurrentAccountStxAddressState(); + const principal = useCurrentStacksAccountAddress(); return useGetBnsNamesOwnedByAddress(principal, { select: resp => { if (principal === '') logger.error('No principal defined'); diff --git a/src/app/query/stacks/mempool/mempool.hooks.ts b/src/app/query/stacks/mempool/mempool.hooks.ts index 5e28aaf8138..947e9050cbb 100644 --- a/src/app/query/stacks/mempool/mempool.hooks.ts +++ b/src/app/query/stacks/mempool/mempool.hooks.ts @@ -1,21 +1,16 @@ import { useMemo } from 'react'; -import { - MempoolTokenTransferTransaction, - MempoolTransaction, -} from '@stacks/stacks-blockchain-api-types'; -import BigNumber from 'bignumber.js'; - -import { createMoney } from '@shared/models/money.model'; +import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; import { increaseValueByOneMicroStx } from '@app/common/math/helpers'; import { microStxToStx } from '@app/common/money/unit-conversion'; import { useTransactionsById } from '@app/query/stacks/transactions/transactions-by-id.query'; -import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useStacksConfirmedTransactions } from '../transactions/transactions-with-transfers.hooks'; import { useAccountMempoolQuery } from './mempool.query'; +import { calculatePendingTxsMoneyBalance } from './mempool.utils'; const droppedCache = new Map(); @@ -50,7 +45,7 @@ function useAccountMempoolTransactions(address: string) { } export function useStacksPendingTransactions() { - const address = useCurrentAccountStxAddressState(); + const address = useCurrentStacksAccountAddress(); const { query, transactions } = useAccountMempoolTransactions(address ?? ''); return useMemo(() => { const nonEmptyTransactions = transactions.filter(tx => !!tx) as MempoolTransaction[]; @@ -59,55 +54,32 @@ export function useStacksPendingTransactions() { } export function useCurrentAccountMempool() { - const address = useCurrentAccountStxAddressState(); + const address = useCurrentStacksAccountAddress(); return useAccountMempoolQuery(address ?? ''); } -// TODO: Asset refactor: combine inbound/outbound into one function? export function useMempoolTxsInboundBalance(address: string) { const { transactions: pendingTransactions } = useStacksPendingTransactions(); const confirmedTxs = useStacksConfirmedTransactions(); - const pendingInboundTxs = pendingTransactions.filter(tx => { - if (confirmedTxs.some(confirmedTx => confirmedTx.nonce === tx.nonce)) { - return false; - } - return tx.tx_type === 'token_transfer' && tx.token_transfer.recipient_address === address; - }) as unknown as MempoolTokenTransferTransaction[]; - - const tokenTransferTxsBalance = pendingInboundTxs.reduce( - (acc, tx) => acc.plus(tx.token_transfer.amount), - new BigNumber(0) - ); - const pendingTxsFeesBalance = pendingInboundTxs.reduce( - (acc, tx) => acc.plus(tx.fee_rate), - new BigNumber(0) - ); - - return createMoney(tokenTransferTxsBalance.plus(pendingTxsFeesBalance), 'STX'); + return calculatePendingTxsMoneyBalance({ + address, + confirmedTxs, + pendingTxs: pendingTransactions, + type: 'inbound', + }); } export function useMempoolTxsOutboundBalance(address: string) { const { transactions: pendingTransactions } = useStacksPendingTransactions(); const confirmedTxs = useStacksConfirmedTransactions(); - const pendingOutboundTxs = pendingTransactions.filter(tx => { - if (confirmedTxs.some(confirmedTx => confirmedTx.nonce === tx.nonce)) { - return false; - } - return tx.tx_type === 'token_transfer' && tx.sender_address === address; - }) as unknown as MempoolTokenTransferTransaction[]; - - const tokenTransferTxsBalance = pendingOutboundTxs.reduce( - (acc, tx) => acc.plus(tx.token_transfer.amount), - new BigNumber(0) - ); - const pendingTxsFeesBalance = pendingOutboundTxs.reduce( - (acc, tx) => acc.plus(tx.fee_rate), - new BigNumber(0) - ); - - return createMoney(tokenTransferTxsBalance.plus(pendingTxsFeesBalance), 'STX'); + return calculatePendingTxsMoneyBalance({ + address, + confirmedTxs, + pendingTxs: pendingTransactions, + type: 'outbound', + }); } export function useStacksValidateFeeByNonce() { diff --git a/src/app/query/stacks/mempool/mempool.utils.ts b/src/app/query/stacks/mempool/mempool.utils.ts new file mode 100644 index 00000000000..c2c301a1a95 --- /dev/null +++ b/src/app/query/stacks/mempool/mempool.utils.ts @@ -0,0 +1,62 @@ +import type { + MempoolTokenTransferTransaction, + MempoolTransaction, + Transaction, +} from '@stacks/stacks-blockchain-api-types'; + +import { createMoney } from '@shared/models/money.model'; + +import { sumNumbers } from '@app/common/math/helpers'; + +type PendingTransactionType = 'inbound' | 'outbound'; + +function getInboundPendingTxs( + address: string, + confirmedTxs: Transaction[], + pendingTxs: MempoolTransaction[] +) { + return pendingTxs.filter(tx => { + if (confirmedTxs.some(confirmedTx => confirmedTx.nonce === tx.nonce)) { + return false; + } + return tx.tx_type === 'token_transfer' && tx.token_transfer.recipient_address === address; + }) as unknown as MempoolTokenTransferTransaction[]; +} + +function getOutboundPendingTxs( + address: string, + confirmedTxs: Transaction[], + pendingTxs: MempoolTransaction[] +) { + return pendingTxs.filter(tx => { + if (confirmedTxs.some(confirmedTx => confirmedTx.nonce === tx.nonce)) { + return false; + } + return tx.tx_type === 'token_transfer' && tx.sender_address === address; + }) as unknown as MempoolTokenTransferTransaction[]; +} + +interface CalculatePendingTxsMoneyBalanceArgs { + address: string; + confirmedTxs: Transaction[]; + pendingTxs: MempoolTransaction[]; + type: PendingTransactionType; +} +export function calculatePendingTxsMoneyBalance({ + address, + confirmedTxs, + pendingTxs, + type, +}: CalculatePendingTxsMoneyBalanceArgs) { + const filteredPendingTxs = + type === 'inbound' + ? getInboundPendingTxs(address, confirmedTxs, pendingTxs) + : getOutboundPendingTxs(address, confirmedTxs, pendingTxs); + + const tokenTransferTxsBalance = sumNumbers( + filteredPendingTxs.map(tx => Number(tx.token_transfer.amount)) + ); + const pendingTxsFeesBalance = sumNumbers(filteredPendingTxs.map(tx => Number(tx.fee_rate))); + + return createMoney(tokenTransferTxsBalance.plus(pendingTxsFeesBalance), 'STX'); +} diff --git a/src/app/query/stacks/nonce/account-nonces.query.ts b/src/app/query/stacks/nonce/account-nonces.query.ts index e4ca85d3fd7..a26bb36490b 100644 --- a/src/app/query/stacks/nonce/account-nonces.query.ts +++ b/src/app/query/stacks/nonce/account-nonces.query.ts @@ -2,7 +2,7 @@ import { useQuery } from '@tanstack/react-query'; import PQueue from 'p-queue'; import { AppUseQueryConfig } from '@app/query/query-config'; -import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useStacksClient } from '@app/store/common/api-clients.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; @@ -36,7 +36,7 @@ type FetchAccountNoncesResp = Awaited( options?: AppUseQueryConfig ) { - const principal = useCurrentAccountStxAddressState(); + const principal = useCurrentStacksAccountAddress(); const network = useCurrentNetworkState(); const client = useStacksClient(); const limiter = useHiroApiRateLimiter(); diff --git a/src/app/query/stacks/sip10/sip10-tokens.hooks.ts b/src/app/query/stacks/sip10/sip10-tokens.hooks.ts new file mode 100644 index 00000000000..d50030f12e1 --- /dev/null +++ b/src/app/query/stacks/sip10/sip10-tokens.hooks.ts @@ -0,0 +1,124 @@ +import { useMemo } from 'react'; + +import type { FtMetadataResponse } from '@hirosystems/token-metadata-api-client'; +import type { Sip10CryptoAssetInfo } from '@leather-wallet/models'; +import BigNumber from 'bignumber.js'; + +import { createMoney } from '@shared/models/money.model'; +import { isDefined, isUndefined } from '@shared/utils'; + +import { getTicker, pullContractIdFromIdentity } from '@app/common/utils'; +import { + useAlexCurrencyPriceAsMarketData, + useAlexSwappableAssets, +} from '@app/query/common/alex-sdk/alex-sdk.hooks'; +import { + type AccountCryptoAssetWithDetails, + type Sip10AccountCryptoAssetWithDetails, + createAccountCryptoAssetWithDetailsFactory, +} from '@app/query/models/crypto-asset.model'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; + +import { useStacksAccountBalanceFungibleTokens } from '../balance/account-balance.hooks'; +import { useStacksAccountFungibleTokenMetadata } from '../token-metadata/fungible-tokens/fungible-token-metadata.hooks'; +import { isFtAsset } from '../token-metadata/token-metadata.utils'; +import { + type Sip10CryptoAssetFilter, + filterSip10AccountCryptoAssetsWithDetails, +} from './sip10-tokens.utils'; + +export function isTransferableSip10Token(asset: Partial) { + return !isUndefined(asset.decimals) && !isUndefined(asset.name) && !isUndefined(asset.symbol); +} + +export function getSip10InfoFromAsset(asset: AccountCryptoAssetWithDetails) { + if ('contractId' in asset.info) return asset.info; + return; +} + +function createSip10CryptoAssetInfo( + contractId: string, + key: string, + token: FtMetadataResponse +): Sip10CryptoAssetInfo { + const { assetName, contractName } = getAssetStringParts(key); + const name = token.name ? token.name : assetName; + + return { + canTransfer: isTransferableSip10Token(token), + contractId, + contractName, + decimals: token.decimals ?? 0, + hasMemo: isTransferableSip10Token(token), + imageCanonicalUri: token.image_canonical_uri ?? '', + name, + symbol: token.symbol ?? getTicker(name), + }; +} + +function useSip10AccountCryptoAssetsWithDetails(address: string) { + const { data: tokens = {} } = useStacksAccountBalanceFungibleTokens(address); + const tokenMetadata = useStacksAccountFungibleTokenMetadata(tokens); + const priceAsMarketData = useAlexCurrencyPriceAsMarketData(); + + return useMemo( + () => + Object.entries(tokens) + .map(([key, value], i) => { + const token = tokenMetadata[i].data; + if (!(token && isFtAsset(token))) return; + const contractId = pullContractIdFromIdentity(key); + + return createAccountCryptoAssetWithDetailsFactory({ + balance: { + availableBalance: createMoney( + new BigNumber(value.balance), + token.symbol ?? getTicker(token.name ?? ''), + token.decimals ?? 0 + ), + }, + chain: 'stacks', + info: createSip10CryptoAssetInfo(contractId, key, token), + marketData: priceAsMarketData(contractId, token.symbol), + type: 'sip-10', + }); + }) + .filter(isDefined) + .filter(asset => asset.balance.availableBalance.amount.isGreaterThan(0)), + [priceAsMarketData, tokenMetadata, tokens] + ); +} + +export function useSip10CryptoAssetWithDetails(contractId: string) { + const address = useCurrentStacksAccountAddress(); + const assets = useSip10AccountCryptoAssetsWithDetails(address); + return useMemo( + () => assets.find(asset => asset.info.contractId === contractId), + [assets, contractId] + ); +} + +interface UseFilteredSip10AccountCryptoAssetsWithDetailsArgs { + address: string; + filter?: Sip10CryptoAssetFilter; +} +export function useFilteredSip10AccountCryptoAssetsWithDetails({ + address, + filter = 'all', +}: UseFilteredSip10AccountCryptoAssetsWithDetailsArgs) { + const assets = useSip10AccountCryptoAssetsWithDetails(address); + const { data: swapAssets = [] } = useAlexSwappableAssets(); + + return useMemo( + () => filterSip10AccountCryptoAssetsWithDetails(assets, swapAssets, filter), + [assets, swapAssets, filter] + ); +} + +export function useTransferableSip10CryptoAssetsWithDetails( + address: string +): Sip10AccountCryptoAssetWithDetails[] { + const assets = useSip10AccountCryptoAssetsWithDetails(address); + return useMemo(() => assets.filter(asset => asset.info.canTransfer), [assets]); +} diff --git a/src/app/query/stacks/sip10/sip10-tokens.spec.ts b/src/app/query/stacks/sip10/sip10-tokens.spec.ts new file mode 100644 index 00000000000..6e92dd062b2 --- /dev/null +++ b/src/app/query/stacks/sip10/sip10-tokens.spec.ts @@ -0,0 +1,32 @@ +import type { FtMetadataResponse } from '@hirosystems/token-metadata-api-client'; + +import { isTransferableSip10Token } from './sip10-tokens.hooks'; + +describe(isTransferableSip10Token.name, () => { + test('assets with a name, symbol and decimals are allowed to be transferred', () => { + const asset: Partial = { + decimals: 9, + name: 'SteLLa the Cat', + symbol: 'CAT', + }; + expect(isTransferableSip10Token(asset)).toBeTruthy(); + }); + + test('a token with no decimals is transferable', () => { + const asset: Partial = { + decimals: 0, + name: 'SteLLa the Cat', + symbol: 'CAT', + }; + expect(isTransferableSip10Token(asset)).toBeTruthy(); + }); + + test('assets missing either name, symbol or decimals may not be transferred', () => { + const asset: Partial = { + name: 'Test token', + symbol: 'TEST', + decimals: undefined, + }; + expect(isTransferableSip10Token(asset)).toBeFalsy(); + }); +}); diff --git a/src/app/query/stacks/sip10/sip10-tokens.utils.ts b/src/app/query/stacks/sip10/sip10-tokens.utils.ts new file mode 100644 index 00000000000..015f5154f60 --- /dev/null +++ b/src/app/query/stacks/sip10/sip10-tokens.utils.ts @@ -0,0 +1,22 @@ +import type { SwapAsset } from '@app/query/common/alex-sdk/alex-sdk.hooks'; +import type { Sip10AccountCryptoAssetWithDetails } from '@app/query/models/crypto-asset.model'; +import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; + +export type Sip10CryptoAssetFilter = 'all' | 'supported' | 'unsupported'; + +export function filterSip10AccountCryptoAssetsWithDetails( + assets: Sip10AccountCryptoAssetWithDetails[], + swapAssets: SwapAsset[], + filter: Sip10CryptoAssetFilter +) { + return assets.filter(asset => { + const { address: contractAddress } = getAssetStringParts(asset.info.contractId); + if (filter === 'supported') { + return swapAssets.some(swapAsset => swapAsset.principal.includes(contractAddress)); + } + if (filter === 'unsupported') { + return !swapAssets.some(swapAsset => swapAsset.principal.includes(contractAddress)); + } + return assets; + }); +} diff --git a/src/app/query/stacks/stx/stx-crypto-asset.hooks.ts b/src/app/query/stacks/stx/stx-crypto-asset.hooks.ts new file mode 100644 index 00000000000..6c89c853988 --- /dev/null +++ b/src/app/query/stacks/stx/stx-crypto-asset.hooks.ts @@ -0,0 +1,58 @@ +import type { StxCryptoAssetInfo } from '@leather-wallet/models'; + +import { STX_DECIMALS } from '@shared/constants'; +import { createMoney } from '@shared/models/money.model'; +import { isUndefined } from '@shared/utils'; + +import { useCryptoCurrencyMarketDataMeanAverage } from '@app/query/common/market-data/market-data.hooks'; +import { + type StxAccountCryptoAssetWithDetails, + createAccountCryptoAssetWithDetailsFactory, +} from '@app/query/models/crypto-asset.model'; + +import { useStxCryptoAssetBalance } from '../balance/account-balance.hooks'; + +const stxCryptoAssetInfo: StxCryptoAssetInfo = { + decimals: STX_DECIMALS, + hasMemo: true, + name: 'stacks', + symbol: 'STX', +}; + +const stxCryptoAssetBalancePlaceholder = { + availableBalance: createMoney(0, 'STX'), + availableUnlockedBalance: createMoney(0, 'STX'), + inboundBalance: createMoney(0, 'STX'), + lockedBalance: createMoney(0, 'STX'), + outboundBalance: createMoney(0, 'STX'), + pendingBalance: createMoney(0, 'STX'), + totalBalance: createMoney(0, 'STX'), + unlockedBalance: createMoney(0, 'STX'), +}; + +export const stxCryptoAssetPlaceholder = + createAccountCryptoAssetWithDetailsFactory({ + balance: stxCryptoAssetBalancePlaceholder, + chain: 'stacks', + info: stxCryptoAssetInfo, + marketData: null, + type: 'stx', + }); + +export function useStxAccountCryptoAssetWithDetails(address: string) { + const { data: balance, isInitialLoading } = useStxCryptoAssetBalance(address); + const marketData = useCryptoCurrencyMarketDataMeanAverage('STX'); + + if (isUndefined(balance)) return { asset: stxCryptoAssetPlaceholder, isInitialLoading }; + + return { + asset: createAccountCryptoAssetWithDetailsFactory({ + balance, + chain: 'stacks', + info: stxCryptoAssetInfo, + marketData, + type: 'stx', + }), + isInitialLoading, + }; +} diff --git a/src/app/query/stacks/stx20/stx20-tokens.hooks.ts b/src/app/query/stacks/stx20/stx20-tokens.hooks.ts index f3394d4a650..ce3231a5b8b 100644 --- a/src/app/query/stacks/stx20/stx20-tokens.hooks.ts +++ b/src/app/query/stacks/stx20/stx20-tokens.hooks.ts @@ -5,7 +5,7 @@ import { createMoney } from '@shared/models/money.model'; import type { Stx20Balance, Stx20Token } from '../stacks-client'; import { useStx20BalancesQuery } from './stx20-tokens.query'; -function makeStx20Token(token: Stx20Balance): Stx20Token { +function createStx20Token(token: Stx20Balance): Stx20Token { return { balance: createMoney(new BigNumber(token.balance), token.ticker, 0), marketData: null, @@ -15,6 +15,6 @@ function makeStx20Token(token: Stx20Balance): Stx20Token { export function useStx20Tokens(address: string) { return useStx20BalancesQuery(address, { - select: resp => resp.map(balance => makeStx20Token(balance)), + select: resp => resp.map(balance => createStx20Token(balance)), }); } diff --git a/src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.hooks.ts b/src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.hooks.ts new file mode 100644 index 00000000000..ee8966d866c --- /dev/null +++ b/src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.hooks.ts @@ -0,0 +1,12 @@ +import type { AddressBalanceResponse } from '@shared/models/account.model'; + +import { pullContractIdFromIdentity } from '@app/common/utils'; + +import { useGetFungibleTokenMetadataListQuery } from './fungible-token-metadata.query'; + +export function useStacksAccountFungibleTokenMetadata( + tokens: AddressBalanceResponse['fungible_tokens'] +) { + const tokenContractIds = Object.keys(tokens).map(key => pullContractIdFromIdentity(key)); + return useGetFungibleTokenMetadataListQuery(tokenContractIds); +} diff --git a/src/app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query.ts b/src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query.ts similarity index 100% rename from src/app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query.ts rename to src/app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query.ts diff --git a/src/app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-holdings.query.ts b/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-holdings.query.ts similarity index 100% rename from src/app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-holdings.query.ts rename to src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-holdings.query.ts diff --git a/src/app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.hooks.ts b/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.hooks.ts similarity index 100% rename from src/app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.hooks.ts rename to src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.hooks.ts diff --git a/src/app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.query.ts b/src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.query.ts similarity index 100% rename from src/app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.query.ts rename to src/app/query/stacks/token-metadata/non-fungible-tokens/non-fungible-token-metadata.query.ts diff --git a/src/app/query/stacks/tokens/token-metadata.utils.ts b/src/app/query/stacks/token-metadata/token-metadata.utils.ts similarity index 100% rename from src/app/query/stacks/tokens/token-metadata.utils.ts rename to src/app/query/stacks/token-metadata/token-metadata.utils.ts diff --git a/src/app/query/stacks/transactions/transactions-with-transfers.query.ts b/src/app/query/stacks/transactions/transactions-with-transfers.query.ts index 73659faee96..a999bbdd02b 100644 --- a/src/app/query/stacks/transactions/transactions-with-transfers.query.ts +++ b/src/app/query/stacks/transactions/transactions-with-transfers.query.ts @@ -3,7 +3,7 @@ import { UseQueryOptions, UseQueryResult, useQuery } from '@tanstack/react-query import { DEFAULT_LIST_LIMIT } from '@shared/constants'; -import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useStacksClient } from '@app/store/common/api-clients.hooks'; import { useCurrentNetworkState } from '@app/store/networks/networks.hooks'; @@ -18,7 +18,7 @@ const queryOptions: UseQueryOptions = { }; export function useGetAccountTransactionsWithTransfersQuery() { - const principal = useCurrentAccountStxAddressState(); + const principal = useCurrentStacksAccountAddress(); const { chain } = useCurrentNetworkState(); const client = useStacksClient(); const limiter = useHiroApiRateLimiter(); diff --git a/src/app/store/accounts/blockchain/bitcoin/bitcoin.ledger.ts b/src/app/store/accounts/blockchain/bitcoin/bitcoin.ledger.ts index e1647fe0bbb..425caa9fbff 100644 --- a/src/app/store/accounts/blockchain/bitcoin/bitcoin.ledger.ts +++ b/src/app/store/accounts/blockchain/bitcoin/bitcoin.ledger.ts @@ -8,6 +8,8 @@ import { import { selectDefaultWalletBitcoinKeys } from '@app/store/ledger/bitcoin/bitcoin-key.slice'; import { useCurrentNetwork } from '@app/store/networks/networks.selectors'; +// TODO: Asset refactor: remove if determined unnecessary +// ts-unused-exports:disable-next-line export function useHasBitcoinLedgerKeychain() { const network = useCurrentNetwork(); const accounts = useSelector(selectDefaultWalletBitcoinKeys); diff --git a/src/app/store/accounts/blockchain/stacks/stacks-account.hooks.ts b/src/app/store/accounts/blockchain/stacks/stacks-account.hooks.ts index 70af3b031fe..5e1cafc412f 100644 --- a/src/app/store/accounts/blockchain/stacks/stacks-account.hooks.ts +++ b/src/app/store/accounts/blockchain/stacks/stacks-account.hooks.ts @@ -17,6 +17,9 @@ export function useStacksAccounts() { return useAtomValue(stacksAccountState); } +// TODO: Refactor, we need to use conditional empty strings everywhere +// Can we remove these atoms? + // Comment below from original atom. This pattern encourages view level // implementation details to leak into the state structure. Do not do this. // This contains the state of the current account: @@ -38,7 +41,7 @@ export function useCurrentStacksAccount() { }, [accountIndex, accounts, hasSwitched, signatureIndex, txIndex]); } -export function useCurrentAccountStxAddressState() { +export function useCurrentStacksAccountAddress() { return useCurrentStacksAccount()?.address ?? ''; } diff --git a/src/app/store/accounts/blockchain/utils.ts b/src/app/store/accounts/blockchain/utils.ts index 01b497cbab3..e1f9ca75c53 100644 --- a/src/app/store/accounts/blockchain/utils.ts +++ b/src/app/store/accounts/blockchain/utils.ts @@ -1,18 +1,22 @@ import { useCallback } from 'react'; +import type { Blockchains } from '@leather-wallet/models'; + import { useHasCurrentBitcoinAccount } from './bitcoin/bitcoin.hooks'; import { useHasStacksLedgerKeychain } from './stacks/stacks.hooks'; +// TODO: Asset refactor: remove if determined unnecessary +// ts-unused-exports:disable-next-line export function useCheckLedgerBlockchainAvailable() { const hasBitcoinLedgerKeys = useHasCurrentBitcoinAccount(); const hasStacksLedgerKeys = useHasStacksLedgerKeychain(); return useCallback( - (symbol: string) => { - if (symbol === 'bitcoin') { + (chain: Blockchains) => { + if (chain === 'bitcoin') { return hasBitcoinLedgerKeys; } - if (symbol === 'stacks') { + if (chain === 'stacks') { return hasStacksLedgerKeys; } return false; diff --git a/src/app/store/transactions/post-conditions.hooks.ts b/src/app/store/transactions/post-conditions.hooks.ts index 15133fbd322..9162e91c76b 100644 --- a/src/app/store/transactions/post-conditions.hooks.ts +++ b/src/app/store/transactions/post-conditions.hooks.ts @@ -7,9 +7,9 @@ import { getPostCondition, handlePostConditions, } from '@app/common/transactions/stacks/post-condition.utils'; -import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/tokens/fungible-tokens/fungible-token-metadata.query'; -import { isFtAsset } from '@app/query/stacks/tokens/token-metadata.utils'; -import { useCurrentAccountStxAddressState } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; +import { useGetFungibleTokenMetadataQuery } from '@app/query/stacks/token-metadata/fungible-tokens/fungible-token-metadata.query'; +import { isFtAsset } from '@app/query/stacks/token-metadata/token-metadata.utils'; +import { useCurrentStacksAccountAddress } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useTransactionRequestState } from './requests.hooks'; @@ -34,7 +34,7 @@ export function usePostConditionModeState() { export function usePostConditionState() { const payload = useTransactionRequestState(); - const address = useCurrentAccountStxAddressState(); + const address = useCurrentStacksAccountAddress(); return useMemo(() => formatPostConditionState(payload, address), [address, payload]); } diff --git a/src/app/store/transactions/token-transfer.hooks.ts b/src/app/store/transactions/token-transfer.hooks.ts index 792518f2574..e60c6138d6b 100644 --- a/src/app/store/transactions/token-transfer.hooks.ts +++ b/src/app/store/transactions/token-transfer.hooks.ts @@ -1,6 +1,7 @@ -import { useCallback, useMemo } from 'react'; +import { useCallback } from 'react'; import { useAsync } from 'react-async-hook'; +import type { Sip10CryptoAssetInfo } from '@leather-wallet/models'; import { bytesToHex } from '@stacks/common'; import { TransactionTypes } from '@stacks/connect'; import { @@ -16,7 +17,6 @@ import { uintCV, } from '@stacks/transactions'; -import type { StacksFungibleTokenAssetBalance } from '@shared/models/crypto-asset-balance.model'; import type { StacksSendFormValues, StacksTransactionFormValues } from '@shared/models/form.model'; import { stxToMicroStx } from '@app/common/money/unit-conversion'; @@ -25,33 +25,13 @@ import { GenerateUnsignedTransactionOptions, generateUnsignedTransaction, } from '@app/common/transactions/stacks/generate-unsigned-txs'; -import { getStacksFungibleTokenCurrencyAssetBalance } from '@app/query/stacks/balance/stacks-ft-balances.utils'; import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { useCurrentStacksNetworkState } from '@app/store/networks/networks.hooks'; import { makePostCondition } from '@app/store/transactions/transaction.hooks'; +import { getAssetStringParts } from '@app/ui/utils/get-asset-string-parts'; import { useCurrentStacksAccount } from '../accounts/blockchain/stacks/stacks-account.hooks'; -function useMakeFungibleTokenTransfer(assetBalance?: StacksFungibleTokenAssetBalance) { - const currentAccount = useCurrentStacksAccount(); - const network = useCurrentStacksNetworkState(); - - return useMemo(() => { - if (assetBalance && currentAccount && currentAccount.address) { - const { contractAddress, contractAssetName, contractName } = assetBalance.asset; - return { - assetBalance, - contractAssetName, - contractAddress, - contractName, - network, - stxAddress: currentAccount.address, - }; - } - return; - }, [assetBalance, currentAccount, network]); -} - export function useGenerateStxTokenTransferUnsignedTx() { const { data: nextNonce } = useNextNonce(); const network = useCurrentStacksNetworkState(); @@ -99,29 +79,16 @@ export function useStxTokenTransferUnsignedTxState(values?: StacksSendFormValues return tx.result; } -export function useGenerateFtTokenTransferUnsignedTx( - assetBalance: StacksFungibleTokenAssetBalance -) { +export function useGenerateFtTokenTransferUnsignedTx(assetInfo: Sip10CryptoAssetInfo) { const { data: nextNonce } = useNextNonce(); const account = useCurrentStacksAccount(); - - const tokenCurrencyAssetBalance = getStacksFungibleTokenCurrencyAssetBalance(assetBalance); - const assetTransferState = useMakeFungibleTokenTransfer(tokenCurrencyAssetBalance); + const network = useCurrentStacksNetworkState(); return useCallback( async (values?: StacksSendFormValues | StacksTransactionFormValues) => { - if (!assetTransferState || !account) return; - const { - assetBalance, - network, - contractAddress, - contractAssetName, - contractName, - stxAddress, - } = assetTransferState; + if (!account) return; const functionName = 'transfer'; - const recipient = values && 'recipient' in values ? createAddress(values.recipient || '') @@ -132,29 +99,31 @@ export function useGenerateFtTokenTransferUnsignedTx( ? someCV(bufferCVFromString(values.memo || '')) : noneCV(); - const realAmount = - assetBalance.type === 'fungible-token' - ? ftUnshiftDecimals(amount, assetBalance.asset.decimals || 0) - : amount; + const amountAsFractionalUnit = ftUnshiftDecimals(amount, assetInfo.decimals || 0); + const { + address: contractAddress, + contractName, + assetName, + } = getAssetStringParts(assetInfo.contractId); const postConditionOptions = { - amount: realAmount, + amount: amountAsFractionalUnit, contractAddress, - contractAssetName, + contractAssetName: assetName, contractName, - stxAddress, + stxAddress: account.address, }; const postConditions = [makePostCondition(postConditionOptions)]; // (transfer (uint principal principal) (response bool uint)) const functionArgs: ClarityValue[] = [ - uintCV(realAmount), - standardPrincipalCVFromAddress(createAddress(stxAddress)), + uintCV(amountAsFractionalUnit), + standardPrincipalCVFromAddress(createAddress(account.address)), standardPrincipalCVFromAddress(recipient), ]; - if (assetBalance.asset.hasMemo) { + if (assetInfo.hasMemo) { functionArgs.push(memo); } @@ -177,13 +146,20 @@ export function useGenerateFtTokenTransferUnsignedTx( return generateUnsignedTransaction(options); }, - [account, assetTransferState, nextNonce] + [ + account, + assetInfo.contractId, + assetInfo.decimals, + assetInfo.hasMemo, + network, + nextNonce?.nonce, + ] ); } -export function useFtTokenTransferUnsignedTx(assetBalance: StacksFungibleTokenAssetBalance) { - const generateTx = useGenerateFtTokenTransferUnsignedTx(assetBalance); +export function useFtTokenTransferUnsignedTx(assetInfo: Sip10CryptoAssetInfo) { const account = useCurrentStacksAccount(); + const generateTx = useGenerateFtTokenTransferUnsignedTx(assetInfo); const tx = useAsync(async () => generateTx(), [account]); return tx.result; diff --git a/src/app/store/transactions/transaction.hooks.ts b/src/app/store/transactions/transaction.hooks.ts index 245ef93f4e1..a2eaa416684 100644 --- a/src/app/store/transactions/transaction.hooks.ts +++ b/src/app/store/transactions/transaction.hooks.ts @@ -30,8 +30,8 @@ import { useLedgerNavigate } from '@app/features/ledger/hooks/use-ledger-navigat import { useToast } from '@app/features/toasts/use-toast'; import { useNextNonce } from '@app/query/stacks/nonce/account-nonces.hooks'; import { - useCurrentAccountStxAddressState, useCurrentStacksAccount, + useCurrentStacksAccountAddress, } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks'; import { useCurrentStacksNetworkState } from '@app/store/networks/networks.hooks'; @@ -45,7 +45,7 @@ export function useTransactionPostConditions() { export function useUnsignedStacksTransactionBaseState() { const network = useCurrentStacksNetworkState(); const { data: nextNonce } = useNextNonce(); - const stxAddress = useCurrentAccountStxAddressState(); + const stxAddress = useCurrentStacksAccountAddress(); const payload = useTransactionRequestState(); const postConditions = useTransactionPostConditions(); const account = useCurrentStacksAccount(); diff --git a/src/shared/models/account.model.ts b/src/shared/models/account.model.ts index b3b5231ed87..96f12d39e57 100644 --- a/src/shared/models/account.model.ts +++ b/src/shared/models/account.model.ts @@ -1,7 +1,5 @@ import { AddressTokenOfferingLocked } from '@stacks/stacks-blockchain-api-types/generated'; -import type { Money } from './money.model'; - type SelectedKeys = | 'balance' | 'total_sent' @@ -47,17 +45,3 @@ export interface AddressBalanceResponse { >; token_offering_locked?: AddressTokenOfferingLocked; } - -export interface AccountStxBalanceBigNumber - extends Omit { - balance: Money; - total_sent: Money; - total_received: Money; - total_fees_sent: Money; - total_miner_rewards_received: Money; - locked: Money; -} - -export interface AccountBalanceResponseBigNumber extends Omit { - stx: AccountStxBalanceBigNumber; -} diff --git a/src/shared/models/crypto-asset-balance.model.ts b/src/shared/models/crypto-asset-balance.model.ts deleted file mode 100644 index e4274cbeb7c..00000000000 --- a/src/shared/models/crypto-asset-balance.model.ts +++ /dev/null @@ -1,46 +0,0 @@ -import BigNumber from 'bignumber.js'; - -import { - BitcoinCryptoCurrencyAsset, - StacksCryptoCurrencyAsset, - StacksFungibleTokenAsset, - StacksNonFungibleTokenAsset, -} from './crypto-asset.model'; -import { Money } from './money.model'; - -export interface BitcoinCryptoCurrencyAssetBalance { - readonly blockchain: 'bitcoin'; - readonly type: 'crypto-currency'; - readonly asset: BitcoinCryptoCurrencyAsset; - readonly balance: Money; -} - -export interface StacksCryptoCurrencyAssetBalance { - readonly blockchain: 'stacks'; - readonly type: 'crypto-currency'; - readonly asset: StacksCryptoCurrencyAsset; - readonly balance: Money; -} - -export interface StacksFungibleTokenAssetBalance { - readonly blockchain: 'stacks'; - readonly type: 'fungible-token'; - readonly asset: StacksFungibleTokenAsset; - readonly balance: Money; -} - -// ts-unused-exports:disable-next-line -export interface StacksNonFungibleTokenAssetBalance { - readonly blockchain: 'stacks'; - readonly type: 'non-fungible-token'; - readonly asset: StacksNonFungibleTokenAsset; - readonly count: BigNumber; -} - -export type AllCryptoCurrencyAssetBalances = - | BitcoinCryptoCurrencyAssetBalance - | StacksCryptoCurrencyAssetBalance; - -export type AllTransferableCryptoAssetBalances = - | AllCryptoCurrencyAssetBalances - | StacksFungibleTokenAssetBalance; diff --git a/src/shared/models/crypto-asset.model.ts b/src/shared/models/crypto-asset.model.ts deleted file mode 100644 index 78f75dc8adb..00000000000 --- a/src/shared/models/crypto-asset.model.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { MarketData } from './market.model'; - -export interface BitcoinCryptoCurrencyAsset { - decimals: number; - hasMemo: boolean; - name: string; - symbol: 'BTC'; -} - -export interface StacksCryptoCurrencyAsset { - decimals: number; - hasMemo: boolean; - name: string; - symbol: 'STX'; -} - -export interface StacksFungibleTokenAsset { - canTransfer: boolean; - contractId: string; - contractAddress: string; - contractAssetName: string; - contractName: string; - decimals: number; - hasMemo: boolean; - imageCanonicalUri: string; - name: string; - marketData: MarketData | null; - symbol: string; -} - -export interface StacksNonFungibleTokenAsset { - contractAddress: string; - contractAssetName: string; - contractName: string; - imageCanonicalUri: string; - name: string; -} diff --git a/tests/selectors/home.selectors.ts b/tests/selectors/home.selectors.ts index ab42923c2b9..ea28f10278d 100644 --- a/tests/selectors/home.selectors.ts +++ b/tests/selectors/home.selectors.ts @@ -1,4 +1,5 @@ export enum HomePageSelectors { + AssetList = 'asset-list', HomePageContainer = 'home-page-container', ReceiveCryptoAssetBtn = 'receive-crypto-asset-btn', ReceiveBtcNativeSegwitQrCodeBtn = 'receive-native-segwit-qr-code-btn', @@ -11,5 +12,4 @@ export enum HomePageSelectors { BalancesTabBtn = 'tab-balances', SwapBtn = 'swap-btn', FundAccountBtn = 'fund-account-btn', - BalancesList = 'balances-list', } diff --git a/tests/specs/rpc-get-addresses/get-addresses.spec.ts b/tests/specs/rpc-get-addresses/get-addresses.spec.ts index 48b13fc6b15..ac646f86d32 100644 --- a/tests/specs/rpc-get-addresses/get-addresses.spec.ts +++ b/tests/specs/rpc-get-addresses/get-addresses.spec.ts @@ -1,4 +1,3 @@ -import '@leather-wallet/types'; import type { BrowserContext, Page } from '@playwright/test'; import { TEST_PASSWORD } from '@tests/mocks/constants'; import { makeLedgerTestAccountWalletState } from '@tests/page-object-models/onboarding.page';