diff --git a/e2e-tests/popup/buy-cspr/buy-cspr.spec.ts b/e2e-tests/popup/buy-cspr/buy-cspr.spec.ts index 15ee207df..ea073682c 100644 --- a/e2e-tests/popup/buy-cspr/buy-cspr.spec.ts +++ b/e2e-tests/popup/buy-cspr/buy-cspr.spec.ts @@ -39,7 +39,7 @@ popup.describe('Popup UI: buy cspr', () => { } ); - popup( + popup.skip( 'should redirect to Ramp provider page', async ({ popupPage, unlockVault, context }) => { await unlockVault(); diff --git a/package-lock.json b/package-lock.json index e96dbbaf2..e59c9331c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "big.js": "^6.2.1", "casper-cep18-js-client": "1.0.2", "casper-js-sdk": "2.15.4", - "casper-wallet-core": "git+ssh://git@github.com:make-software/casper-wallet-core.git#v0.9.5", + "casper-wallet-core": "git+ssh://git@github.com:make-software/casper-wallet-core.git#v0.9.6", "date-fns": "^2.30.0", "i18next": "^23.11.0", "i18next-browser-languagedetector": "^7.2.1", @@ -42,7 +42,7 @@ "micro-aes-gcm": "0.3.3", "qrcode.react": "^3.1.0", "react": "^18.2.0", - "react-dom": "^18.0.2", + "react-dom": "^18.3.1", "react-hook-form": "7.48.2", "react-i18next": "14.1.0", "react-infinite-scroll-hook": "^4.1.1", @@ -66,7 +66,7 @@ "@babel/preset-env": "7.23.2", "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "^7.23.3", - "@playwright/test": "^1.39.0", + "@playwright/test": "^1.47.2", "@redux-devtools/cli": "^4.0.0", "@redux-devtools/remote": "^0.9.3", "@testing-library/dom": "9.3.4", @@ -82,7 +82,7 @@ "@types/md5": "^2.3.5", "@types/node": "^20.9.0", "@types/react": "^18.2.55", - "@types/react-dom": "^18.2.18", + "@types/react-dom": "^18.3.0", "@types/react-virtualized": "^9.21.30", "@types/styled-components": "^5.1.26", "@typescript-eslint/parser": "^5.62.0", @@ -4996,18 +4996,18 @@ "dev": true }, "node_modules/@playwright/test": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", - "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", + "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", "dev": true, "dependencies": { - "playwright": "1.39.0" + "playwright": "1.47.2" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/@pnpm/network.ca-file": { @@ -7484,9 +7484,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.18", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", - "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "devOptional": true, "dependencies": { "@types/react": "*" @@ -10601,8 +10601,8 @@ }, "node_modules/casper-wallet-core": { "name": "CasperWalletCore", - "version": "0.9.5", - "resolved": "git+ssh://git@github.com/make-software/casper-wallet-core.git#9d91ccbec58b1bd5c94a6d5dd658503a2fa1ce4c", + "version": "0.9.6", + "resolved": "git+ssh://git@github.com/make-software/casper-wallet-core.git#cef8e4ffb029396095993f020d19d472db3c4d02", "dependencies": { "@make-software/ces-js-parser": "^1.3.3", "@react-native/typescript-config": "0.74.83", @@ -23711,33 +23711,33 @@ } }, "node_modules/playwright": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", - "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", + "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", "dev": true, "dependencies": { - "playwright-core": "1.39.0" + "playwright-core": "1.47.2" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" }, "optionalDependencies": { "fsevents": "2.3.2" } }, "node_modules/playwright-core": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", - "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", + "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", "dev": true, "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/possible-typed-array-names": { @@ -24356,23 +24356,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-dom/node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" + "react": "^18.3.1" } }, "node_modules/react-fast-compare": { @@ -25501,6 +25493,14 @@ "integrity": "sha512-MgUIvuca+90fBrCWY5LdlU9YUWjlkPFwdpvmomcwQEu3t2id/6YHdG2nhB6o7nhRp4ocfmcXQTh00r/tJtynSg==", "dev": true }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/schema-utils": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", @@ -33328,12 +33328,12 @@ } }, "@playwright/test": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", - "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", + "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", "dev": true, "requires": { - "playwright": "1.39.0" + "playwright": "1.47.2" } }, "@pnpm/network.ca-file": { @@ -35363,9 +35363,9 @@ } }, "@types/react-dom": { - "version": "18.2.18", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", - "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "devOptional": true, "requires": { "@types/react": "*" @@ -37770,8 +37770,8 @@ } }, "casper-wallet-core": { - "version": "git+ssh://git@github.com/make-software/casper-wallet-core.git#9d91ccbec58b1bd5c94a6d5dd658503a2fa1ce4c", - "from": "casper-wallet-core@git+ssh://git@github.com:make-software/casper-wallet-core.git#v0.9.5", + "version": "git+ssh://git@github.com/make-software/casper-wallet-core.git#cef8e4ffb029396095993f020d19d472db3c4d02", + "from": "casper-wallet-core@git+ssh://git@github.com:make-software/casper-wallet-core.git#v0.9.6", "requires": { "@make-software/ces-js-parser": "^1.3.3", "@react-native/typescript-config": "0.74.83", @@ -47536,19 +47536,19 @@ } }, "playwright": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", - "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", + "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.39.0" + "playwright-core": "1.47.2" } }, "playwright-core": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", - "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", + "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", "dev": true }, "possible-typed-array-names": { @@ -48009,22 +48009,12 @@ } }, "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "requires": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "dependencies": { - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "requires": { - "loose-envify": "^1.1.0" - } - } + "scheduler": "^0.23.2" } }, "react-fast-compare": { @@ -48869,6 +48859,14 @@ "integrity": "sha512-MgUIvuca+90fBrCWY5LdlU9YUWjlkPFwdpvmomcwQEu3t2id/6YHdG2nhB6o7nhRp4ocfmcXQTh00r/tJtynSg==", "dev": true }, + "scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "requires": { + "loose-envify": "^1.1.0" + } + }, "schema-utils": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", diff --git a/package.json b/package.json index cdf258627..54646437b 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "big.js": "^6.2.1", "casper-cep18-js-client": "1.0.2", "casper-js-sdk": "2.15.4", - "casper-wallet-core": "git+ssh://git@github.com:make-software/casper-wallet-core.git#v0.9.5", + "casper-wallet-core": "git+ssh://git@github.com:make-software/casper-wallet-core.git#v0.9.6", "date-fns": "^2.30.0", "i18next": "^23.11.0", "i18next-browser-languagedetector": "^7.2.1", @@ -86,7 +86,7 @@ "micro-aes-gcm": "0.3.3", "qrcode.react": "^3.1.0", "react": "^18.2.0", - "react-dom": "^18.0.2", + "react-dom": "^18.3.1", "react-hook-form": "7.48.2", "react-i18next": "14.1.0", "react-infinite-scroll-hook": "^4.1.1", @@ -110,7 +110,7 @@ "@babel/preset-env": "7.23.2", "@babel/preset-react": "7.18.6", "@babel/preset-typescript": "^7.23.3", - "@playwright/test": "^1.39.0", + "@playwright/test": "^1.47.2", "@redux-devtools/cli": "^4.0.0", "@redux-devtools/remote": "^0.9.3", "@testing-library/dom": "9.3.4", @@ -126,7 +126,7 @@ "@types/md5": "^2.3.5", "@types/node": "^20.9.0", "@types/react": "^18.2.55", - "@types/react-dom": "^18.2.18", + "@types/react-dom": "^18.3.0", "@types/react-virtualized": "^9.21.30", "@types/styled-components": "^5.1.26", "@typescript-eslint/parser": "^5.62.0", diff --git a/src/apps/connect-to-app/pages/switch-account/content.tsx b/src/apps/connect-to-app/pages/switch-account/content.tsx index ce479aef8..af4446c11 100644 --- a/src/apps/connect-to-app/pages/switch-account/content.tsx +++ b/src/apps/connect-to-app/pages/switch-account/content.tsx @@ -47,7 +47,7 @@ type SwitchAccountContentProps = { requestId: string }; export function SwitchAccountContent({ requestId }: SwitchAccountContentProps) { const activeOrigin = useSelector(selectActiveOrigin); const activeAccount = useSelector(selectVaultActiveAccount); - const publicKeys = useSelector(selectVaultAccountsPublicKeys); + const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); const connectedAccountsToActiveTab = useSelector( selectConnectedAccountsWithActiveOrigin ); @@ -77,7 +77,7 @@ export function SwitchAccountContent({ requestId }: SwitchAccountContentProps) { account => account.name !== activeAccount?.name ); - const accountsInfo = useFetchAccountsInfo(publicKeys); + const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); return ( @@ -141,7 +141,7 @@ export function SwitchAccountContent({ requestId }: SwitchAccountContentProps) { {account.name} diff --git a/src/apps/onboarding/index.tsx b/src/apps/onboarding/index.tsx index a245e07ac..6775baf4f 100644 --- a/src/apps/onboarding/index.tsx +++ b/src/apps/onboarding/index.tsx @@ -1,3 +1,4 @@ +import { QueryClientProvider } from '@tanstack/react-query'; import React, { Suspense, useState } from 'react'; import { createRoot } from 'react-dom/client'; import { Provider as ReduxProvider } from 'react-redux'; @@ -14,6 +15,7 @@ import { onboardingAppInit } from '@background/redux/windowManagement/actions'; import '@libs/i18n/i18n'; import { ErrorBoundary } from '@libs/layout'; +import { newQueryClient } from '@libs/services/query-client'; import { GlobalStyle, lightTheme } from '@libs/ui'; const Tree = () => { @@ -37,9 +39,11 @@ const Tree = () => { - - - + + + + + diff --git a/src/apps/onboarding/pages/create-vault-password/index.tsx b/src/apps/onboarding/pages/create-vault-password/index.tsx index efbd80f11..0c08c95e0 100644 --- a/src/apps/onboarding/pages/create-vault-password/index.tsx +++ b/src/apps/onboarding/pages/create-vault-password/index.tsx @@ -3,7 +3,7 @@ import { useWatch } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; -import { TermsLink } from '@src/constants'; +import { TERMS_URLS } from '@src/constants'; import { Stepper } from '@onboarding/components/stepper'; import { RouterPath } from '@onboarding/router'; @@ -69,7 +69,7 @@ export function CreateVaultPasswordPage({ onClick={event => { event.stopPropagation(); event.preventDefault(); - window.open(TermsLink.Tos, '_blank'); + window.open(TERMS_URLS.tos, '_blank'); }} color="contentAction" > @@ -80,7 +80,7 @@ export function CreateVaultPasswordPage({ onClick={event => { event.stopPropagation(); event.preventDefault(); - window.open(TermsLink.Privacy, '_blank'); + window.open(TERMS_URLS.privacy, '_blank'); }} color="contentAction" > diff --git a/src/apps/onboarding/pages/select-accounts-to-recover/content.tsx b/src/apps/onboarding/pages/select-accounts-to-recover/content.tsx index d80b0b79b..d0384d4de 100644 --- a/src/apps/onboarding/pages/select-accounts-to-recover/content.tsx +++ b/src/apps/onboarding/pages/select-accounts-to-recover/content.tsx @@ -1,5 +1,5 @@ import { Player } from '@lottiefiles/react-lottie-player'; -import React, { SetStateAction } from 'react'; +import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; import styled from 'styled-components'; @@ -13,12 +13,7 @@ import { TabPageContainer, VerticalSpaceContainer } from '@libs/layout'; -import { AccountListRows } from '@libs/types/account'; -import { - DynamicAccountsListWithSelect, - Tile, - Typography -} from '@libs/ui/components'; +import { Tile, Typography } from '@libs/ui/components'; const AnimationContainer = styled(CenteredFlexColumn)` padding: 106px 16px; @@ -26,24 +21,12 @@ const AnimationContainer = styled(CenteredFlexColumn)` interface SelectAccountsToRecoverContentProps { isLoading: boolean; - derivedAccountsWithBalance: AccountListRows[]; - isLoadingMore: boolean; - onLoadMore: () => void; - maxItemsToRender: number; - setSelectedAccounts: React.Dispatch>; - selectedAccounts: AccountListRows[]; - setIsButtonDisabled: React.Dispatch>; + children: React.ReactNode; } export const SelectAccountsToRecoverContent = ({ isLoading, - isLoadingMore, - onLoadMore, - derivedAccountsWithBalance, - maxItemsToRender, - setSelectedAccounts, - selectedAccounts, - setIsButtonDisabled + children }: SelectAccountsToRecoverContentProps) => { const { t } = useTranslation(); const isDarkMode = useIsDarkMode(); @@ -90,16 +73,7 @@ export const SelectAccountsToRecoverContent = ({ ) : ( - + children )} ); diff --git a/src/apps/onboarding/pages/select-accounts-to-recover/index.tsx b/src/apps/onboarding/pages/select-accounts-to-recover/index.tsx index ea1250ca7..12f364066 100644 --- a/src/apps/onboarding/pages/select-accounts-to-recover/index.tsx +++ b/src/apps/onboarding/pages/select-accounts-to-recover/index.tsx @@ -1,8 +1,5 @@ import React, { useEffect, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; - -import { isEqualCaseInsensitive } from '@src/utils'; import { Stepper } from '@onboarding/components/stepper'; import { SelectAccountsToRecoverContent } from '@onboarding/pages/select-accounts-to-recover/content'; @@ -15,7 +12,6 @@ import { import { closeActiveTab } from '@onboarding/utils/close-active-tab'; import { recoverVault } from '@background/redux/sagas/actions'; -import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; import { dispatchToMainStore } from '@background/redux/utils'; import { getAccountHashFromPublicKey } from '@libs/entities/Account'; @@ -25,9 +21,9 @@ import { TabFooterContainer, TabHeaderContainer } from '@libs/layout'; -import { dispatchFetchAccountBalances } from '@libs/services/balance-service'; +import { useFetchAccountsBalances } from '@libs/services/balance-service'; import { Account, AccountListRows, KeyPair } from '@libs/types/account'; -import { Button } from '@libs/ui/components'; +import { Button, DynamicAccountsListWithSelect } from '@libs/ui/components'; export const SelectAccountsToRecoverPage = () => { const [derivedAccounts, setDerivedAccounts] = useState([]); @@ -47,10 +43,6 @@ export const SelectAccountsToRecoverPage = () => { const { t } = useTranslation(); const location = useTypedLocation(); - const { casperWalletApiUrl } = useSelector( - selectApiConfigBasedOnActiveNetwork - ); - useEffect(() => { if (location.state?.secretPhrase) { const keyPairs = getKeyPairList({ @@ -63,49 +55,31 @@ export const SelectAccountsToRecoverPage = () => { } }, [location.state?.secretPhrase, setDerivedAccounts]); - useEffect(() => { - if (!derivedAccounts.length) return; + const accountHashes = derivedAccounts.map(account => + getAccountHashFromPublicKey(account.publicKey) + ); - const hashes = derivedAccounts.reduce( - (previousValue, currentValue, currentIndex) => { - const hash = getAccountHashFromPublicKey(currentValue.publicKey); + const { accountsBalances, isLoadingBalances } = + useFetchAccountsBalances(accountHashes); - return derivedAccounts.length === currentIndex + 1 - ? previousValue + `${hash}` - : previousValue + `${hash},`; - }, - '' + useEffect(() => { + if (!derivedAccounts.length || isLoadingBalances) return; + + const derivedAccountsWithBalance: AccountListRows[] = derivedAccounts.map( + (account, index) => ({ + ...account, + id: account.publicKey, + hidden: false, + derivationIndex: index, + name: '' + }) ); - dispatchFetchAccountBalances(hashes) - .then(({ payload }) => { - if ('data' in payload) { - const derivedAccountsWithBalance: AccountListRows[] = - derivedAccounts.map((account, index) => { - const accountWithBalance = payload.data.find(ac => - isEqualCaseInsensitive(ac.public_key, account.publicKey) - ); - - return { - ...account, - id: account.publicKey, - hidden: false, - derivationIndex: index, - name: '', - balance: { - liquidMotes: `${accountWithBalance?.balance ?? '0'}` - } - }; - }); - - setDerivedAccountsWithBalance(derivedAccountsWithBalance); - } - }) - .finally(() => { - setIsLoading(false); - setIsLoadingMore(false); - }); - }, [derivedAccounts, casperWalletApiUrl]); + setDerivedAccountsWithBalance(derivedAccountsWithBalance); + + setIsLoading(false); + setIsLoadingMore(false); + }, [accountsBalances, derivedAccounts, isLoadingBalances]); const onLoadMore = () => { try { @@ -156,16 +130,19 @@ export const SelectAccountsToRecoverPage = () => { )} renderContent={() => ( - + + + )} renderFooter={() => ( diff --git a/src/apps/popup/pages/account-settings/content.tsx b/src/apps/popup/pages/account-settings/content.tsx index fd964cc6a..b43a6fbcc 100644 --- a/src/apps/popup/pages/account-settings/content.tsx +++ b/src/apps/popup/pages/account-settings/content.tsx @@ -12,6 +12,7 @@ import { dispatchToMainStore } from '@background/redux/utils'; import { hideAccountFromListChanged } from '@background/redux/vault/actions'; import { selectVaultAccount, + selectVaultAccountsPublicKeys, selectVaultHiddenAccountsNames, selectVaultImportedAccountNames, selectVaultLedgerAccountNames @@ -42,13 +43,14 @@ export function AccountSettingsPageContent() { const account = useSelector((state: RootState) => selectVaultAccount(state, accountName || '') ); + const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); if (!account) { throw new Error("Account doesn't exist"); } + const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); const accountHash = getAccountHashFromPublicKey(account.publicKey); - const accountsInfo = useFetchAccountsInfo([account.publicKey]); const csprName = accountsInfo && accountsInfo[accountHash]?.csprName; diff --git a/src/apps/popup/pages/all-accounts/content.tsx b/src/apps/popup/pages/all-accounts/content.tsx index 9898508c8..6155aa316 100644 --- a/src/apps/popup/pages/all-accounts/content.tsx +++ b/src/apps/popup/pages/all-accounts/content.tsx @@ -13,6 +13,7 @@ import { import { getAccountHashFromPublicKey } from '@libs/entities/Account'; import { ContentContainer, SpacingSize } from '@libs/layout'; import { useFetchAccountsInfo } from '@libs/services/account-info'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { AccountListRowWithAccountHash, AccountListRows @@ -39,6 +40,7 @@ export const AllAccountsContent = () => { const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); + const { accountsBalances, isLoadingBalance } = useFetchWalletBalance(); useEffect(() => { const visibleAccountListRows = sortAccounts( @@ -79,6 +81,10 @@ export const AllAccountsContent = () => { const isConnected = connectedAccountNames.includes(account.name); const isActiveAccount = activeAccountName === account.name; + const accountLiquidBalance = + accountsBalances && + accountsBalances[account.accountHash]?.liquidBalance; + return ( { isConnected={isConnected} showHideAccountItem accountsInfo={accountsInfo} + accountLiquidBalance={accountLiquidBalance} + isLoadingBalance={isLoadingBalance} /> ); }} @@ -100,6 +108,10 @@ export const AllAccountsContent = () => { renderRow={account => { const isConnected = connectedAccountNames.includes(account.name); + const accountLiquidBalance = + accountsBalances && + accountsBalances[account.accountHash]?.liquidBalance; + return ( { isConnected={isConnected} showHideAccountItem accountsInfo={accountsInfo} + accountLiquidBalance={accountLiquidBalance} + isLoadingBalance={isLoadingBalance} /> ); }} diff --git a/src/apps/popup/pages/buy-cspr/country.tsx b/src/apps/popup/pages/buy-cspr/country.tsx index 7bf564501..87cb8c7d1 100644 --- a/src/apps/popup/pages/buy-cspr/country.tsx +++ b/src/apps/popup/pages/buy-cspr/country.tsx @@ -1,9 +1,6 @@ import React, { useEffect, useState } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; - -import { selectAccountBalance } from '@background/redux/account-info/selectors'; import { ContentContainer, @@ -11,6 +8,7 @@ import { ParagraphContainer, SpacingSize } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { ResponseCountryPropsWithId } from '@libs/services/buy-cspr-service/types'; import { ActiveAccountPlate, @@ -21,7 +19,6 @@ import { SvgIcon, Typography } from '@libs/ui/components'; -import { motesToCSPR } from '@libs/ui/utils'; import { CountryRow } from './components/country-row'; import { ListRow } from './components/list-row'; @@ -45,10 +42,7 @@ export const Country = ({ >([]); const { t } = useTranslation(); - const csprBalance = useSelector(selectAccountBalance); - - const balance = - csprBalance.liquidMotes && motesToCSPR(csprBalance.liquidMotes); + const { accountBalance } = useFetchWalletBalance(); const { register, control, setValue } = useForm(); @@ -79,7 +73,7 @@ export const Country = ({ diff --git a/src/apps/popup/pages/connect-another-account/index.tsx b/src/apps/popup/pages/connect-another-account/index.tsx index f5dcb115f..3c5e3d0b2 100644 --- a/src/apps/popup/pages/connect-another-account/index.tsx +++ b/src/apps/popup/pages/connect-another-account/index.tsx @@ -58,7 +58,7 @@ export function ConnectAnotherAccountPageContent() { const { targetAccountName } = useParams(); const activeOrigin = useSelector(selectActiveOrigin); - const publicKeys = useSelector(selectVaultAccountsPublicKeys); + const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); const { connectAnotherAccountWithEvent: connectAccount, @@ -87,7 +87,7 @@ export function ConnectAnotherAccountPageContent() { targetAccount?.publicKey ); - const accountsInfo = useFetchAccountsInfo(publicKeys); + const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); const targetAccountCsprName = accountsInfo && accountsInfo[targetAccountHash]?.csprName; diff --git a/src/apps/popup/pages/connected-sites/index.tsx b/src/apps/popup/pages/connected-sites/index.tsx index c3ec3879e..b2560728d 100644 --- a/src/apps/popup/pages/connected-sites/index.tsx +++ b/src/apps/popup/pages/connected-sites/index.tsx @@ -32,11 +32,11 @@ export function ConnectedSitesPage() { const accountsByOrigin = useSelector(selectAccountsByOriginDict); const siteNameByOriginDict = useSelector(selectSiteNameByOriginDict); - const publicKeys = useSelector(selectVaultAccountsPublicKeys); + const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); const isNoSitesConnected = !Object.entries(accountsByOrigin).length; - const accountsInfo = useFetchAccountsInfo(publicKeys); + const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); if (isNoSitesConnected) { return ( diff --git a/src/apps/popup/pages/contact-details/details.tsx b/src/apps/popup/pages/contact-details/details.tsx index 3655ce83b..5482d06a5 100644 --- a/src/apps/popup/pages/contact-details/details.tsx +++ b/src/apps/popup/pages/contact-details/details.tsx @@ -1,14 +1,19 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; +import { useSelector } from 'react-redux'; import styled from 'styled-components'; +import { selectAllContactsPublicKeys } from '@background/redux/contacts/selectors'; import { Contact } from '@background/redux/contacts/types'; +import { getAccountHashFromPublicKey } from '@libs/entities/Account'; import { ContentContainer, + FlexColumn, LeftAlignedFlexColumn, SpacingSize } from '@libs/layout'; +import { useFetchAccountsInfo } from '@libs/services/account-info'; import { Avatar, Hash, @@ -32,6 +37,13 @@ interface ContactDetailsProps { export const ContactDetails = ({ contact }: ContactDetailsProps) => { const { t } = useTranslation(); + const contactPublicKeys = useSelector(selectAllContactsPublicKeys); + + const accountsInfo = useFetchAccountsInfo(contactPublicKeys); + + const accountHash = getAccountHashFromPublicKey(contact.publicKey); + const csprName = accountsInfo && accountsInfo[accountHash]?.csprName; + return ( @@ -44,6 +56,16 @@ export const ContactDetails = ({ contact }: ContactDetailsProps) => { variant={HashVariant.CaptionHash} color="contentPrimary" /> + {csprName ? ( + + + CSPR.name + + + {csprName} + + + ) : null} Last edited: {formatShortTimestamp(contact.lastModified)} diff --git a/src/apps/popup/pages/contact-details/index.tsx b/src/apps/popup/pages/contact-details/index.tsx index 8e9a5eeec..ef6eaf767 100644 --- a/src/apps/popup/pages/contact-details/index.tsx +++ b/src/apps/popup/pages/contact-details/index.tsx @@ -2,13 +2,13 @@ import React, { useCallback, useMemo, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; -import { PasswordProtectionPage } from 'src/apps/popup/pages/password-protection-page'; import { getBlockExplorerAccountUrl } from '@src/constants'; import { DeleteContactPageContent } from '@popup/pages/contact-details/deleting'; import { ContactDetails } from '@popup/pages/contact-details/details'; import { EditingContactPageContent } from '@popup/pages/contact-details/editing'; +import { PasswordProtectionPage } from '@popup/pages/password-protection-page'; import { RouterPath, useTypedNavigate } from '@popup/router'; import { diff --git a/src/apps/popup/pages/contacts/components/contacts-plate.tsx b/src/apps/popup/pages/contacts/components/contacts-plate.tsx index 8e0a3b226..421ca3a82 100644 --- a/src/apps/popup/pages/contacts/components/contacts-plate.tsx +++ b/src/apps/popup/pages/contacts/components/contacts-plate.tsx @@ -1,13 +1,10 @@ +import { Maybe } from 'casper-wallet-core/src/typings/common'; import React from 'react'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; import { RouterPath, useTypedNavigate } from '@popup/router'; -import { selectAllContactsPublicKeys } from '@background/redux/contacts/selectors'; - import { AlignedFlexRow, FlexColumn, SpacingSize } from '@libs/layout'; -import { useFetchAccountsInfo } from '@libs/services/account-info'; import { Avatar, Hash, HashVariant, Typography } from '@libs/ui/components'; const Container = styled(AlignedFlexRow)` @@ -19,22 +16,18 @@ const Container = styled(AlignedFlexRow)` interface ContactsPlateProps { publicKey: string; name: string; - accountHash: string; + csprName: Maybe | undefined; + brandingLogo: Maybe | undefined; } export const ContactsPlate = ({ publicKey, name, - accountHash + csprName, + brandingLogo }: ContactsPlateProps) => { const navigate = useTypedNavigate(); - const contactPublicKeys = useSelector(selectAllContactsPublicKeys); - const accountsInfo = useFetchAccountsInfo(contactPublicKeys); - - const csprName = accountsInfo && accountsInfo[accountHash]?.csprName; - const brandingLogo = accountsInfo && accountsInfo[accountHash]?.brandingLogo; - return ( { const contacts = useSelector(selectAllContacts); const lastModified = useSelector(selectLastModified); + const contactPublicKeys = useSelector(selectAllContactsPublicKeys); const contactsWithId = contacts.map((contact, index) => ({ ...contact, @@ -31,6 +34,8 @@ export const ContactsBookPageContent = () => { accountHash: getAccountHashFromPublicKey(contact.publicKey) })); + const accountsInfo = useFetchAccountsInfo(contactPublicKeys); + if (contactsWithId.length === 0) { return ; } @@ -53,13 +58,20 @@ export const ContactsBookPageContent = () => { )} ( - - )} + renderRow={({ publicKey, name, accountHash }) => { + const csprName = accountsInfo && accountsInfo[accountHash]?.csprName; + const brandingLogo = + accountsInfo && accountsInfo[accountHash]?.brandingLogo; + + return ( + + ); + }} marginLeftForItemSeparatorLine={54} /> diff --git a/src/apps/popup/pages/download-account-keys/components/account-list-item.tsx b/src/apps/popup/pages/download-account-keys/components/account-list-item.tsx index 39ea860d3..29300b1e3 100644 --- a/src/apps/popup/pages/download-account-keys/components/account-list-item.tsx +++ b/src/apps/popup/pages/download-account-keys/components/account-list-item.tsx @@ -1,3 +1,4 @@ +import { IAccountInfo } from 'casper-wallet-core/src/domain/accountInfo'; import React from 'react'; import styled from 'styled-components'; @@ -6,7 +7,11 @@ import { FlexColumn, SpacingSize } from '@libs/layout'; -import { AccountListRows, HardwareWalletType } from '@libs/types/account'; +import { + AccountListRowWithAccountHash, + AccountListRows, + HardwareWalletType +} from '@libs/types/account'; import { Avatar, Checkbox, @@ -37,11 +42,13 @@ const AccountName = styled(Typography)` `; interface AccountListItemProps { - account: AccountListRows; + account: AccountListRowWithAccountHash; onClick: (event: React.MouseEvent) => void; isConnected: boolean; isActiveAccount: boolean; isSelected?: boolean; + accountsInfo: Record | undefined; + accountLiquidBalance: string | undefined; } export const AccountListItem = ({ @@ -49,14 +56,19 @@ export const AccountListItem = ({ onClick, isActiveAccount, isConnected, - isSelected + isSelected, + accountsInfo, + accountLiquidBalance }: AccountListItemProps) => { - const accountBalance = - account.balance?.liquidMotes != null - ? formatNumber(motesToCSPR(account.balance.liquidMotes), { - precision: { max: 0 } - }) - : '-'; + const accountBalance = accountLiquidBalance + ? formatNumber(motesToCSPR(accountLiquidBalance), { + precision: { max: 0 } + }) + : '0'; + + const csprName = accountsInfo && accountsInfo[account.accountHash]?.csprName; + const brandingLogo = + accountsInfo && accountsInfo[account.accountHash]?.brandingLogo; return ( @@ -68,23 +80,21 @@ export const AccountListItem = ({ isConnected={isConnected} displayContext="accountList" isActiveAccount={isActiveAccount} + brandingLogo={brandingLogo} /> {account.name} - + {accountBalance} { - const [accountsWithId, setAccountsWithId] = useState([]); + const [accountsWithId, setAccountsWithId] = useState< + AccountListRowWithAccountHash[] + >([]); const { t } = useTranslation(); - // TODO: update this when the ledger feature is ready, to not allow the download of the ledger accounts - const accounts = useSelector(selectVaultAccountsWithBalances); + const accounts = useSelector(selectVaultAccountsExceptLedgersAccounts); const connectedAccountNames = useSelector(selectConnectedAccountNamesWithActiveOrigin) || []; const activeAccountName = useSelector(selectVaultActiveAccountName); + const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); + + const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); + const { accountsBalances } = useFetchWalletBalance(); useEffect(() => { const accountsWithId = accounts.map(account => ({ ...account, - id: account.name + id: account.name, + accountHash: getAccountHashFromPublicKey(account.publicKey) })); setAccountsWithId(accountsWithId); @@ -99,6 +112,10 @@ export const Download = ({ const isSelected = selectedAccounts.findIndex(acc => acc.id === account.id) !== -1; + const accountLiquidBalance = + accountsBalances && + accountsBalances[account.accountHash]?.liquidBalance; + return ( toggleAccount(account)} isSelected={isSelected} + accountLiquidBalance={accountLiquidBalance} + accountsInfo={accountsInfo} /> ); }} diff --git a/src/apps/popup/pages/home/components/account-balance/amount-bar.tsx b/src/apps/popup/pages/home/components/account-balance/amount-bar.tsx index 63b859651..a0c557062 100644 --- a/src/apps/popup/pages/home/components/account-balance/amount-bar.tsx +++ b/src/apps/popup/pages/home/components/account-balance/amount-bar.tsx @@ -1,10 +1,8 @@ import React, { useMemo } from 'react'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; -import { selectAccountBalance } from '@background/redux/account-info/selectors'; - import { FlexRow, SpacingSize } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { ContentColor, getColorFromTheme, motesToCSPR } from '@libs/ui/utils'; const Container = styled(FlexRow)` @@ -20,8 +18,8 @@ const Filled = styled.div<{ filledWidth: number; color: ContentColor }>` `; function calculateLiquidPercentage( - amountMotes: string | null, - fullBalanceMotes: string | null + amountMotes: string | undefined, + fullBalanceMotes: string | undefined ) { if (amountMotes != null && fullBalanceMotes != null) { const current = Number(motesToCSPR(amountMotes)); @@ -34,28 +32,31 @@ function calculateLiquidPercentage( } export const AmountBar = () => { - const balance = useSelector(selectAccountBalance); + const { accountBalance } = useFetchWalletBalance(); const liquidPercentage = useMemo( () => - calculateLiquidPercentage(balance.liquidMotes, balance.totalBalanceMotes), - [balance.liquidMotes, balance.totalBalanceMotes] + calculateLiquidPercentage( + accountBalance.liquidBalance, + accountBalance.totalBalance + ), + [accountBalance.liquidBalance, accountBalance.totalBalance] ); const delegatedPercentage = useMemo( () => calculateLiquidPercentage( - balance.delegatedMotes, - balance.totalBalanceMotes + accountBalance.delegatedBalance, + accountBalance.totalBalance ), - [balance.delegatedMotes, balance.totalBalanceMotes] + [accountBalance.delegatedBalance, accountBalance.totalBalance] ); const undelegatingPercentage = useMemo( () => calculateLiquidPercentage( - balance.undelegatingMotes, - balance.totalBalanceMotes + accountBalance.undelegatingBalance, + accountBalance.totalBalance ), - [balance.undelegatingMotes, balance.totalBalanceMotes] + [accountBalance.undelegatingBalance, accountBalance.totalBalance] ); return ( diff --git a/src/apps/popup/pages/home/components/account-balance/amount-row.tsx b/src/apps/popup/pages/home/components/account-balance/amount-row.tsx index 9160dbb50..410c4afeb 100644 --- a/src/apps/popup/pages/home/components/account-balance/amount-row.tsx +++ b/src/apps/popup/pages/home/components/account-balance/amount-row.tsx @@ -1,61 +1,42 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; - -import { selectAccountCurrencyRate } from '@background/redux/account-info/selectors'; import { AlignedSpaceBetweenFlexRow, FlexRow, SpacingSize } from '@libs/layout'; import { Typography } from '@libs/ui/components'; -import { - ContentColor, - formatCurrency, - formatNumber, - motesToCSPR, - motesToCurrency -} from '@libs/ui/utils'; +import { ContentColor } from '@libs/ui/utils'; interface AmountRowProps { text: string; - amountMotes: string | null; + fiatAmount: string | undefined; + amountFormattedDecimalBalance: string | undefined; color: ContentColor; showFiatAmounts: boolean; } export const AmountRow = ({ text, - amountMotes, + fiatAmount, + amountFormattedDecimalBalance, color, showFiatAmounts }: AmountRowProps) => { const { t } = useTranslation(); - const currencyRate = useSelector(selectAccountCurrencyRate); - - const fiatAmount = - currencyRate != null && amountMotes != null - ? formatCurrency( - motesToCurrency(String(amountMotes), currencyRate), - 'USD', - { - precision: 2 - } - ) - : ''; - return ( {text} {showFiatAmounts ? ( - {fiatAmount} + + {fiatAmount} + ) : ( - - {amountMotes == null - ? '-' - : formatNumber(motesToCSPR(amountMotes), { - precision: { max: 5 } - })} + + {amountFormattedDecimalBalance} CSPR diff --git a/src/apps/popup/pages/home/components/account-balance/index.tsx b/src/apps/popup/pages/home/components/account-balance/index.tsx index ddc5dd09a..89cdf5a1b 100644 --- a/src/apps/popup/pages/home/components/account-balance/index.tsx +++ b/src/apps/popup/pages/home/components/account-balance/index.tsx @@ -1,13 +1,10 @@ import React, { useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; -import { selectAccountBalance } from '@background/redux/account-info/selectors'; - import { FlexColumn, FlexRow, SpacingSize } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { Typography, getFontSizeBasedOnTextLength } from '@libs/ui/components'; -import { formatNumber, motesToCSPR } from '@libs/ui/utils'; import { AmountBar } from './amount-bar'; import { AmountRow } from './amount-row'; @@ -20,7 +17,7 @@ export const AccountBalance = () => { const [showFiatAmounts, setShowFiatAmounts] = useState(false); const { t } = useTranslation(); - const balance = useSelector(selectAccountBalance); + const { accountBalance } = useFetchWalletBalance(); return ( @@ -28,30 +25,26 @@ export const AccountBalance = () => { Total balance ยท - {balance.totalBalanceFiat} + {accountBalance.totalFormattedFiatBalance} - {balance.totalBalanceMotes == null - ? '-' - : formatNumber(motesToCSPR(balance.totalBalanceMotes), { - precision: { max: 5 } - })} + {accountBalance?.totalFormattedDecimalBalance} CSPR @@ -67,21 +60,30 @@ export const AccountBalance = () => { > - {balance.undelegatingMotes !== '0' && - balance.undelegatingMotes != null && ( + {accountBalance?.undelegatingBalance && + accountBalance?.undelegatingBalance !== '0' && ( diff --git a/src/apps/popup/pages/home/components/tokens-list/index.tsx b/src/apps/popup/pages/home/components/tokens-list/index.tsx index d36f2e0bd..3b5909d35 100644 --- a/src/apps/popup/pages/home/components/tokens-list/index.tsx +++ b/src/apps/popup/pages/home/components/tokens-list/index.tsx @@ -5,12 +5,12 @@ import styled from 'styled-components'; import { RouterPath, useTypedNavigate } from '@popup/router'; import { TokenType, useCasperToken } from '@hooks/use-casper-token'; -import { useFetchErc20Tokens } from '@hooks/use-fetch-erc20-tokens'; import { SpaceBetweenFlexRow, SpacingSize } from '@libs/layout'; +import { useFetchCep18Tokens } from '@libs/services/cep18-service'; import { List, TokenPlate, Typography } from '@libs/ui/components'; -import { formatErc20TokenBalance } from './utils'; +import { formatCep18Tokens } from './utils'; const TotalValueContainer = styled(SpaceBetweenFlexRow)` padding: 12px 16px; @@ -25,12 +25,12 @@ export const TokensList = () => { const [totalAmountFiat, setTotalAmountFiat] = useState(null); const casperToken = useCasperToken(); - const erc20Tokens = useFetchErc20Tokens(); + const { cep18Tokens } = useFetchCep18Tokens(); const { t } = useTranslation(); const navigate = useTypedNavigate(); useEffect(() => { - const erc20TokensList = formatErc20TokenBalance(erc20Tokens); + const formatedCep18Tokens = formatCep18Tokens(cep18Tokens); const tokensList: TokenType[] = []; @@ -39,12 +39,12 @@ export const TokensList = () => { setTotalAmountFiat(casperToken.amountFiat); } - if (erc20TokensList) { - tokensList.push(...erc20TokensList); + if (formatedCep18Tokens) { + tokensList.push(...formatedCep18Tokens); } setTokensList(tokensList); - }, [casperToken, erc20Tokens]); + }, [casperToken, cep18Tokens]); useEffect(() => { const container = document.querySelector('#ms-container'); diff --git a/src/apps/popup/pages/home/components/tokens-list/utils.ts b/src/apps/popup/pages/home/components/tokens-list/utils.ts index edebce245..2bf4b328e 100644 --- a/src/apps/popup/pages/home/components/tokens-list/utils.ts +++ b/src/apps/popup/pages/home/components/tokens-list/utils.ts @@ -1,40 +1,22 @@ -import { TokenType } from '@hooks/use-casper-token'; - -import { ContractPackageWithBalance } from '@libs/services/erc20-service'; -import { divideErc20Balance, formatNumber } from '@libs/ui/utils'; +import { TokenDto } from 'casper-wallet-core/src/data/dto'; -export const MINIMUM_SHOWING_BALANCE = 0.00001; +import { TokenType } from '@hooks/use-casper-token'; -export const formatErc20TokenBalance = ( - erc20Tokens: ContractPackageWithBalance[] | null +export const formatCep18Tokens = ( + cep18Tokens: TokenDto[] | undefined ): TokenType[] | undefined => { - return erc20Tokens - ?.map(token => { - const calculatedErc20Balance = token?.balance - ? divideErc20Balance(token?.balance, token.metadata?.decimals) - : '0'; - - const formattedErc20Amount = calculatedErc20Balance - ? formatNumber(calculatedErc20Balance, { precision: { max: 5 } }) - : '-'; - - const erc20Amount = - parseFloat(formattedErc20Amount) >= MINIMUM_SHOWING_BALANCE - ? formattedErc20Amount - : '0'; - - return { - id: token.contract_package_hash, - contractHash: token.contractHash, - name: token.contract_name, - balance: token.balance, - amount: erc20Amount, - symbol: token?.metadata?.symbol || '', - decimals: token?.metadata?.decimals, - amountFiat: null, - icon: token.icon_url || 'assets/icons/cep18-contract-icon.svg' - }; - }) + return cep18Tokens + ?.map(token => ({ + id: token.contractPackageHash, + contractHash: token.contractHash, + name: token.name, + balance: token.balance, + amount: token.formattedDecimalBalance, + symbol: token.symbol, + decimals: token.decimals, + amountFiat: null, + icon: token.iconUrl || 'assets/icons/cep18-contract-icon.svg' + })) .sort((a, b) => { const first = a.amount.split(',').join(''); const second = b.amount.split(',').join(''); diff --git a/src/apps/popup/pages/import-account-from-ledger/connected-ledger.tsx b/src/apps/popup/pages/import-account-from-ledger/connected-ledger.tsx index b08952d8a..2b83073be 100644 --- a/src/apps/popup/pages/import-account-from-ledger/connected-ledger.tsx +++ b/src/apps/popup/pages/import-account-from-ledger/connected-ledger.tsx @@ -1,14 +1,10 @@ import { Player } from '@lottiefiles/react-lottie-player'; import React, { useEffect, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; -import { isEqualCaseInsensitive } from '@src/utils'; - import { RouterPath, useTypedNavigate } from '@popup/router'; -import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; import { dispatchToMainStore } from '@background/redux/utils'; import { accountsImported } from '@background/redux/vault/actions'; @@ -28,7 +24,7 @@ import { SpacingSize, VerticalSpaceContainer } from '@libs/layout'; -import { dispatchFetchAccountBalances } from '@libs/services/balance-service'; +import { useFetchAccountsBalances } from '@libs/services/balance-service'; import { LedgerAccount, LedgerEventStatus, @@ -73,10 +69,6 @@ export const ConnectedLedger: React.FC = ({ const navigate = useTypedNavigate(); const isDarkMode = useIsDarkMode(); - const { casperWalletApiUrl } = useSelector( - selectApiConfigBasedOnActiveNetwork - ); - useEffect(() => { ledger.getAccountList({ size: 5, offset: 0 }); }, []); @@ -93,48 +85,30 @@ export const ConnectedLedger: React.FC = ({ return () => sub.unsubscribe(); }, []); - useEffect(() => { - if (!accountsFromLedger.length) return; + const accountHashes = accountsFromLedger.map(account => + getAccountHashFromPublicKey(account.publicKey) + ); - const hashes = accountsFromLedger.reduce( - (previousValue, currentValue, currentIndex) => { - const hash = getAccountHashFromPublicKey(currentValue.publicKey); + const { accountsBalances, isLoadingBalances } = + useFetchAccountsBalances(accountHashes); - return accountsFromLedger.length === currentIndex + 1 - ? previousValue + `${hash}` - : previousValue + `${hash},`; - }, - '' + useEffect(() => { + if (!accountsFromLedger.length || isLoadingBalances) return; + + const accountsWithBalance = accountsFromLedger.map( + account => ({ + publicKey: account.publicKey, + derivationIndex: account.index, + name: '', + id: account.publicKey + }) ); - dispatchFetchAccountBalances(hashes) - .then(({ payload }) => { - if ('data' in payload) { - const accountsWithBalance = - accountsFromLedger.map(account => { - const accountWithBalance = payload.data.find(ac => - isEqualCaseInsensitive(ac.public_key, account.publicKey) - ); - - return { - publicKey: account.publicKey, - derivationIndex: account.index, - name: '', - id: account.publicKey, - balance: { - liquidMotes: `${accountWithBalance?.balance ?? '0'}` - } - }; - }); - - setLedgerAccountsWithBalance(accountsWithBalance); - } - }) - .finally(() => { - setIsLoading(false); - setIsLoadingMore(false); - }); - }, [casperWalletApiUrl, accountsFromLedger]); + setLedgerAccountsWithBalance(accountsWithBalance); + + setIsLoading(false); + setIsLoadingMore(false); + }, [accountsBalances, accountsFromLedger, isLoadingBalances]); const onSubmit = () => { const accounts: Account[] = selectedAccounts.map(account => ({ @@ -232,6 +206,7 @@ export const ConnectedLedger: React.FC = ({ onLoadMore={onLoadMore} isLoadingMore={isLoadingMore} namePrefix="Ledger account" + accountsBalances={accountsBalances} /> )} diff --git a/src/apps/popup/pages/import-account-from-ledger/types.ts b/src/apps/popup/pages/import-account-from-ledger/types.ts index bae38cb3e..9bf6d14d0 100644 --- a/src/apps/popup/pages/import-account-from-ledger/types.ts +++ b/src/apps/popup/pages/import-account-from-ledger/types.ts @@ -1,6 +1,6 @@ -import { AccountWithBalance } from '@libs/types/account'; +import { Account } from '@libs/types/account'; export type ILedgerAccountListItem = Omit< - AccountWithBalance, + Account, 'hidden' | 'secretKey' | 'imported' | 'hardware' > & { id: string }; diff --git a/src/apps/popup/pages/navigation-menu/index.tsx b/src/apps/popup/pages/navigation-menu/index.tsx index ac068c39a..fc5377a18 100644 --- a/src/apps/popup/pages/navigation-menu/index.tsx +++ b/src/apps/popup/pages/navigation-menu/index.tsx @@ -3,6 +3,12 @@ import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import styled from 'styled-components'; +import { + ABOUT_US_URL, + SHARE_FEEDBACK_URL, + TERMS_URLS, + USER_GUIDES_URL +} from '@src/constants'; import { isLedgerAvailable, isSafariBuild } from '@src/utils'; import { TimeoutDurationSetting } from '@popup/constants'; @@ -320,23 +326,37 @@ export function NavigationMenuPageContent() { items: [ { id: 1, + title: t('Terms & Conditions'), + iconPath: 'assets/icons/books.svg', + href: TERMS_URLS.tos, + disabled: false + }, + { + id: 2, + title: t('Privacy Policy'), + iconPath: 'assets/icons/books.svg', + href: TERMS_URLS.privacy, + disabled: false + }, + { + id: 3, title: t('Share feedback'), iconPath: 'assets/icons/chat.svg', - href: 'https://casper-wallet.canny.io/feature-requests', + href: SHARE_FEEDBACK_URL, disabled: false }, { - id: 2, + id: 4, title: t('User guides'), iconPath: 'assets/icons/books.svg', - href: 'https://casperwallet.io/user-guide', + href: USER_GUIDES_URL, disabled: false }, { - id: 3, + id: 5, title: t('About us'), iconPath: 'assets/icons/team.svg', - href: 'https://make.services/', + href: ABOUT_US_URL, disabled: false } ] diff --git a/src/apps/popup/pages/nft-details/content.tsx b/src/apps/popup/pages/nft-details/content.tsx index f24a017b4..3253b1e05 100644 --- a/src/apps/popup/pages/nft-details/content.tsx +++ b/src/apps/popup/pages/nft-details/content.tsx @@ -154,7 +154,7 @@ export const NftDetailsContent = ({ ); if (extendedDeployInfo) { - if (extendedDeployInfo.status === Status.Executed) { + if (extendedDeployInfo.status !== Status.Pending) { dispatchToMainStore( accountTrackingIdOfSentNftTokensRemoved(nftToken?.tracking_id!) ); diff --git a/src/apps/popup/pages/receive/content.tsx b/src/apps/popup/pages/receive/content.tsx index 55bd830b9..a4308deff 100644 --- a/src/apps/popup/pages/receive/content.tsx +++ b/src/apps/popup/pages/receive/content.tsx @@ -1,16 +1,12 @@ import { QRCodeSVG } from 'qrcode.react'; import React, { useEffect, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { shallowEqual, useSelector } from 'react-redux'; +import { useSelector } from 'react-redux'; import styled from 'styled-components'; -import { formatErc20TokenBalance } from '@popup/pages/home/components/tokens-list/utils'; +import { formatCep18Tokens } from '@popup/pages/home/components/tokens-list/utils'; import { useTypedLocation } from '@popup/router'; -import { - selectAccountBalance, - selectErc20Tokens -} from '@background/redux/account-info/selectors'; import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; import { useCopyToClipboard } from '@hooks/use-copy-to-clipboard'; @@ -22,6 +18,8 @@ import { SpacingSize, VerticalSpaceContainer } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; +import { useFetchCep18Tokens } from '@libs/services/cep18-service'; import { ActiveAccountPlate, Tile, Typography } from '@libs/ui/components'; import { motesToCSPR } from '@libs/ui/utils'; @@ -57,22 +55,24 @@ export const ReceivePageContent = () => { symbol: location?.state?.tokenData?.symbol ?? '' }); - const csprBalance = useSelector(selectAccountBalance, shallowEqual); - const tokens = useSelector(selectErc20Tokens, shallowEqual); + const { accountBalance } = useFetchWalletBalance(); + const { cep18Tokens } = useFetchCep18Tokens(); useEffect(() => { if (tokenData?.symbol === 'CSPR') { const balance = - (csprBalance.liquidMotes && motesToCSPR(csprBalance.liquidMotes)) || + (accountBalance.liquidBalance && + motesToCSPR(accountBalance.liquidBalance)) || '0'; setTokenData(prev => ({ ...prev, balance })); } else { - const erc20Tokens = formatErc20TokenBalance(tokens); + const formatedCep18Tokens = formatCep18Tokens(cep18Tokens); const balance = - erc20Tokens?.find(t => t?.symbol === tokenData?.symbol)?.amount ?? '0'; + formatedCep18Tokens?.find(t => t?.symbol === tokenData?.symbol) + ?.amount ?? '0'; setTokenData(prev => ({ ...prev, balance })); } - }, [csprBalance.liquidMotes, tokenData?.symbol, tokens]); + }, [accountBalance.liquidBalance, tokenData?.symbol, cep18Tokens]); return ( diff --git a/src/apps/popup/pages/stakes/amount-step.tsx b/src/apps/popup/pages/stakes/amount-step.tsx index 204dbe3e7..5ae05f39d 100644 --- a/src/apps/popup/pages/stakes/amount-step.tsx +++ b/src/apps/popup/pages/stakes/amount-step.tsx @@ -2,7 +2,6 @@ import Big from 'big.js'; import React, { useEffect, useState } from 'react'; import { UseFormReturn, useWatch } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; import { @@ -11,17 +10,13 @@ import { STAKE_COST_MOTES } from '@src/constants'; -import { - selectAccountBalance, - selectAccountCurrencyRate -} from '@background/redux/account-info/selectors'; - import { AlignedFlexRow, ParagraphContainer, SpacingSize, VerticalSpaceContainer } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { Error, Input, Typography } from '@libs/ui/components'; import { StakeAmountFormValues } from '@libs/ui/forms/stakes-form'; import { @@ -54,22 +49,20 @@ export const AmountStep = ({ const { t } = useTranslation(); - const currencyRate = useSelector(selectAccountCurrencyRate); - const csprBalance = useSelector(selectAccountBalance); + const { accountBalance, currencyRate } = useFetchWalletBalance(); useEffect(() => { switch (stakeType) { case AuctionManagerEntryPoint.delegate: { - const maxAmountMotes: string = - csprBalance.liquidMotes == null - ? '0' - : Big(csprBalance.liquidMotes).sub(STAKE_COST_MOTES).toFixed(); + const maxAmountMotes: string = !accountBalance.liquidBalance + ? '0' + : Big(accountBalance.liquidBalance).sub(STAKE_COST_MOTES).toFixed(); const minAmount = Big(STAKE_COST_MOTES) .add(DELEGATION_MIN_AMOUNT_MOTES) .toFixed(); const hasEnoughBalance = - csprBalance.liquidMotes != null && - Big(csprBalance.liquidMotes).gte(minAmount); + accountBalance.liquidBalance && + Big(accountBalance.liquidBalance).gte(minAmount); setDisabled(!hasEnoughBalance); @@ -79,14 +72,14 @@ export const AmountStep = ({ case AuctionManagerEntryPoint.undelegate: case AuctionManagerEntryPoint.redelegate: { const hasEnoughBalance = - csprBalance.liquidMotes != null && - Big(csprBalance.liquidMotes).gte(STAKE_COST_MOTES); + accountBalance.liquidBalance && + Big(accountBalance.liquidBalance).gte(STAKE_COST_MOTES); setDisabled(!hasEnoughBalance); setMaxAmountMotes(stakeAmountMotes); } } - }, [csprBalance.liquidMotes, stakeAmountMotes, stakeType]); + }, [accountBalance.liquidBalance, stakeAmountMotes, stakeType]); const { register, @@ -103,7 +96,10 @@ export const AmountStep = ({ const amountLabel = t('Amount'); - const fiatAmount = formatFiatAmount(amount || '0', currencyRate); + const fiatAmount = formatFiatAmount( + amount || '0', + currencyRate?.rate || null + ); return ( <> diff --git a/src/apps/popup/pages/stakes/confirm-step.tsx b/src/apps/popup/pages/stakes/confirm-step.tsx index c2aad3e22..11497a18f 100644 --- a/src/apps/popup/pages/stakes/confirm-step.tsx +++ b/src/apps/popup/pages/stakes/confirm-step.tsx @@ -1,13 +1,10 @@ import Big from 'big.js'; import React from 'react'; import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; import { AuctionManagerEntryPoint } from '@src/constants'; -import { selectAccountCurrencyRate } from '@background/redux/account-info/selectors'; - import { AmountContainer, ParagraphContainer, @@ -15,6 +12,7 @@ import { SpacingSize, VerticalSpaceContainer } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { getAuctionManagerDeployCost } from '@libs/services/deployer-service'; import { ValidatorResult } from '@libs/services/validators-service/types'; import { List, Typography, ValidatorPlate } from '@libs/ui/components'; @@ -40,7 +38,7 @@ export const ConfirmStep = ({ }: ConfirmStepProps) => { const { t } = useTranslation(); - const currencyRate = useSelector(selectAccountCurrencyRate); + const { currencyRate } = useFetchWalletBalance(); const transferFeeMotes = getAuctionManagerDeployCost(stakeType); @@ -58,14 +56,18 @@ export const ConfirmStep = ({ amount: formatNumber(inputAmountCSPR, { precision: { max: 5 } }), - fiatPrice: formatFiatAmount(inputAmountCSPR, currencyRate), + fiatPrice: formatFiatAmount(inputAmountCSPR, currencyRate?.rate || null), symbol: 'CSPR' }, { id: 2, text: t('Transaction fee'), amount: transferCostInCSPR, - fiatPrice: formatFiatAmount(transferCostInCSPR, currencyRate, 3), + fiatPrice: formatFiatAmount( + transferCostInCSPR, + currencyRate?.rate || null, + 3 + ), symbol: 'CSPR' }, { @@ -74,7 +76,7 @@ export const ConfirmStep = ({ amount: formatNumber(totalCSPR, { precision: { max: 5 } }), - fiatPrice: formatFiatAmount(totalCSPR, currencyRate), + fiatPrice: formatFiatAmount(totalCSPR, currencyRate?.rate || null), symbol: 'CSPR', bold: true } diff --git a/src/apps/popup/pages/stakes/index.tsx b/src/apps/popup/pages/stakes/index.tsx index bb1b32d65..3b485048f 100644 --- a/src/apps/popup/pages/stakes/index.tsx +++ b/src/apps/popup/pages/stakes/index.tsx @@ -24,7 +24,6 @@ import { ValidatorDropdownInput } from '@popup/pages/stakes/validator-dropdown-i import { RouterPath, useTypedNavigate } from '@popup/router'; import { accountPendingDeployHashesChanged } from '@background/redux/account-info/actions'; -import { selectAccountBalance } from '@background/redux/account-info/selectors'; import { ledgerDeployChanged } from '@background/redux/ledger/actions'; import { selectAskForReviewAfter, @@ -55,6 +54,7 @@ import { VerticalSpaceContainer, createErrorLocationState } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { makeAuctionManagerDeploy, sendSignDeploy, @@ -111,13 +111,14 @@ export const StakesPage = () => { const { networkName, nodeUrl, auctionManagerContractHash } = useSelector( selectApiConfigBasedOnActiveNetwork ); - const csprBalance = useSelector(selectAccountBalance); const ratedInStore = useSelector(selectRatedInStore); const askForReviewAfter = useSelector(selectAskForReviewAfter); const { t } = useTranslation(); const navigate = useTypedNavigate(); + const { accountBalance } = useFetchWalletBalance(); + const { stakeType, validatorList, undelegateValidatorList, loading } = useStakeType(); @@ -130,7 +131,7 @@ export const StakesPage = () => { ); const { amountForm, validatorForm, newValidatorForm } = useStakesForm( - csprBalance.liquidMotes, + accountBalance.liquidBalance, stakeType, stakeAmountMotes, validator?.delegators_number, @@ -538,7 +539,8 @@ export const StakesPage = () => { if ( (stakeType === AuctionManagerEntryPoint.undelegate || stakeType === AuctionManagerEntryPoint.redelegate) && - (csprBalance.delegatedMotes == null || csprBalance.delegatedMotes === '0') + (!accountBalance.delegatedBalance || + accountBalance.delegatedBalance === '0') ) { return ( = ({ - erc20Tokens -}) => { +export const TokenPageContent: React.FC = () => { const { t } = useTranslation(); const { tokenName } = useParams(); @@ -34,7 +27,7 @@ export const TokenPageContent: React.FC = ({ Token - + Activity diff --git a/src/apps/popup/pages/token-details/index.tsx b/src/apps/popup/pages/token-details/index.tsx index c1de5a5c7..f4d554218 100644 --- a/src/apps/popup/pages/token-details/index.tsx +++ b/src/apps/popup/pages/token-details/index.tsx @@ -1,22 +1,21 @@ import React from 'react'; -import { useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; -import { selectErc20Tokens } from '@background/redux/account-info/selectors'; - import { HeaderPopup, HeaderSubmenuBarNavLink, HeaderViewInExplorer, PopupLayout } from '@libs/layout'; +import { useFetchCep18Tokens } from '@libs/services/cep18-service'; import { TokenPageContent } from './content'; export const TokenDetailPage = () => { - const erc20Tokens = useSelector(selectErc20Tokens); const { tokenName } = useParams(); + const { cep18Tokens } = useFetchCep18Tokens(); + return ( ( @@ -29,13 +28,13 @@ export const TokenDetailPage = () => { )} /> )} - renderContent={() => } + renderContent={() => } /> ); }; diff --git a/src/apps/popup/pages/token-details/token.tsx b/src/apps/popup/pages/token-details/token.tsx index 4d02143c4..c3b1ffd6d 100644 --- a/src/apps/popup/pages/token-details/token.tsx +++ b/src/apps/popup/pages/token-details/token.tsx @@ -6,7 +6,7 @@ import styled from 'styled-components'; import { NetworkSetting } from '@src/constants'; -import { formatErc20TokenBalance } from '@popup/pages/home/components/tokens-list/utils'; +import { formatCep18Tokens } from '@popup/pages/home/components/tokens-list/utils'; import { RouterPath, useTypedLocation, useTypedNavigate } from '@popup/router'; import { @@ -23,7 +23,7 @@ import { SpaceBetweenFlexRow, SpacingSize } from '@libs/layout'; -import { ContractPackageWithBalance } from '@libs/services/erc20-service'; +import { useFetchCep18Tokens } from '@libs/services/cep18-service'; import { Button, List, @@ -52,11 +52,7 @@ type TokenInfoList = { value: string; }; -type TokenProps = { - erc20Tokens: ContractPackageWithBalance[] | null; -}; - -export const Token = ({ erc20Tokens }: TokenProps) => { +export const Token = () => { const location = useTypedLocation(); const [tokenData, setTokenData] = useState( location.state.tokenData ?? null @@ -71,6 +67,7 @@ export const Token = ({ erc20Tokens }: TokenProps) => { const { casperLiveUrl } = useSelector(selectApiConfigBasedOnActiveNetwork); const activeAccount = useSelector(selectVaultActiveAccount); const network = useSelector(selectActiveNetworkSetting); + const { cep18Tokens } = useFetchCep18Tokens(); useEffect(() => { if (tokenName === 'Casper') { @@ -82,10 +79,10 @@ export const Token = ({ erc20Tokens }: TokenProps) => { ]); } } else { - // ERC-20 token case - const erc20TokensList = formatErc20TokenBalance(erc20Tokens); + // CEP-18 token case + const formatedCep18Tokens = formatCep18Tokens(cep18Tokens); - const token = erc20TokensList?.find(token => token.id === tokenName); + const token = formatedCep18Tokens?.find(token => token.id === tokenName); if (token) { setTokenData(token); @@ -110,7 +107,7 @@ export const Token = ({ erc20Tokens }: TokenProps) => { casperToken, activeAccount, casperLiveUrl, - erc20Tokens, + cep18Tokens, tokenData?.symbol, tokenData?.decimals ]); diff --git a/src/apps/popup/pages/transfer-nft/index.tsx b/src/apps/popup/pages/transfer-nft/index.tsx index bc2507e73..0b77a4c2d 100644 --- a/src/apps/popup/pages/transfer-nft/index.tsx +++ b/src/apps/popup/pages/transfer-nft/index.tsx @@ -17,10 +17,7 @@ import { accountPendingDeployHashesChanged, accountTrackingIdOfSentNftTokensChanged } from '@background/redux/account-info/actions'; -import { - selectAccountBalance, - selectAccountNftTokens -} from '@background/redux/account-info/selectors'; +import { selectAccountNftTokens } from '@background/redux/account-info/selectors'; import { selectAllContactsPublicKeys } from '@background/redux/contacts/selectors'; import { ledgerDeployChanged, @@ -52,6 +49,7 @@ import { SpacingSize, createErrorLocationState } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { makeNFTDeploy, sendSignDeploy, @@ -87,7 +85,6 @@ export const TransferNftPage = () => { const { contractPackageHash, tokenId } = useParams(); const nftTokens = useSelector(selectAccountNftTokens); - const csprBalance = useSelector(selectAccountBalance); const activeAccount = useSelector(selectVaultActiveAccount); const isActiveAccountFromLedger = useSelector( selectIsActiveAccountFromLedger @@ -99,6 +96,8 @@ export const TransferNftPage = () => { const ratedInStore = useSelector(selectRatedInStore); const askForReviewAfter = useSelector(selectAskForReviewAfter); + const { accountBalance } = useFetchWalletBalance(); + const { t } = useTranslation(); const navigate = useTypedNavigate(); const location = useTypedLocation(); @@ -137,7 +136,7 @@ export const TransferNftPage = () => { ); const { recipientForm, amountForm } = useTransferNftForm( - csprBalance.liquidMotes, + accountBalance.liquidBalance, defaultPaymentAmount ); diff --git a/src/apps/popup/pages/transfer-nft/review-step.tsx b/src/apps/popup/pages/transfer-nft/review-step.tsx index aa22224c0..3f6e2a6bd 100644 --- a/src/apps/popup/pages/transfer-nft/review-step.tsx +++ b/src/apps/popup/pages/transfer-nft/review-step.tsx @@ -1,9 +1,6 @@ import React from 'react'; import { UseFormReturn, useWatch } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; - -import { selectAccountCurrencyRate } from '@background/redux/account-info/selectors'; import { ContentContainer, @@ -11,6 +8,7 @@ import { SpacingSize, VerticalSpaceContainer } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { NFTTokenResult } from '@libs/services/nft-service'; import { Input, Typography } from '@libs/ui/components'; import { TransferNftAmountFormValues } from '@libs/ui/forms/transfer-nft'; @@ -34,7 +32,7 @@ export const ReviewStep = ({ }: ReviewStepProps) => { const { t } = useTranslation(); - const currencyRate = useSelector(selectAccountCurrencyRate); + const { currencyRate } = useFetchWalletBalance(); const { register, @@ -49,7 +47,7 @@ export const ReviewStep = ({ const paymentFiatAmount = formatFiatAmount( paymentAmount || '0', - currencyRate + currencyRate?.rate || null ); return ( diff --git a/src/apps/popup/pages/transfer/amount-step.tsx b/src/apps/popup/pages/transfer/amount-step.tsx index f1d38627d..1a8c132b6 100644 --- a/src/apps/popup/pages/transfer/amount-step.tsx +++ b/src/apps/popup/pages/transfer/amount-step.tsx @@ -6,10 +6,6 @@ import { useSelector } from 'react-redux'; import { TRANSFER_COST_MOTES, TRANSFER_MIN_AMOUNT_MOTES } from '@src/constants'; -import { - selectAccountBalance, - selectAccountCurrencyRate -} from '@background/redux/account-info/selectors'; import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; @@ -21,6 +17,7 @@ import { SpacingSize, VerticalSpaceContainer } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { Error, Input, Typography } from '@libs/ui/components'; import { TransactionFeePlate } from '@libs/ui/components/transaction-fee-plate/transaction-fee-plate'; import { calculateSubmitButtonDisabled } from '@libs/ui/forms/get-submit-button-state-from-validation'; @@ -56,11 +53,11 @@ export const AmountStep = ({ const { t } = useTranslation(); - const currencyRate = useSelector(selectAccountCurrencyRate); - const csprBalance = useSelector(selectAccountBalance); const activeAccount = useSelector(selectVaultActiveAccount); const { networkName } = useSelector(selectApiConfigBasedOnActiveNetwork); + const { accountBalance, currencyRate } = useFetchWalletBalance(); + const erc20Balance = (selectedToken?.balance && divideErc20Balance( @@ -72,22 +69,21 @@ export const AmountStep = ({ const amountForm = useTransferAmountForm( erc20Balance, isErc20Transfer, - csprBalance.liquidMotes, + accountBalance.liquidBalance, paymentAmount, selectedToken?.decimals ); useEffect(() => { - const maxAmountMotes: string = - csprBalance.liquidMotes == null - ? '0' - : Big(csprBalance.liquidMotes).sub(TRANSFER_COST_MOTES).toFixed(); + const maxAmountMotes: string = !accountBalance.liquidBalance + ? '0' + : Big(accountBalance.liquidBalance).sub(TRANSFER_COST_MOTES).toFixed(); const hasEnoughBalance = Big(maxAmountMotes).gte(TRANSFER_MIN_AMOUNT_MOTES); setMaxAmountMotes(maxAmountMotes); setDisabled(!hasEnoughBalance); - }, [csprBalance.liquidMotes]); + }, [accountBalance.liquidBalance]); useEffect(() => { if (!isErc20Transfer) { @@ -109,7 +105,7 @@ export const AmountStep = ({ trigger, formState.touchedFields.amount, selectedToken?.amount, - csprBalance.liquidMotes, + accountBalance.liquidBalance, paymentAmount ]); @@ -131,10 +127,13 @@ export const AmountStep = ({ const transferIdLabel = t('Transfer ID (memo)'); const paymentAmoutLabel = t('Set custom transaction fee'); const fiatAmount = !isErc20Transfer - ? formatFiatAmount(amount || '0', currencyRate) + ? formatFiatAmount(amount || '0', currencyRate?.rate || null) : undefined; const paymentFiatAmount = isErc20Transfer - ? formatFiatAmount(paymentAmountFieldValue || '0', currencyRate) + ? formatFiatAmount( + paymentAmountFieldValue || '0', + currencyRate?.rate || null + ) : undefined; useEffect(() => { diff --git a/src/apps/popup/pages/transfer/confirm-step.tsx b/src/apps/popup/pages/transfer/confirm-step.tsx index 76543f2b8..5a2719c2c 100644 --- a/src/apps/popup/pages/transfer/confirm-step.tsx +++ b/src/apps/popup/pages/transfer/confirm-step.tsx @@ -1,13 +1,10 @@ import Big from 'big.js'; import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; import { TRANSFER_COST_MOTES } from '@src/constants'; -import { selectAccountCurrencyRate } from '@background/redux/account-info/selectors'; - import { getAccountHashFromPublicKey } from '@libs/entities/Account'; import { AmountContainer, @@ -18,6 +15,7 @@ import { VerticalSpaceContainer } from '@libs/layout'; import { useFetchAccountsInfo } from '@libs/services/account-info'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { ActiveAccountPlate, List, @@ -33,7 +31,7 @@ const ListItemContainer = styled(SpaceBetweenFlexRow)` interface ConfirmStepProps { recipientPublicKey: string; amount: string; - balance: string | null; + balance: string | undefined; symbol: string | null; isErc20Transfer: boolean; paymentAmount: string; @@ -50,8 +48,7 @@ export const ConfirmStep = ({ }: ConfirmStepProps) => { const { t } = useTranslation(); - const currencyRate = useSelector(selectAccountCurrencyRate); - + const { currencyRate } = useFetchWalletBalance(); const accountsInfo = useFetchAccountsInfo([recipientPublicKey]); const accountHash = getAccountHashFromPublicKey(recipientPublicKey); @@ -74,14 +71,18 @@ export const ConfirmStep = ({ amount: formatNumber(amount, { precision: { max: 5 } }), - fiatPrice: formatFiatAmount(amount, currencyRate), + fiatPrice: formatFiatAmount(amount, currencyRate?.rate || null), symbol }, { id: 2, text: t('Transaction fee'), amount: transferCostInCSPR, - fiatPrice: formatFiatAmount(transferCostInCSPR, currencyRate, 3), + fiatPrice: formatFiatAmount( + transferCostInCSPR, + currencyRate?.rate || null, + 3 + ), symbol }, { @@ -90,7 +91,7 @@ export const ConfirmStep = ({ amount: formatNumber(totalCSPR, { precision: { max: 5 } }), - fiatPrice: formatFiatAmount(totalCSPR, currencyRate), + fiatPrice: formatFiatAmount(totalCSPR, currencyRate?.rate || null), symbol, bold: true } @@ -112,7 +113,11 @@ export const ConfirmStep = ({ amount: formatNumber(paymentAmount, { precision: { max: 5 } }), - fiatPrice: formatFiatAmount(paymentAmount, currencyRate, 3), + fiatPrice: formatFiatAmount( + paymentAmount, + currencyRate?.rate || null, + 3 + ), symbol: 'CSPR' } ]; diff --git a/src/apps/popup/pages/transfer/index.tsx b/src/apps/popup/pages/transfer/index.tsx index 5ccb09ece..e8ab1827b 100644 --- a/src/apps/popup/pages/transfer/index.tsx +++ b/src/apps/popup/pages/transfer/index.tsx @@ -336,7 +336,7 @@ export const TransferPage = () => { { const tokensList: TokenType[] = []; - if (isLoading) return; + if (isLoadingTokens) return; + const formatedCep18Tokens = formatCep18Tokens(cep18Tokens); if (casperToken) { tokensList.push(casperToken); } - if (tokens) { - tokensList.push(...tokens); + if (formatedCep18Tokens) { + tokensList.push(...formatedCep18Tokens); } setTokenList(tokensList); @@ -61,7 +64,13 @@ export const TokenStep = ({ } else { setSelectedToken(casperToken); } - }, [casperToken, tokens, isLoading]); + }, [ + casperToken, + cep18Tokens, + isLoadingTokens, + selectedToken?.id, + setSelectedToken + ]); return ( @@ -117,7 +126,7 @@ export const TokenStep = ({ diff --git a/src/background/index.ts b/src/background/index.ts index cbefa9e4a..29f98fade 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -29,13 +29,6 @@ import { openOnboardingUi } from '@background/open-onboarding-flow'; import { - accountBalancesChanged, - accountBalancesReseted -} from '@background/redux/account-balances/actions'; -import { - accountBalanceChanged, - accountCurrencyRateChanged, - accountErc20Changed, accountInfoReset, accountNftTokensAdded, accountNftTokensCountChanged, @@ -114,17 +107,11 @@ import { sdkEvent } from '@content/sdk-event'; import { SdkMethod, isSDKMethod, sdkMethod } from '@content/sdk-method'; import { fetchExtendedDeploysInfo } from '@libs/services/account-activity-service'; -import { - fetchAccountBalance, - fetchAccountBalances, - fetchCurrencyRate -} from '@libs/services/balance-service'; import { fetchOnRampOptionGet, fetchOnRampOptionPost, fetchOnRampSelectionPost } from '@libs/services/buy-cspr-service'; -import { fetchErc20Tokens } from '@libs/services/erc20-service'; import { fetchNftTokens } from '@libs/services/nft-service'; import { fetchAuctionValidators, @@ -612,12 +599,9 @@ runtime.onMessage.addListener( case getType(loginRetryLockoutTimeSet): case getType(recipientPublicKeyAdded): case getType(recipientPublicKeyReseted): - case getType(accountBalanceChanged): - case getType(accountCurrencyRateChanged): case getType(accountInfoReset): case getType(accountPendingDeployHashesChanged): case getType(accountPendingDeployHashesRemove): - case getType(accountErc20Changed): case getType(accountNftTokensAdded): case getType(accountNftTokensUpdated): case getType(accountNftTokensCountChanged): @@ -631,8 +615,6 @@ runtime.onMessage.addListener( case getType(ratedInStoreChanged): case getType(askForReviewAfterChanged): case getType(resetRateApp): - case getType(accountBalancesChanged): - case getType(accountBalancesReseted): case getType(ledgerNewWindowIdChanged): case getType(ledgerStateCleared): case getType(ledgerDeployChanged): @@ -648,53 +630,6 @@ runtime.onMessage.addListener( return; // SERVICE MESSAGE HANDLERS - case getType(serviceMessage.fetchBalanceRequest): { - const { casperWalletApiUrl, casperClarityApiUrl } = - selectApiConfigBasedOnActiveNetwork(store.getState()); - - try { - const [accountData, rate] = await Promise.all([ - fetchAccountBalance({ - accountHash: action.payload.accountHash, - casperWalletApiUrl - }), - fetchCurrencyRate({ casperClarityApiUrl }) - ]); - - return sendResponse( - serviceMessage.fetchBalanceResponse({ - accountData: accountData?.data || null, - currencyRate: rate?.data || null - }) - ); - } catch (error) { - console.error(error); - } - - return; - } - - case getType(serviceMessage.fetchAccountBalancesRequest): { - const { casperWalletApiUrl } = selectApiConfigBasedOnActiveNetwork( - store.getState() - ); - - try { - const data = await fetchAccountBalances({ - accountHashes: action.payload.accountHashes, - casperWalletApiUrl - }); - - return sendResponse( - serviceMessage.fetchAccountBalancesResponse(data) - ); - } catch (error) { - console.error(error); - } - - return; - } - case getType(serviceMessage.fetchExtendedDeploysInfoRequest): { const { casperClarityApiUrl } = selectApiConfigBasedOnActiveNetwork( store.getState() @@ -720,39 +655,6 @@ runtime.onMessage.addListener( return; } - case getType(serviceMessage.fetchErc20TokensRequest): { - const { casperClarityApiUrl } = selectApiConfigBasedOnActiveNetwork( - store.getState() - ); - - try { - const { data: tokensList } = await fetchErc20Tokens({ - casperClarityApiUrl, - accountHash: action.payload.accountHash - }); - - if (tokensList) { - const erc20Tokens = tokensList.map(token => ({ - balance: token.balance, - contractHash: token.latest_contract?.contract_hash, - ...(token?.contract_package ?? {}) - })); - - return sendResponse( - serviceMessage.fetchErc20TokensResponse(erc20Tokens) - ); - } else { - return sendResponse( - serviceMessage.fetchErc20TokensResponse([]) - ); - } - } catch (error) { - console.error(error); - } - - return; - } - case getType(serviceMessage.fetchNftTokensRequest): { const { casperClarityApiUrl } = selectApiConfigBasedOnActiveNetwork( store.getState() diff --git a/src/background/redux/account-balances/actions.ts b/src/background/redux/account-balances/actions.ts deleted file mode 100644 index e4926b171..000000000 --- a/src/background/redux/account-balances/actions.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createAction } from 'typesafe-actions'; - -import { AccountData } from '@libs/services/balance-service/types'; - -export const accountBalancesChanged = createAction('ACCOUNT_BALANCES_CHANGED')< - AccountData[] ->(); - -export const accountBalancesReseted = createAction( - 'ACCOUNT_BALANCES_RESETED' -)(); diff --git a/src/background/redux/account-balances/reducer.ts b/src/background/redux/account-balances/reducer.ts deleted file mode 100644 index 044e2af70..000000000 --- a/src/background/redux/account-balances/reducer.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createReducer } from 'typesafe-actions'; - -import { accountBalancesChanged, accountBalancesReseted } from './actions'; -import { AccountBalancesState } from './types'; - -const initialState = [] as AccountBalancesState; - -export const reducer = createReducer(initialState) - .handleAction(accountBalancesReseted, () => initialState) - .handleAction( - accountBalancesChanged, - (state, action: ReturnType) => action.payload - ); diff --git a/src/background/redux/account-balances/selectors.ts b/src/background/redux/account-balances/selectors.ts deleted file mode 100644 index fcd061275..000000000 --- a/src/background/redux/account-balances/selectors.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { RootState } from 'typesafe-actions'; - -export const selectAccountBalances = (state: RootState) => - state.accountBalances; diff --git a/src/background/redux/account-balances/types.ts b/src/background/redux/account-balances/types.ts deleted file mode 100644 index f97a24ed1..000000000 --- a/src/background/redux/account-balances/types.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { AccountData } from '@libs/services/balance-service/types'; - -export type AccountBalancesState = AccountData[]; diff --git a/src/background/redux/account-info/actions.ts b/src/background/redux/account-info/actions.ts index 8e7e3f6c0..1d572a6fa 100644 --- a/src/background/redux/account-info/actions.ts +++ b/src/background/redux/account-info/actions.ts @@ -1,21 +1,7 @@ import { createAction } from 'typesafe-actions'; -import { AccountBalance } from '@libs/services/balance-service/types'; -import { ContractPackageWithBalance } from '@libs/services/erc20-service/types'; import { NFTTokenResult } from '@libs/services/nft-service/types'; -export const accountBalanceChanged = createAction( - 'ACCOUNT_BALANCE_CHANGED' -)(); - -export const accountErc20Changed = createAction('ACCOUNT_ERC20_CHANGED')< - ContractPackageWithBalance[] ->(); - -export const accountCurrencyRateChanged = createAction( - 'ACCOUNT_CURRENCY_RATE_CHANGED' -)(); - export const accountInfoReset = createAction('ACCOUNT_INFO_RESET')(); export const accountPendingDeployHashesChanged = createAction( diff --git a/src/background/redux/account-info/reducer.ts b/src/background/redux/account-info/reducer.ts index 9eb129dee..f0b1f1c89 100644 --- a/src/background/redux/account-info/reducer.ts +++ b/src/background/redux/account-info/reducer.ts @@ -3,9 +3,6 @@ import { createReducer } from 'typesafe-actions'; import { isEqualCaseInsensitive } from '@src/utils'; import { - accountBalanceChanged, - accountCurrencyRateChanged, - accountErc20Changed, accountInfoReset, accountNftTokensAdded, accountNftTokensCountChanged, @@ -18,16 +15,7 @@ import { import { AccountInfoState } from './types'; const initialState: AccountInfoState = { - balance: { - liquidMotes: null, - delegatedMotes: null, - undelegatingMotes: null, - totalBalanceMotes: null, - totalBalanceFiat: null - }, - currencyRate: null, pendingDeployHashes: [], - erc20Tokens: [], accountNftTokens: [], nftTokensCount: 0, accountTrackingIdOfSentNftTokens: {} @@ -35,24 +23,6 @@ const initialState: AccountInfoState = { export const reducer = createReducer(initialState) .handleAction(accountInfoReset, () => initialState) - .handleAction(accountBalanceChanged, (state, { payload }) => ({ - ...state, - balance: payload - })) - .handleAction( - accountCurrencyRateChanged, - (state, { payload }): AccountInfoState => ({ - ...state, - currencyRate: payload - }) - ) - .handleAction( - accountErc20Changed, - (state, { payload }): AccountInfoState => ({ - ...state, - erc20Tokens: payload - }) - ) .handleAction(accountPendingDeployHashesChanged, (state, { payload }) => { return { ...state, diff --git a/src/background/redux/account-info/selectors.ts b/src/background/redux/account-info/selectors.ts index 6008be84a..c4bcee00d 100644 --- a/src/background/redux/account-info/selectors.ts +++ b/src/background/redux/account-info/selectors.ts @@ -1,17 +1,8 @@ import { RootState } from 'typesafe-actions'; -export const selectAccountBalance = (state: RootState) => - state.accountInfo.balance; - -export const selectAccountCurrencyRate = (state: RootState) => - state.accountInfo.currencyRate; - export const selectPendingDeployHashes = (state: RootState) => state.accountInfo.pendingDeployHashes; -export const selectErc20Tokens = (state: RootState) => - state.accountInfo.erc20Tokens; - export const selectAccountNftTokens = (state: RootState) => state.accountInfo.accountNftTokens; diff --git a/src/background/redux/account-info/types.ts b/src/background/redux/account-info/types.ts index 6d0d3ab1f..24ad0b53a 100644 --- a/src/background/redux/account-info/types.ts +++ b/src/background/redux/account-info/types.ts @@ -1,11 +1,6 @@ -import { AccountBalance } from '@libs/services/balance-service'; -import { ContractPackageWithBalance } from '@libs/services/erc20-service'; import { NFTTokenResult } from '@libs/services/nft-service'; export interface AccountInfoState { - balance: AccountBalance; - erc20Tokens: ContractPackageWithBalance[]; - currencyRate: number | null; pendingDeployHashes: string[]; accountNftTokens: NFTTokenResult[] | null; nftTokensCount: number; diff --git a/src/background/redux/get-main-store.ts b/src/background/redux/get-main-store.ts index f79f7c562..33e8eb8a5 100644 --- a/src/background/redux/get-main-store.ts +++ b/src/background/redux/get-main-store.ts @@ -58,7 +58,6 @@ export const selectPopupState = (state: RootState): PopupState => { accountInfo: state.accountInfo, contacts: state.contacts, rateApp: state.rateApp, - accountBalances: state.accountBalances, ledger: state.ledger, promotion: state.promotion }; diff --git a/src/background/redux/redux-action.ts b/src/background/redux/redux-action.ts index 7ddc20f6f..48a260502 100644 --- a/src/background/redux/redux-action.ts +++ b/src/background/redux/redux-action.ts @@ -1,6 +1,5 @@ import { ActionType } from 'typesafe-actions'; -import * as accountBalances from './account-balances/actions'; import * as accountInfo from './account-info/actions'; import * as activeOrigin from './active-origin/actions'; import * as contacts from './contacts/actions'; @@ -35,7 +34,6 @@ const reduxAction = { accountInfo, contacts, rateApp, - accountBalances, ledger, promotion }; diff --git a/src/background/redux/root-reducer.ts b/src/background/redux/root-reducer.ts index d3d6a3aa6..aea14de02 100644 --- a/src/background/redux/root-reducer.ts +++ b/src/background/redux/root-reducer.ts @@ -1,6 +1,5 @@ import { combineReducers } from 'redux'; -import { reducer as accountBalances } from './account-balances/reducer'; import { reducer as accountInfo } from './account-info/reducer'; import { reducer as activeOrigin } from './active-origin/reducer'; import { reducer as contacts } from './contacts/reducer'; @@ -33,7 +32,6 @@ const rootReducer = combineReducers({ accountInfo, contacts, rateApp, - accountBalances, ledger, promotion }); diff --git a/src/background/redux/sagas/vault-sagas.ts b/src/background/redux/sagas/vault-sagas.ts index 15d8dcee4..567644534 100644 --- a/src/background/redux/sagas/vault-sagas.ts +++ b/src/background/redux/sagas/vault-sagas.ts @@ -8,7 +8,6 @@ import { MapTimeoutDurationSettingToValue } from '@popup/constants'; -import { accountBalancesReseted } from '@background/redux/account-balances/actions'; import { loginRetryLockoutTimeReseted, loginRetryLockoutTimeSet @@ -120,7 +119,6 @@ function* lockVaultSaga() { yield put(vaultReseted()); yield put(deploysReseted()); yield put(accountInfoReset()); - yield put(accountBalancesReseted()); emitSdkEventToActiveTabs(() => { return sdkEvent.lockedEvent({ diff --git a/src/background/redux/types.d.ts b/src/background/redux/types.d.ts index efc28c05e..a708694cc 100644 --- a/src/background/redux/types.d.ts +++ b/src/background/redux/types.d.ts @@ -1,6 +1,5 @@ import { ActionType, StateType } from 'typesafe-actions'; -import { AccountBalancesState } from '@background/redux/account-balances/types'; import { AccountInfoState } from '@background/redux/account-info/types'; import { ActiveOriginState } from '@background/redux/active-origin/types'; import { ContactsState } from '@background/redux/contacts/types'; @@ -50,7 +49,6 @@ export type PopupState = { accountInfo: AccountInfoState; contacts: ContactsState; rateApp: RateAppState; - accountBalances: AccountBalancesState; ledger: LedgerState; promotion: PromotionState; }; diff --git a/src/background/redux/vault/selectors.ts b/src/background/redux/vault/selectors.ts index 1fb369709..09506eade 100644 --- a/src/background/redux/vault/selectors.ts +++ b/src/background/redux/vault/selectors.ts @@ -1,15 +1,10 @@ import { createSelector } from 'reselect'; import { RootState } from 'typesafe-actions'; -import { selectAccountBalances } from '@background/redux/account-balances/selectors'; import { VaultState } from '@background/redux/vault/types'; import { SecretPhrase } from '@libs/crypto'; -import { - Account, - AccountWithBalance, - HardwareWalletType -} from '@libs/types/account'; +import { Account, HardwareWalletType } from '@libs/types/account'; import { selectActiveOrigin } from '../active-origin/selectors'; @@ -27,35 +22,12 @@ export const selectVaultAccounts = (state: RootState): Account[] => export const selectVaultAccountsPublicKeys = (state: RootState): string[] => state.vault.accounts.map(key => key.publicKey); -export const selectVaultAccountsWithBalances = createSelector( - selectVaultAccounts, - selectAccountBalances, - (accounts, accountBalances): AccountWithBalance[] => - accounts.map(account => { - if (!accountBalances.length) { - return { - ...account, - balance: { - liquidMotes: null - } - }; - } - - const accountBalance = accountBalances.find( - ac => ac.public_key === account.publicKey - ); - - return { - ...account, - balance: { - liquidMotes: String(accountBalance?.balance || '0') - } - }; - }) -); +export const selectVaultAccountsExceptLedgersAccounts = ( + state: RootState +): Account[] => state.vault.accounts.filter(account => !account.hardware); export const selectVaultCountsOfAccounts = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, accounts => accounts.length ); @@ -63,12 +35,12 @@ export const selectVaultHasAccounts = (state: RootState): boolean => state.vault.accounts.length > 0; export const selectVaultAccountsNames = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, accounts => accounts.map(account => account.name) ); export const selectVaultImportedAccounts = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, accounts => accounts.filter(account => account.imported) ); @@ -78,12 +50,12 @@ export const selectVaultImportedAccountNames = createSelector( ); export const selectVaultVisibleAccounts = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, accounts => accounts.filter(account => !account.hidden) ); export const selectVaultHiddenAccounts = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, accounts => accounts.filter(account => account.hidden) ); @@ -98,12 +70,12 @@ export const selectVaultHasImportedAccount = createSelector( ); export const selectVaultDerivedAccounts = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, accounts => accounts.filter(account => !account.imported && !account.hardware) ); export const selectVaultLedgerAccounts = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, accounts => accounts.filter(account => account.hardware === HardwareWalletType.Ledger) ); @@ -114,12 +86,12 @@ export const selectVaultLedgerAccountNames = createSelector( ); export const selectVaultAccountsSecretKeysBase64 = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, accounts => accounts.map(account => account.secretKey) ); export const selectVaultAccount = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, (_: RootState, accountName: string) => accountName, (accounts, accountName) => accounts.find(account => account.name === accountName) @@ -129,7 +101,7 @@ export const selectVaultActiveAccountName = (state: RootState) => state.vault.activeAccountName; export const selectVaultActiveAccount = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, selectVaultActiveAccountName, (accounts, activeAccountName) => { return accounts.find(account => account.name === activeAccountName); @@ -222,19 +194,19 @@ export const selectConnectedAccountNamesWithActiveOrigin = createSelector( export const selectConnectedAccountsWithActiveOrigin = createSelector( selectActiveOrigin, - selectVaultAccountsWithBalances, + selectVaultAccounts, selectConnectedAccountNamesWithActiveOrigin, - (origin, accounts, connectedAccountNamesWithOrigin): AccountWithBalance[] => { + (origin, accounts, connectedAccountNamesWithOrigin): Account[] => { return (connectedAccountNamesWithOrigin || []) .map(accountName => accounts.find(account => account.name === accountName) ) - .filter((account): account is AccountWithBalance => !!account); + .filter((account): account is Account => !!account); } ); export const selectUnconnectedAccountsWithActiveOrigin = createSelector( - selectVaultAccountsWithBalances, + selectVaultAccounts, selectConnectedAccountsWithActiveOrigin, (accounts, connectedAccountsToActiveTab) => accounts.filter( diff --git a/src/background/service-message.ts b/src/background/service-message.ts index e91c66159..dc9c4a8f8 100644 --- a/src/background/service-message.ts +++ b/src/background/service-message.ts @@ -1,10 +1,6 @@ import { IDeploy } from 'casper-wallet-core'; import { ActionType, createAction } from 'typesafe-actions'; -import { - AccountData, - FetchBalanceResponse -} from '@libs/services/balance-service/types'; import { GetOnRampResponse, OptionsPostRequestData, @@ -12,7 +8,6 @@ import { ResponseSelectionProps, SelectionPostRequestData } from '@libs/services/buy-cspr-service/types'; -import { ContractPackageWithBalance } from '@libs/services/erc20-service/types'; import { NFTTokenResult } from '@libs/services/nft-service/types'; import { ErrorResponse, PaginatedResponse } from '@libs/services/types'; import { @@ -23,22 +18,6 @@ import { type Meta = void; export const serviceMessage = { - fetchBalanceRequest: createAction('FETCH_ACCOUNT_BALANCE')< - { accountHash: string }, - Meta - >(), - fetchBalanceResponse: createAction('FETCH_ACCOUNT_BALANCE_RESPONSE')< - FetchBalanceResponse, - Meta - >(), - fetchAccountBalancesRequest: createAction('FETCH_ACCOUNT_BALANCES')< - { accountHashes: string }, - Meta - >(), - fetchAccountBalancesResponse: createAction('FETCH_ACCOUNT_BALANCES_RESPONSE')< - PaginatedResponse | ErrorResponse, - Meta - >(), fetchExtendedDeploysInfoRequest: createAction('FETCH_EXTENDED_DEPLOYS_INFO')< { deployHash: string; publicKey: string }, Meta @@ -46,14 +25,6 @@ export const serviceMessage = { fetchExtendedDeploysInfoResponse: createAction( 'FETCH_EXTENDED_DEPLOYS_INFO_RESPONSE' )(), - fetchErc20TokensRequest: createAction('FETCH_ERC20_TOKENS')< - { accountHash: string }, - Meta - >(), - fetchErc20TokensResponse: createAction('FETCH_ERC20_TOKENS_RESPONSE')< - ContractPackageWithBalance[], - Meta - >(), fetchNftTokensRequest: createAction('FETCH_NFT_TOKENS')< { accountHash: string; page: number }, Meta diff --git a/src/background/wallet-repositories.ts b/src/background/wallet-repositories.ts index ea4e8b05d..901479d47 100644 --- a/src/background/wallet-repositories.ts +++ b/src/background/wallet-repositories.ts @@ -1,5 +1,6 @@ import { setupRepositories } from 'casper-wallet-core'; -const { deploysRepository, accountInfoRepository } = setupRepositories(); +const { deploysRepository, accountInfoRepository, tokensRepository } = + setupRepositories(); -export { deploysRepository, accountInfoRepository }; +export { deploysRepository, accountInfoRepository, tokensRepository }; diff --git a/src/constants.ts b/src/constants.ts index 918356617..a52b7fc0c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,17 +1,10 @@ const SECOND = 1000; -export const FETCH_QUERY_OPTIONS = { - // cached for 30 sec - apiCacheTime: 30 * SECOND -}; - export const BALANCE_REFRESH_RATE = 15 * SECOND; export const CURRENCY_REFRESH_RATE = 30 * SECOND; export const TOKENS_REFRESH_RATE = 15 * SECOND; export const NFT_TOKENS_REFRESH_RATE = 60 * SECOND; -export const ACCOUNT_DEPLOY_REFRESH_RATE = 30 * SECOND; export const ACCOUNT_CASPER_ACTIVITY_REFRESH_RATE = 30 * SECOND; -export const ERC20_TOKEN_ACTIVITY_REFRESH_RATE = 30 * SECOND; export const VALIDATORS_REFRESH_RATE = 30 * SECOND; export const PENDING_DEPLOY_REFETCH_INTERVAL = 5 * SECOND; export const DEPLOY_DETAILS_REFRESH_RATE = 30 * SECOND; @@ -29,6 +22,16 @@ export const STAKE_COST_MOTES = '2500000000'; // 2.5 CSPR export const DELEGATION_MIN_AMOUNT_MOTES = '500000000000'; // 500 CSPR export const MAX_DELEGATORS = 1200; +export const USER_GUIDES_URL = 'https://casperwallet.io/user-guide'; +export const SHARE_FEEDBACK_URL = + 'https://casper-wallet.canny.io/feature-requests'; +export const ABOUT_US_URL = 'https://make.services'; +export const TERMS_URLS = { + tos: 'https://www.casperwallet.io/tos', + privacy: 'https://www.casperwallet.io/privacy' +}; +export const REFERRER_URL = 'https://casperwallet.io'; + export const getBlockExplorerAccountUrl = ( casperLiveUrl: string, publicKey: string @@ -84,18 +87,11 @@ export enum Browser { Edge = 'edge' } -export enum TermsLink { - Tos = 'https://www.casperwallet.io/tos', - Privacy = 'https://www.casperwallet.io/privacy' -} - export enum CasperNodeUrl { MainnetUrl = 'https://node.cspr.cloud/rpc', TestnetUrl = 'https://node.testnet.cspr.cloud/rpc' } -export const ReferrerUrl = 'https://casperwallet.io'; - export enum NetworkName { Mainnet = 'casper', Testnet = 'casper-test' diff --git a/src/fixtures/initial-state-for-popup-tests.ts b/src/fixtures/initial-state-for-popup-tests.ts index 35b1094ad..8894f950d 100644 --- a/src/fixtures/initial-state-for-popup-tests.ts +++ b/src/fixtures/initial-state-for-popup-tests.ts @@ -91,16 +91,7 @@ export const initialStateForPopupTests: RootState = { }, recentRecipientPublicKeys: [], accountInfo: { - balance: { - liquidMotes: null, - delegatedMotes: null, - undelegatingMotes: null, - totalBalanceMotes: null, - totalBalanceFiat: null - }, - currencyRate: null, pendingDeployHashes: [], - erc20Tokens: [], accountNftTokens: [], nftTokensCount: 0, accountTrackingIdOfSentNftTokens: {} @@ -113,7 +104,6 @@ export const initialStateForPopupTests: RootState = { ratedInStore: false, askForReviewAfter: null }, - accountBalances: [], promotion: { showCSPRNamePromotion: true } diff --git a/src/hooks/use-active-account-erc20-tokens.ts b/src/hooks/use-active-account-erc20-tokens.ts deleted file mode 100644 index a6588542b..000000000 --- a/src/hooks/use-active-account-erc20-tokens.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { useEffect, useRef, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; - -import { BALANCE_REFRESH_RATE } from '@src/constants'; - -import { useForceUpdate } from '@popup/hooks/use-force-update'; -import { formatErc20TokenBalance } from '@popup/pages/home/components/tokens-list/utils'; - -import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; -import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; - -import { TokenType } from '@hooks/use-casper-token'; - -import { getAccountHashFromPublicKey } from '@libs/entities/Account'; -import { dispatchFetchErc20TokensRequest } from '@libs/services/erc20-service'; - -/** - * Will get all active account erc20 tokens with automatic refresh - */ -export const useActiveAccountErc20Tokens = () => { - const effectTimeoutRef = useRef(); - const forceUpdate = useForceUpdate(); - const [tokens, setTokens] = useState(undefined); - const [isLoading, setIsLoading] = useState(true); - const { t } = useTranslation(); - - const activeAccount = useSelector(selectVaultActiveAccount); - const { casperClarityApiUrl } = useSelector( - selectApiConfigBasedOnActiveNetwork - ); - - useEffect(() => { - setIsLoading(true); - dispatchFetchErc20TokensRequest( - getAccountHashFromPublicKey(activeAccount?.publicKey) - ) - .then(({ payload: erc20Tokens }) => { - const erc20TokensList = formatErc20TokenBalance(erc20Tokens); - - setTokens(erc20TokensList); - }) - .catch(error => { - console.error('Balance request failed:', error); - }) - .finally(() => setIsLoading(false)); - - // will cause effect to run again after timeout - effectTimeoutRef.current = setTimeout(() => { - forceUpdate(); - }, BALANCE_REFRESH_RATE + 1); - - return () => { - clearTimeout(effectTimeoutRef.current); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeAccount?.publicKey, casperClarityApiUrl, t, forceUpdate]); - - return { tokens, isLoading }; -}; diff --git a/src/hooks/use-casper-token.ts b/src/hooks/use-casper-token.ts index d5f8ad23e..2765e4871 100644 --- a/src/hooks/use-casper-token.ts +++ b/src/hooks/use-casper-token.ts @@ -1,18 +1,8 @@ import { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; -import { - selectAccountBalance, - selectAccountCurrencyRate -} from '@background/redux/account-info/selectors'; - -import { - formatCurrency, - formatNumber, - motesToCSPR, - motesToCurrency -} from '@libs/ui/utils'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; +// TODO: review this in future and maybe remove and use type from casper wallet core export type TokenType = { id: string; contractHash?: string; @@ -28,37 +18,22 @@ export type TokenType = { export const useCasperToken = () => { const [casperToken, setCasperToken] = useState(null); - const balance = useSelector(selectAccountBalance); - const currencyRate = useSelector(selectAccountCurrencyRate); - - const amount = - balance?.liquidMotes == null - ? '-' - : formatNumber(motesToCSPR(balance.liquidMotes), { - precision: { max: 5 } - }); - const amountFiat = - currencyRate != null && balance?.liquidMotes != null - ? formatCurrency( - motesToCurrency(String(balance.liquidMotes), currencyRate), - 'USD', - { - precision: 2 - } - ) - : ''; + const { accountBalance } = useFetchWalletBalance(); useEffect(() => { setCasperToken({ id: 'Casper', contractHash: undefined, name: 'Casper', - amount, - amountFiat: amountFiat, + amount: accountBalance.liquidFormattedDecimalBalance || '-', + amountFiat: accountBalance.liquidFormattedFiatBalance, symbol: 'CSPR', icon: '/assets/icons/casper.svg' }); - }, [amount, amountFiat]); + }, [ + accountBalance.liquidFormattedDecimalBalance, + accountBalance.liquidFormattedFiatBalance + ]); return casperToken; }; diff --git a/src/hooks/use-fetch-account-balances.ts b/src/hooks/use-fetch-account-balances.ts deleted file mode 100644 index 0500c8d28..000000000 --- a/src/hooks/use-fetch-account-balances.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { useEffect, useRef } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; - -import { BALANCE_REFRESH_RATE } from '@src/constants'; - -import { useForceUpdate } from '@popup/hooks/use-force-update'; - -import { accountBalancesChanged } from '@background/redux/account-balances/actions'; -import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; -import { dispatchToMainStore } from '@background/redux/utils'; -import { selectVaultAccounts } from '@background/redux/vault/selectors'; - -import { getAccountHashFromPublicKey } from '@libs/entities/Account'; -import { dispatchFetchAccountBalances } from '@libs/services/balance-service'; - -export const useFetchAccountBalances = () => { - const effectTimeoutRef = useRef(); - const forceUpdate = useForceUpdate(); - const { t } = useTranslation(); - - const { casperClarityApiUrl } = useSelector( - selectApiConfigBasedOnActiveNetwork - ); - const accounts = useSelector(selectVaultAccounts); - - const hashes = accounts.reduce( - (previousValue, currentValue, currentIndex) => { - const hash = getAccountHashFromPublicKey(currentValue.publicKey); - - return accounts.length === currentIndex + 1 - ? previousValue + `${hash}` - : previousValue + `${hash},`; - }, - '' - ); - - useEffect(() => { - dispatchFetchAccountBalances(hashes) - .then(({ payload }) => { - if ('data' in payload) { - dispatchToMainStore(accountBalancesChanged(payload.data)); - } else { - dispatchToMainStore(accountBalancesChanged([])); - } - }) - .catch(error => { - console.error(t('Failed to fetch accounts balance'), error); - }); - - // will cause effect to run again after timeout - effectTimeoutRef.current = setTimeout(() => { - forceUpdate(); - }, BALANCE_REFRESH_RATE + 1); - - return () => { - clearTimeout(effectTimeoutRef.current); - }; - }, [casperClarityApiUrl, forceUpdate, t, hashes]); -}; diff --git a/src/hooks/use-fetch-active-account-balance.ts b/src/hooks/use-fetch-active-account-balance.ts deleted file mode 100644 index 3968d437d..000000000 --- a/src/hooks/use-fetch-active-account-balance.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { useEffect, useRef } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; - -import { BALANCE_REFRESH_RATE } from '@src/constants'; - -import { useForceUpdate } from '@popup/hooks/use-force-update'; - -import { - accountBalanceChanged, - accountCurrencyRateChanged -} from '@background/redux/account-info/actions'; -import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; -import { dispatchToMainStore } from '@background/redux/utils'; -import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; - -import { getAccountHashFromPublicKey } from '@libs/entities/Account'; -import { dispatchFetchActiveAccountBalance } from '@libs/services/balance-service'; -import { formatCurrency, motesToCurrency } from '@libs/ui/utils'; - -export const useFetchActiveAccountBalance = () => { - const effectTimeoutRef = useRef(); - const forceUpdate = useForceUpdate(); - const { t } = useTranslation(); - - const activeAccount = useSelector(selectVaultActiveAccount); - const { casperClarityApiUrl } = useSelector( - selectApiConfigBasedOnActiveNetwork - ); - - useEffect(() => { - if (!activeAccount?.publicKey) return; - - const accountHash = getAccountHashFromPublicKey(activeAccount.publicKey); - - dispatchFetchActiveAccountBalance(accountHash) - .then(({ payload: { accountData, currencyRate } }) => { - if (accountData != null) { - const liquidBalance = accountData?.balance || 0; - const delegatedBalance = accountData?.delegated_balance || 0; - const undelegatingBalance = accountData?.undelegating_balance || 0; - - const totalAmountMotes = - liquidBalance + delegatedBalance + undelegatingBalance; - const totalBalanceFiat = - currencyRate != null - ? formatCurrency( - motesToCurrency(String(totalAmountMotes), currencyRate), - 'USD', - { - precision: 2 - } - ) - : t('Currency service is offline...'); - - dispatchToMainStore(accountCurrencyRateChanged(currencyRate)); - dispatchToMainStore( - accountBalanceChanged({ - liquidMotes: String(liquidBalance), - delegatedMotes: String(delegatedBalance), - undelegatingMotes: String(undelegatingBalance), - totalBalanceMotes: String(totalAmountMotes), - totalBalanceFiat: totalBalanceFiat - }) - ); - } else { - dispatchToMainStore(accountCurrencyRateChanged(currencyRate)); - dispatchToMainStore( - accountBalanceChanged({ - liquidMotes: null, - undelegatingMotes: null, - delegatedMotes: null, - totalBalanceMotes: null, - totalBalanceFiat: null - }) - ); - } - }) - .catch(error => { - console.error('Balance request failed:', error); - }); - - // will cause effect to run again after timeout - effectTimeoutRef.current = setTimeout(() => { - forceUpdate(); - }, BALANCE_REFRESH_RATE + 1); - - return () => { - clearTimeout(effectTimeoutRef.current); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeAccount?.publicKey, casperClarityApiUrl, t, forceUpdate]); -}; diff --git a/src/hooks/use-fetch-erc20-tokens.ts b/src/hooks/use-fetch-erc20-tokens.ts deleted file mode 100644 index 897322364..000000000 --- a/src/hooks/use-fetch-erc20-tokens.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; - -import { accountErc20Changed } from '@background/redux/account-info/actions'; -import { selectErc20Tokens } from '@background/redux/account-info/selectors'; -import { selectApiConfigBasedOnActiveNetwork } from '@background/redux/settings/selectors'; -import { dispatchToMainStore } from '@background/redux/utils'; -import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; - -import { getAccountHashFromPublicKey } from '@libs/entities/Account'; -import { - ContractPackageWithBalance, - dispatchFetchErc20TokensRequest -} from '@libs/services/erc20-service'; - -export const useFetchErc20Tokens = () => { - const activeAccount = useSelector(selectVaultActiveAccount); - const { casperClarityApiUrl } = useSelector( - selectApiConfigBasedOnActiveNetwork - ); - const tokens = useSelector(selectErc20Tokens); - - const [erc20Tokens, setErc20Tokens] = useState< - ContractPackageWithBalance[] | null - >(tokens); - - useEffect(() => { - if (!activeAccount?.publicKey) return; - - dispatchFetchErc20TokensRequest( - getAccountHashFromPublicKey(activeAccount.publicKey) - ).then(({ payload: tokens }) => { - dispatchToMainStore(accountErc20Changed(tokens)); - setErc20Tokens(tokens); - }); - }, [activeAccount?.publicKey, casperClarityApiUrl]); - - return erc20Tokens; -}; diff --git a/src/libs/layout/header/header-connection-status.tsx b/src/libs/layout/header/header-connection-status.tsx index 1f1ce3af0..839c02459 100644 --- a/src/libs/layout/header/header-connection-status.tsx +++ b/src/libs/layout/header/header-connection-status.tsx @@ -1,7 +1,10 @@ import React from 'react'; import { useSelector } from 'react-redux'; -import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; +import { + selectVaultAccountsPublicKeys, + selectVaultActiveAccount +} from '@background/redux/vault/selectors'; import { getAccountHashFromPublicKey } from '@libs/entities/Account'; import { @@ -30,8 +33,9 @@ export function HeaderConnectionStatus({ isConnected }: HeaderConnectionStatusProps) { const activeAccount = useSelector(selectVaultActiveAccount); + const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); - const accountsInfo = useFetchAccountsInfo([activeAccount?.publicKey!]); + const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); const accountHash = getAccountHashFromPublicKey(activeAccount?.publicKey); diff --git a/src/libs/layout/header/header-data-updater.tsx b/src/libs/layout/header/header-data-updater.tsx index 76a57ef3a..99730cbbb 100644 --- a/src/libs/layout/header/header-data-updater.tsx +++ b/src/libs/layout/header/header-data-updater.tsx @@ -1,13 +1,11 @@ import React from 'react'; -import { useFetchAccountBalances } from '@hooks/use-fetch-account-balances'; -import { useFetchActiveAccountBalance } from '@hooks/use-fetch-active-account-balance'; -import { useFetchErc20Tokens } from '@hooks/use-fetch-erc20-tokens'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; +import { useFetchCep18Tokens } from '@libs/services/cep18-service'; export const HeaderDataUpdater: React.FC = () => { - useFetchActiveAccountBalance(); - useFetchErc20Tokens(); - useFetchAccountBalances(); + useFetchWalletBalance(); + useFetchCep18Tokens(); return null; }; diff --git a/src/libs/layout/header/header-submenu-bar-nav-link.tsx b/src/libs/layout/header/header-submenu-bar-nav-link.tsx index 8108ffeea..e5c44e825 100644 --- a/src/libs/layout/header/header-submenu-bar-nav-link.tsx +++ b/src/libs/layout/header/header-submenu-bar-nav-link.tsx @@ -1,17 +1,15 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; import { RouterPath } from '@popup/router/paths'; import { useTypedNavigate } from '@popup/router/use-typed-navigate'; import { closeCurrentWindow } from '@background/close-current-window'; -import { selectAccountBalance } from '@background/redux/account-info/selectors'; import { AlignedFlexRow, SpacingSize } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { Link, SvgIcon, Typography } from '@libs/ui/components'; -import { formatNumber, motesToCSPR } from '@libs/ui/utils/formatters'; const LinkWithIconContainer = styled.div` display: flex; @@ -40,11 +38,7 @@ export function HeaderSubmenuBarNavLink({ const { t } = useTranslation(); const navigate = useTypedNavigate(); - const balance = useSelector(selectAccountBalance); - - const formattedBalance = formatNumber( - (balance.liquidMotes && motesToCSPR(balance.liquidMotes)) || '' - ); + const { accountBalance } = useFetchWalletBalance(); switch (linkType) { case 'close': @@ -81,14 +75,16 @@ export function HeaderSubmenuBarNavLink({ }} withLeftChevronIcon /> - - - Balance: - - - {`${formattedBalance} CSPR`} - - + {accountBalance.liquidFormattedDecimalBalance && ( + + + Balance: + + + {`${accountBalance.liquidFormattedDecimalBalance} CSPR`} + + + )} ) : ( token.contract_package_hash === tokenName + const token = cep18Tokens?.find( + token => token.contractPackageHash === tokenName ); if (token) { setHrefToTokenOnCasperLive( getBlockExplorerContractPackageUrl( casperLiveUrl, - token.contract_package_hash + token.contractPackageHash ) ); } @@ -88,7 +88,7 @@ export function HeaderViewInExplorer({ casperToken, activeAccount, casperLiveUrl, - erc20Tokens, + cep18Tokens, deployHash, nftTokenId, contractHash diff --git a/src/libs/services/account-info/use-fetch-accounts-info.ts b/src/libs/services/account-info/use-fetch-accounts-info.ts index c550555fa..273eb8817 100644 --- a/src/libs/services/account-info/use-fetch-accounts-info.ts +++ b/src/libs/services/account-info/use-fetch-accounts-info.ts @@ -19,7 +19,7 @@ export const useFetchAccountsInfo = (accountPublicKeys: string[]) => { const accountHashesString = accountHashes.toString(); const { data: accountsInfo } = useQuery({ - queryKey: ['ACCOUNT_INFO', accountHashesString], + queryKey: ['ACCOUNT_INFO', accountHashesString, network], queryFn: async () => { return await accountInfoRepository.getAccountsInfo({ accountHashes, diff --git a/src/libs/services/balance-service/balance-service.ts b/src/libs/services/balance-service/balance-service.ts deleted file mode 100644 index 9b6660867..000000000 --- a/src/libs/services/balance-service/balance-service.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { BALANCE_REFRESH_RATE, CURRENCY_REFRESH_RATE } from '@src/constants'; - -import { dispatchToMainStore } from '@background/redux/utils'; -import { serviceMessage } from '@background/service-message'; - -import { - DataResponse, - ErrorResponse, - PaginatedResponse, - Payload -} from '@libs/services/types'; - -import { queryClient } from '../query-client'; -import { handleError, toJson } from '../utils'; -import { - getAccountBalanceUrl, - getAccountBalancesUrl, - getCurrencyRateUrl -} from './constants'; -import { - AccountData, - FetchBalanceResponse, - GetCurrencyRateRequestResponse -} from './types'; - -export const currencyRateRequest = ( - casperClarityApiUrl: string, - signal?: AbortSignal -): Promise => - fetch(getCurrencyRateUrl(casperClarityApiUrl), { signal }) - .then(toJson) - .catch(handleError); - -export const accountBalanceRequest = ( - accountHash: string, - casperWalletApiUrl: string, - signal?: AbortSignal -): Promise => { - if (!accountHash) { - throw Error('Missing account hash'); - } - - return fetch(getAccountBalanceUrl({ accountHash, casperWalletApiUrl }), { - signal - }) - .then(res => { - if (res.status === 404) { - return { - data: { - balance: 0 - } as AccountData - }; - } - - return toJson(res); - }) - .catch(handleError); -}; - -export const accountBalancesRequest = ( - accountHashes: string, - casperWalletApiUrl: string, - signal?: AbortSignal -): Promise | ErrorResponse> => - fetch( - getAccountBalancesUrl({ - accountHashes, - casperWalletApiUrl - }), - { - signal - } - ) - .then(toJson) - .catch(handleError); - -export const dispatchFetchActiveAccountBalance = ( - accountHash = '' -): Promise> => - dispatchToMainStore(serviceMessage.fetchBalanceRequest({ accountHash })); - -export const dispatchFetchAccountBalances = ( - accountHashes = '' -): Promise | ErrorResponse>> => - dispatchToMainStore( - serviceMessage.fetchAccountBalancesRequest({ accountHashes }) - ); - -export const fetchAccountBalance = ({ - accountHash, - casperWalletApiUrl -}: { - accountHash: string; - casperWalletApiUrl: string; -}): Promise> => - queryClient.fetchQuery( - ['getAccountBalanceRequest', accountHash, casperWalletApiUrl], - ({ signal }) => - accountBalanceRequest(accountHash, casperWalletApiUrl, signal), - { - staleTime: BALANCE_REFRESH_RATE - } - ); - -export const fetchCurrencyRate = ({ - casperClarityApiUrl -}: { - casperClarityApiUrl: string; -}) => - queryClient.fetchQuery( - 'getCurrencyRateRequest', - ({ signal }) => currencyRateRequest(casperClarityApiUrl, signal), - { - staleTime: CURRENCY_REFRESH_RATE - } - ); - -export const fetchAccountBalances = ({ - accountHashes, - casperWalletApiUrl -}: { - accountHashes: string; - casperWalletApiUrl: string; -}) => - queryClient.fetchQuery( - ['getAccountBalancesRequest', accountHashes, casperWalletApiUrl], - ({ signal }) => - accountBalancesRequest(accountHashes, casperWalletApiUrl, signal), - { - staleTime: BALANCE_REFRESH_RATE - } - ); diff --git a/src/libs/services/balance-service/constants.ts b/src/libs/services/balance-service/constants.ts index 44cc59b0a..33bf11a9f 100644 --- a/src/libs/services/balance-service/constants.ts +++ b/src/libs/services/balance-service/constants.ts @@ -1,24 +1,8 @@ -interface GetAccountBalanceUrl { - accountHash: string; - casperWalletApiUrl: string; -} - -interface GetAccountBalancesUrl { - accountHashes: string; - casperWalletApiUrl: string; -} +import { ICsprBalance } from 'casper-wallet-core/src/domain/tokens'; -export const getCurrencyRateUrl = (casperClarityApiUrl: string) => - `${casperClarityApiUrl}/rates/1/amount`; +type AccountsBalances = Record | undefined; -export const getAccountBalanceUrl = ({ - accountHash, - casperWalletApiUrl -}: GetAccountBalanceUrl) => - `${casperWalletApiUrl}/accounts/${accountHash}?includes=delegated_balance,undelegating_balance`; - -export const getAccountBalancesUrl = ({ - accountHashes, - casperWalletApiUrl -}: GetAccountBalancesUrl) => - `${casperWalletApiUrl}/accounts?account_hash=${accountHashes}&page=1&page_size=100&includes=delegated_balance,undelegating_balance`; +export interface UseFetchAccountsBalances { + accountsBalances: AccountsBalances; + isLoadingBalances: boolean; +} diff --git a/src/libs/services/balance-service/index.ts b/src/libs/services/balance-service/index.ts index b7d524836..f929e3a8f 100644 --- a/src/libs/services/balance-service/index.ts +++ b/src/libs/services/balance-service/index.ts @@ -1,3 +1,2 @@ -export * from './balance-service'; -export * from './types'; -export * from './constants'; +export * from './use-fetch-accounts-balances'; +export * from './use-fetch-wallet-balance'; diff --git a/src/libs/services/balance-service/queries.ts b/src/libs/services/balance-service/queries.ts new file mode 100644 index 000000000..56a00a91c --- /dev/null +++ b/src/libs/services/balance-service/queries.ts @@ -0,0 +1,68 @@ +import { CasperNetwork } from 'casper-wallet-core/src/domain/common/common'; + +import { + BALANCE_REFRESH_RATE, + CURRENCY_REFRESH_RATE, + NetworkSetting +} from '@src/constants'; + +import { + accountInfoRepository, + tokensRepository +} from '@background/wallet-repositories'; + +import { Account } from '@libs/types/account'; + +interface AccountBalanceQueryProps { + network: NetworkSetting; + activeAccount: Account | undefined; +} + +interface CurrencyRateQueryProps { + network: NetworkSetting; +} + +interface AccountBalancesQueryProps { + accountHashesString: string; + network: NetworkSetting; + accountHashes: string[]; +} + +export const accountBalanceQuery = ({ + network, + activeAccount +}: AccountBalanceQueryProps) => ({ + queryKey: ['ACCOUNT_BALANCE', network, activeAccount?.publicKey], + queryFn: () => + tokensRepository.getCsprBalance({ + publicKey: activeAccount?.publicKey || '', + network: network.toLowerCase() as CasperNetwork + }), + refetchInterval: BALANCE_REFRESH_RATE, + staleTime: BALANCE_REFRESH_RATE +}); + +export const currencyRateQuery = ({ network }: CurrencyRateQueryProps) => ({ + queryKey: ['CURRENCY_RATE', network], + queryFn: () => + tokensRepository.getCsprFiatCurrencyRate({ + network: network.toLowerCase() as CasperNetwork + }), + refetchInterval: CURRENCY_REFRESH_RATE, + staleTime: CURRENCY_REFRESH_RATE +}); + +export const accountsBalancesQuery = ({ + accountHashesString, + network, + accountHashes +}: AccountBalancesQueryProps) => ({ + queryKey: ['ACCOUNTS_BALANCES', accountHashesString, network], + queryFn: async () => + accountInfoRepository.getAccountsBalances({ + accountHashes: accountHashes, + network: network.toLowerCase() as CasperNetwork + }), + refetchInterval: BALANCE_REFRESH_RATE, + staleTime: BALANCE_REFRESH_RATE +}); diff --git a/src/libs/services/balance-service/types.ts b/src/libs/services/balance-service/types.ts deleted file mode 100644 index 867c1a87e..000000000 --- a/src/libs/services/balance-service/types.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface GetCurrencyRateRequestResponse { - data: number; -} - -export interface AccountData { - account_hash: string; - balance: number; - delegated_balance?: number; - main_purse_uref: string; - public_key: string; - undelegating_balance?: number; -} - -export interface FetchBalanceResponse { - currencyRate: number | null; - accountData: AccountData | null; -} - -export interface AccountBalance { - liquidMotes: string | null; - delegatedMotes: string | null; - undelegatingMotes: string | null; - totalBalanceMotes: string | null; - totalBalanceFiat: string | null; -} diff --git a/src/libs/services/balance-service/use-fetch-accounts-balances.ts b/src/libs/services/balance-service/use-fetch-accounts-balances.ts new file mode 100644 index 000000000..8270a4e26 --- /dev/null +++ b/src/libs/services/balance-service/use-fetch-accounts-balances.ts @@ -0,0 +1,28 @@ +import { useQuery } from '@tanstack/react-query'; +import { useSelector } from 'react-redux'; + +import { selectActiveNetworkSetting } from '@background/redux/settings/selectors'; + +import { UseFetchAccountsBalances } from '@libs/services/balance-service/constants'; +import { accountsBalancesQuery } from '@libs/services/balance-service/queries'; + +export const useFetchAccountsBalances = ( + accountHashes: string[] +): UseFetchAccountsBalances => { + const network = useSelector(selectActiveNetworkSetting); + + const accountHashesString = accountHashes.toString(); + + const { data: accountsBalances, isFetching: isLoadingBalances } = useQuery( + accountsBalancesQuery({ + network, + accountHashes, + accountHashesString + }) + ); + + return { + accountsBalances, + isLoadingBalances + }; +}; diff --git a/src/libs/services/balance-service/use-fetch-wallet-balance.ts b/src/libs/services/balance-service/use-fetch-wallet-balance.ts new file mode 100644 index 000000000..76da2ee4f --- /dev/null +++ b/src/libs/services/balance-service/use-fetch-wallet-balance.ts @@ -0,0 +1,123 @@ +import { useQueries } from '@tanstack/react-query'; +import { useSelector } from 'react-redux'; + +import { selectActiveNetworkSetting } from '@background/redux/settings/selectors'; +import { + selectVaultAccounts, + selectVaultActiveAccount +} from '@background/redux/vault/selectors'; + +import { getAccountHashFromPublicKey } from '@libs/entities/Account'; +import { + accountBalanceQuery, + accountsBalancesQuery, + currencyRateQuery +} from '@libs/services/balance-service/queries'; +import { formatCurrency, motesToCurrency } from '@libs/ui/utils'; + +export const useFetchWalletBalance = () => { + const activeAccount = useSelector(selectVaultActiveAccount); + const network = useSelector(selectActiveNetworkSetting); + const accounts = useSelector(selectVaultAccounts); + + const accountHashes = accounts.map(account => + getAccountHashFromPublicKey(account.publicKey) + ); + + const accountHashesString = accountHashes.toString(); + + const { + accountBalance, + isLoadingBalance, + currencyRate, + accountsBalances, + isLoadingBalances + } = useQueries({ + queries: [ + accountBalanceQuery({ + network, + activeAccount + }), + currencyRateQuery({ + network + }), + accountsBalancesQuery({ + accountHashesString, + network, + accountHashes + }) + ], + combine: results => { + const accountBalance = results[0].data; + const totalBalance = results[0].data?.totalBalance; + const liquidBalance = results[0].data?.liquidBalance; + const delegatedBalance = results[0].data?.delegatedBalance; + const undelegatingBalance = results[0].data?.undelegatingBalance; + const currencyRate = results[1].data?.rate; + const accountsBalances = results[2].data; + + const totalFormattedFiatBalance = + currencyRate && totalBalance + ? formatCurrency( + motesToCurrency(String(totalBalance), currencyRate), + 'USD', + { + precision: 2 + } + ) + : ''; + const liquidFormattedFiatBalance = + currencyRate && liquidBalance + ? formatCurrency( + motesToCurrency(String(liquidBalance), currencyRate), + 'USD', + { + precision: 2 + } + ) + : ''; + const delegatedFormattedFiatBalance = + currencyRate && delegatedBalance + ? formatCurrency( + motesToCurrency(String(delegatedBalance), currencyRate), + 'USD', + { + precision: 2 + } + ) + : ''; + const undelegatedFormattedFiatBalance = + currencyRate && undelegatingBalance + ? formatCurrency( + motesToCurrency(String(undelegatingBalance), currencyRate), + 'USD', + { + precision: 2 + } + ) + : ''; + + return { + accountBalance: { + ...accountBalance, + totalFormattedFiatBalance, + liquidFormattedFiatBalance, + delegatedFormattedFiatBalance, + undelegatedFormattedFiatBalance + }, + isLoadingBalance: results[0].isLoading, + currencyRate: results[1].data, + accountsBalances, + isLoadingBalances: results[2].isLoading + }; + } + }); + + return { + accountBalance, + isLoadingBalance, + currencyRate, + accountsBalances, + isLoadingBalances + }; +}; diff --git a/src/libs/services/cep18-service/index.ts b/src/libs/services/cep18-service/index.ts new file mode 100644 index 000000000..75ca6c85e --- /dev/null +++ b/src/libs/services/cep18-service/index.ts @@ -0,0 +1 @@ +export * from './use-fetch-cep18-tokens'; diff --git a/src/libs/services/cep18-service/use-fetch-cep18-tokens.ts b/src/libs/services/cep18-service/use-fetch-cep18-tokens.ts new file mode 100644 index 000000000..87c32bced --- /dev/null +++ b/src/libs/services/cep18-service/use-fetch-cep18-tokens.ts @@ -0,0 +1,27 @@ +import { useQuery } from '@tanstack/react-query'; +import { CasperNetwork } from 'casper-wallet-core/src/domain/common/common'; +import { useSelector } from 'react-redux'; + +import { TOKENS_REFRESH_RATE } from '@src/constants'; + +import { selectActiveNetworkSetting } from '@background/redux/settings/selectors'; +import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; +import { tokensRepository } from '@background/wallet-repositories'; + +export const useFetchCep18Tokens = () => { + const activeAccount = useSelector(selectVaultActiveAccount); + const network = useSelector(selectActiveNetworkSetting); + + const { data: cep18Tokens, isFetching: isLoadingTokens } = useQuery({ + queryKey: ['CEP18_TOKENS', network, activeAccount?.publicKey], + queryFn: () => + tokensRepository.getTokens({ + network: network.toLowerCase() as CasperNetwork, + publicKey: activeAccount?.publicKey || '' + }), + refetchInterval: TOKENS_REFRESH_RATE, + staleTime: TOKENS_REFRESH_RATE + }); + + return { cep18Tokens, isLoadingTokens }; +}; diff --git a/src/libs/services/deployer-service/index.ts b/src/libs/services/deployer-service/index.ts index 3f622926d..c97b42706 100644 --- a/src/libs/services/deployer-service/index.ts +++ b/src/libs/services/deployer-service/index.ts @@ -13,18 +13,14 @@ import { AuctionManagerEntryPoint, CasperNodeUrl, NetworkName, - ReferrerUrl, + REFERRER_URL, STAKE_COST_MOTES, TRANSFER_COST_MOTES } from '@src/constants'; import { getRawPublicKey } from '@libs/entities/Account'; import { toJson } from '@libs/services/utils'; -import { - Account, - AccountWithBalance, - HardwareWalletType -} from '@libs/types/account'; +import { Account, HardwareWalletType } from '@libs/types/account'; import { CSPRtoMotes, multiplyErc20Balance } from '@libs/ui/utils'; import { ledger } from '../ledger'; @@ -55,7 +51,7 @@ export const getDateForDeploy = async (nodeUrl: CasperNodeUrl) => { const casperNodeTimestamp: ICasperNodeStatusResponse = await fetch( `${nodeUrl}/info_get_status`, { - referrer: ReferrerUrl + referrer: REFERRER_URL } ).then(toJson); @@ -119,7 +115,7 @@ export const makeAuctionManagerDeploy = async ( }; export const makeNativeTransferDeploy = async ( - activeAccount: AccountWithBalance, + activeAccount: Account, recipientPublicKeyHex: string, amountMotes: string, networkName: NetworkName, @@ -278,7 +274,7 @@ export const sendSignDeploy = ( return fetch(nodeUrl, { method: 'POST', - referrer: ReferrerUrl, + referrer: REFERRER_URL, body: JSON.stringify({ jsonrpc: '2.0', method: 'account_put_deploy', diff --git a/src/libs/services/erc20-service/constants.ts b/src/libs/services/erc20-service/constants.ts deleted file mode 100644 index 604bf4611..000000000 --- a/src/libs/services/erc20-service/constants.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const getErc20TokensUrl = ( - casperClarityApiUrl: string, - accountHash: string -) => - `${casperClarityApiUrl}/accounts/${accountHash}/erc20-tokens?fields=latest_contract,contract_package`; diff --git a/src/libs/services/erc20-service/erc20-service.ts b/src/libs/services/erc20-service/erc20-service.ts deleted file mode 100644 index c1e71fa21..000000000 --- a/src/libs/services/erc20-service/erc20-service.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { TOKENS_REFRESH_RATE } from '@src/constants'; - -import { dispatchToMainStore } from '@background/redux/utils'; -import { serviceMessage } from '@background/service-message'; - -import { queryClient } from '@libs/services/query-client'; -import { DataResponse, Payload } from '@libs/services/types'; -import { handleError, toJson } from '@libs/services/utils'; - -import { getErc20TokensUrl } from './constants'; -import { ContractPackageWithBalance, Erc20Token } from './types'; - -export const erc20TokensRequest = ( - casperClarityApiUrl: string, - accountHash: string, - signal?: AbortSignal -): Promise> => - fetch(getErc20TokensUrl(casperClarityApiUrl, accountHash), { signal }) - .then(toJson) - .catch(handleError); - -export const fetchErc20Tokens = ({ - casperClarityApiUrl, - accountHash -}: { - casperClarityApiUrl: string; - accountHash: string; -}) => - queryClient.fetchQuery( - ['getErc20Tokens', accountHash, casperClarityApiUrl], - ({ signal }) => - erc20TokensRequest(casperClarityApiUrl, accountHash, signal), - { staleTime: TOKENS_REFRESH_RATE } - ); - -export const dispatchFetchErc20TokensRequest = ( - accountHash: string -): Promise> => - dispatchToMainStore(serviceMessage.fetchErc20TokensRequest({ accountHash })); diff --git a/src/libs/services/erc20-service/index.ts b/src/libs/services/erc20-service/index.ts deleted file mode 100644 index 9f9e834c9..000000000 --- a/src/libs/services/erc20-service/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './erc20-service'; -export * from './types'; -export * from './constants'; diff --git a/src/libs/services/erc20-service/types.ts b/src/libs/services/erc20-service/types.ts deleted file mode 100644 index 3cdce250c..000000000 --- a/src/libs/services/erc20-service/types.ts +++ /dev/null @@ -1,37 +0,0 @@ -export interface Erc20Token { - account_hash: string; - balance: string; - contract_package_hash: string; - latest_contract?: { - contract_hash: string; - contract_package_hash: string; - deploy_hash: string; - contract_type_id: number; - contract_version: number; - is_disabled: boolean; - protocol_version: string; - timestamp: string; - }; - contract_package: ContractPackage; -} - -export interface ContractPackage { - contract_description: string | null; - contract_name: string; - contract_package_hash: string; - contract_type_id: number; - icon_url: string | null; - metadata: { - balances_uref: string; - decimals: number; - symbol: string; - total_supply_uref: string; - }; - owner_public_key: string; - timestamp: string; - contractHash?: string; -} - -export interface ContractPackageWithBalance extends ContractPackage { - balance: string; -} diff --git a/src/libs/types/account.ts b/src/libs/types/account.ts index f5f6a57d0..82a5883a6 100644 --- a/src/libs/types/account.ts +++ b/src/libs/types/account.ts @@ -14,13 +14,7 @@ export enum HardwareWalletType { Ledger = 'Ledger' } -export interface AccountWithBalance extends Account { - balance: { - liquidMotes: string | null; - }; -} - -export interface AccountListRows extends AccountWithBalance { +export interface AccountListRows extends Account { id: string; } diff --git a/src/libs/ui/components/account-list/account-list-item.tsx b/src/libs/ui/components/account-list/account-list-item.tsx index d90ed69b0..d7f7b9e80 100644 --- a/src/libs/ui/components/account-list/account-list-item.tsx +++ b/src/libs/ui/components/account-list/account-list-item.tsx @@ -56,6 +56,8 @@ interface AccountListItemProps { showHideAccountItem?: boolean; closeModal?: (e: React.MouseEvent) => void; accountsInfo: Record | undefined; + accountLiquidBalance: string | undefined; + isLoadingBalance: boolean; } export const AccountListItem = ({ @@ -65,14 +67,15 @@ export const AccountListItem = ({ isConnected, showHideAccountItem, closeModal, - accountsInfo + accountsInfo, + accountLiquidBalance, + isLoadingBalance }: AccountListItemProps) => { - const accountBalance = - account.balance?.liquidMotes != null - ? formatNumber(motesToCSPR(account.balance.liquidMotes), { - precision: { max: 0 } - }) - : '-'; + const accountBalance = accountLiquidBalance + ? formatNumber(motesToCSPR(accountLiquidBalance), { + precision: { max: 0 } + }) + : '0'; const csprName = accountsInfo && accountsInfo[account.accountHash]?.csprName; const brandingLogo = @@ -106,7 +109,7 @@ export const AccountListItem = ({ {accountBalance} diff --git a/src/libs/ui/components/account-list/account-list.tsx b/src/libs/ui/components/account-list/account-list.tsx index d76c669d4..89921ff01 100644 --- a/src/libs/ui/components/account-list/account-list.tsx +++ b/src/libs/ui/components/account-list/account-list.tsx @@ -21,6 +21,7 @@ import { useWindowManager } from '@hooks/use-window-manager'; import { getAccountHashFromPublicKey } from '@libs/entities/Account'; import { FlexColumn, SpacingSize } from '@libs/layout'; import { useFetchAccountsInfo } from '@libs/services/account-info'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { AccountListRowWithAccountHash, AccountListRows @@ -56,6 +57,7 @@ export const AccountList = ({ closeModal }: AccountListProps) => { const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); + const { accountsBalances, isLoadingBalance } = useFetchWalletBalance(); useEffect(() => { const accountListRows = sortAccounts( @@ -82,6 +84,10 @@ export const AccountList = ({ closeModal }: AccountListProps) => { const isConnected = connectedAccountNames.includes(account.name); const isActiveAccount = activeAccountName === account.name; + const accountLiquidBalance = + accountsBalances && + accountsBalances[account.accountHash]?.liquidBalance; + return ( { changeActiveAccount(account.name); closeModal(event); }} + accountLiquidBalance={accountLiquidBalance} accountsInfo={accountsInfo} + isLoadingBalance={isLoadingBalance} /> ); }} diff --git a/src/libs/ui/components/account-list/utils.ts b/src/libs/ui/components/account-list/utils.ts index d794b7637..7b777e8fb 100644 --- a/src/libs/ui/components/account-list/utils.ts +++ b/src/libs/ui/components/account-list/utils.ts @@ -1,7 +1,7 @@ -import { Account, AccountWithBalance } from '@libs/types/account'; +import { Account } from '@libs/types/account'; export function sortAccounts( - accounts: AccountWithBalance[], + accounts: Account[], activeAccountName: string | null, connectedAccountNames: string[] ) { diff --git a/src/libs/ui/components/active-account-plate/active-account-plate.tsx b/src/libs/ui/components/active-account-plate/active-account-plate.tsx index aba7d66cc..0886945e6 100644 --- a/src/libs/ui/components/active-account-plate/active-account-plate.tsx +++ b/src/libs/ui/components/active-account-plate/active-account-plate.tsx @@ -3,7 +3,10 @@ import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import styled from 'styled-components'; -import { selectVaultActiveAccount } from '@background/redux/vault/selectors'; +import { + selectVaultAccountsPublicKeys, + selectVaultActiveAccount +} from '@background/redux/vault/selectors'; import { getAccountHashFromPublicKey } from '@libs/entities/Account'; import { @@ -42,7 +45,7 @@ const Container = styled(TileContainer)` interface ActiveAccountPlateProps { label: string; - balance: string | null; + balance: string | undefined; symbol: string | null; top?: SpacingSize; } @@ -56,11 +59,11 @@ export const ActiveAccountPlate = ({ const { t } = useTranslation(); const activeAccount = useSelector(selectVaultActiveAccount); + const accountsPublicKeys = useSelector(selectVaultAccountsPublicKeys); - const accountsInfo = useFetchAccountsInfo([activeAccount?.publicKey!]); + const accountsInfo = useFetchAccountsInfo(accountsPublicKeys); const accountHash = getAccountHashFromPublicKey(activeAccount?.publicKey); - const csprName = accountsInfo && accountsInfo[accountHash]?.csprName; const brandingLogo = accountsInfo && accountsInfo[accountHash]?.brandingLogo; @@ -107,15 +110,13 @@ export const ActiveAccountPlate = ({ 9 ? balance : undefined - } + title={balance && balance?.length > 9 ? balance : undefined} placement="topLeft" overflowWrap fullWidth > - {balance == null ? '-' : balance} + {balance || '-'} diff --git a/src/libs/ui/components/dynamic-accounts-list-with-select/dynamic-accounts-list-with-select.tsx b/src/libs/ui/components/dynamic-accounts-list-with-select/dynamic-accounts-list-with-select.tsx index 7e4dba9b7..b15cecf37 100644 --- a/src/libs/ui/components/dynamic-accounts-list-with-select/dynamic-accounts-list-with-select.tsx +++ b/src/libs/ui/components/dynamic-accounts-list-with-select/dynamic-accounts-list-with-select.tsx @@ -1,4 +1,5 @@ import { Player } from '@lottiefiles/react-lottie-player'; +import { ICsprBalance } from 'casper-wallet-core/src/domain/tokens'; import React, { useEffect, useState } from 'react'; import { Controller, @@ -23,6 +24,7 @@ import { useIsDarkMode } from '@hooks/use-is-dark-mode'; import dotsDarkModeAnimation from '@libs/animations/dots_dark_mode.json'; import dotsLightModeAnimation from '@libs/animations/dots_light_mode.json'; +import { getAccountHashFromPublicKey } from '@libs/entities/Account'; import { CenteredFlexRow, FlexColumn, @@ -68,6 +70,7 @@ interface ListProps { onLoadMore: () => void; isLoadingMore: boolean; namePrefix: string; + accountsBalances: Record | undefined; } type FormFields = FieldValues & { @@ -83,7 +86,8 @@ export const DynamicAccountsListWithSelect = ({ maxItemsToRender, onLoadMore, isLoadingMore, - namePrefix + namePrefix, + accountsBalances }: ListProps) => { const [accountNames, setAccountNames] = useState<{ name: string }[]>([]); const [checkboxes, setCheckboxes] = useState([]); @@ -152,12 +156,16 @@ export const DynamicAccountsListWithSelect = ({ renderRow={(account, index) => { const inputFieldName = `accountNames.${index}.name`; const checkBoxFieldName = `checkbox.${index}`; - const balance = formatNumber( - motesToCSPR(String(account.balance.liquidMotes)), - { - precision: { max: 0 } - } - ); + const accountHash = getAccountHashFromPublicKey(account.publicKey); + + const accountLiquidBalance = + accountsBalances && accountsBalances[accountHash]?.liquidBalance; + + const accountBalance = accountLiquidBalance + ? formatNumber(motesToCSPR(accountLiquidBalance), { + precision: { max: 0 } + }) + : '0'; const isAlreadyConnected = alreadyConnectedLedgerAccounts.some( alreadyConnectedAccount => @@ -232,7 +240,9 @@ export const DynamicAccountsListWithSelect = ({ 9 ? balance : undefined} + title={ + accountBalance.length > 9 ? accountBalance : undefined + } placement="topLeft" overflowWrap fullWidth @@ -242,7 +252,7 @@ export const DynamicAccountsListWithSelect = ({ style={{ textAlign: 'right' }} ellipsis > - {balance} + {accountBalance} { diff --git a/src/libs/ui/components/transaction-fee-plate/transaction-fee-plate.tsx b/src/libs/ui/components/transaction-fee-plate/transaction-fee-plate.tsx index 111e1afe6..da8dd3d73 100644 --- a/src/libs/ui/components/transaction-fee-plate/transaction-fee-plate.tsx +++ b/src/libs/ui/components/transaction-fee-plate/transaction-fee-plate.tsx @@ -1,16 +1,14 @@ import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; import styled from 'styled-components'; import { TRANSFER_COST_MOTES } from '@src/constants'; -import { selectAccountCurrencyRate } from '@background/redux/account-info/selectors'; - import { AlignedSpaceBetweenFlexRow, RightAlignedFlexColumn } from '@libs/layout'; +import { useFetchWalletBalance } from '@libs/services/balance-service'; import { Tile, Typography } from '@libs/ui/components'; import { formatFiatAmount, formatNumber, motesToCSPR } from '@libs/ui/utils'; @@ -27,7 +25,7 @@ export const TransactionFeePlate = ({ }: TransactionFeePlateProps) => { const { t } = useTranslation(); - const currencyRate = useSelector(selectAccountCurrencyRate); + const { currencyRate } = useFetchWalletBalance(); return ( @@ -45,7 +43,7 @@ export const TransactionFeePlate = ({ {formatFiatAmount( motesToCSPR(TRANSFER_COST_MOTES) || '0', - currencyRate, + currencyRate?.rate || null, 3 )} diff --git a/src/libs/ui/forms/create-account.ts b/src/libs/ui/forms/create-account.ts index 382166e93..e633ec467 100644 --- a/src/libs/ui/forms/create-account.ts +++ b/src/libs/ui/forms/create-account.ts @@ -3,7 +3,7 @@ import { yupResolver } from '@hookform/resolvers/yup/dist/yup'; import { UseFormProps, useForm } from 'react-hook-form'; import { SecretPhrase, deriveKeyPair } from '@libs/crypto'; -import { AccountWithBalance } from '@libs/types/account'; +import { Account } from '@libs/types/account'; import { useAccountNameRule } from './form-validation-rules'; @@ -34,7 +34,7 @@ export function useCreateAccountForm( export function getDefaultName( existingAccountNames: string[], - derivedAccounts: AccountWithBalance[], + derivedAccounts: Account[], secretPhrase: SecretPhrase | null ) { const accountString = 'Account'; diff --git a/src/libs/ui/forms/form-validation-rules.ts b/src/libs/ui/forms/form-validation-rules.ts index 0a7789715..1732c8eeb 100644 --- a/src/libs/ui/forms/form-validation-rules.ts +++ b/src/libs/ui/forms/form-validation-rules.ts @@ -137,17 +137,18 @@ export const useRecipientPublicKeyRule = () => { return false; }, - message: t('Recipient should be a valid public key or cspr name') + message: t( + 'The recipient should be a valid public key, CSPR.name or contact name' + ) }); }; -export const useCSPRTransferAmountRule = (amountMotes: string | null) => { +export const useCSPRTransferAmountRule = (amountMotes: string | undefined) => { const { t } = useTranslation(); - const maxAmountMotes: string = - amountMotes == null - ? '0' - : Big(amountMotes).sub(TRANSFER_COST_MOTES).toFixed(); + const maxAmountMotes: string = !amountMotes + ? '0' + : Big(amountMotes).sub(TRANSFER_COST_MOTES).toFixed(); return Yup.string() .required(t('Amount is required')) @@ -269,11 +270,12 @@ export const useErc20AmountRule = ( }); }; -export const usePaymentAmountRule = (csprBalance: string | null) => { +export const usePaymentAmountRule = (csprBalance: string | undefined) => { const { t } = useTranslation(); - const maxAmountMotes: string = - csprBalance == null ? '0' : Big(csprBalance).toFixed(); + const maxAmountMotes: string = !csprBalance + ? '0' + : Big(csprBalance).toFixed(); return Yup.string() .required(t('Payment amount is required')) @@ -315,7 +317,7 @@ export const usePaymentAmountRule = (csprBalance: string | null) => { }; export const useCSPRStakeAmountRule = ( - amountMotes: string | null, + amountMotes: string | undefined, mode: AuctionManagerEntryPoint, stakeAmountMotes: string ) => { @@ -337,10 +339,9 @@ export const useCSPRStakeAmountRule = ( } }; - const maxAmountMotes: string = - amountMotes == null - ? '0' - : Big(amountMotes).sub(STAKE_COST_MOTES).toFixed(); + const maxAmountMotes: string = !amountMotes + ? '0' + : Big(amountMotes).sub(STAKE_COST_MOTES).toFixed(); return Yup.string() .required({ diff --git a/src/libs/ui/forms/stakes-form.ts b/src/libs/ui/forms/stakes-form.ts index 4b40c5660..2b57b78cf 100644 --- a/src/libs/ui/forms/stakes-form.ts +++ b/src/libs/ui/forms/stakes-form.ts @@ -23,7 +23,7 @@ export type StakeAmountFormValues = { }; export const useStakesForm = ( - amountMotes: string | null, + amountMotes: string | undefined, stakeType: AuctionManagerEntryPoint, stakeAmountMotes: string, delegatorsNumber?: number, diff --git a/src/libs/ui/forms/transfer-nft.ts b/src/libs/ui/forms/transfer-nft.ts index e0fc37bbd..b7225cfe2 100644 --- a/src/libs/ui/forms/transfer-nft.ts +++ b/src/libs/ui/forms/transfer-nft.ts @@ -16,7 +16,7 @@ export type TransferNftRecipientFormValues = { }; export const useTransferNftForm = ( - amountMotes: string | null, + amountMotes: string | undefined, paymentAmount: string ) => { const recipientFormSchema = Yup.object().shape({ diff --git a/src/libs/ui/forms/transfer.ts b/src/libs/ui/forms/transfer.ts index 6d6fe9aaf..1006fddf0 100644 --- a/src/libs/ui/forms/transfer.ts +++ b/src/libs/ui/forms/transfer.ts @@ -41,7 +41,7 @@ export const useTransferRecipientForm = () => { export const useTransferAmountForm = ( erc20Balance: string | null, isErc20: boolean, - amountMotes: string | null, + amountMotes: string | undefined, paymentAmount: string, decimals: number | undefined ) => {