From b6572c1b7d6ae5caf4bf05ce159764b4f6ca34ba Mon Sep 17 00:00:00 2001 From: "G. Kami Ekbatanifard" <46442452+Nick-1979@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:39:09 +0330 Subject: [PATCH] Revamp home to show total balances, its changes (24hr) and the list of tokens, ... (#44) utilize new features including deep links, card,heading size prop, ... --- package.json | 3 + packages/dapp/package.json | 2 + packages/dapp/pro package.json | 57 ------- packages/dapp/setDappPath.js | 9 ++ packages/snap/package.json | 4 +- packages/snap/snap.manifest.json | 4 +- packages/snap/src/constants.ts | 21 +++ packages/snap/src/defaults.ts | 4 - packages/snap/src/index.tsx | 43 ++--- packages/snap/src/rpc/getAddress.ts | 2 +- packages/snap/src/rpc/stateManagement.ts | 17 +- packages/snap/src/ui/accountInfo.tsx | 28 +--- packages/snap/src/ui/balanceDetails.tsx | 23 +++ .../snap/src/ui/components/BalanceInfo.tsx | 90 +++++------ packages/snap/src/ui/image/chains/acala.svg | 10 ++ packages/snap/src/ui/image/chains/ajuna.svg | 31 ++++ .../snap/src/ui/image/chains/assetHub.svg | 10 ++ .../snap/src/ui/image/chains/basilisk.svg | 3 + packages/snap/src/ui/image/chains/bifrost.svg | 13 ++ .../snap/src/ui/image/chains/centrifuge.svg | 6 + .../snap/src/ui/image/chains/composable.svg | 20 +++ .../snap/src/ui/image/chains/darwinia.svg | 15 ++ packages/snap/src/ui/image/chains/getLogo.ts | 36 ++++- packages/snap/src/ui/image/chains/hydradx.svg | 4 + packages/snap/src/ui/image/chains/index.tsx | 18 ++- packages/snap/src/ui/image/chains/karura.svg | 14 ++ packages/snap/src/ui/image/chains/kulupu.svg | 3 + packages/snap/src/ui/image/chains/nodle.svg | 3 + packages/snap/src/ui/image/chains/picasso.svg | 3 + packages/snap/src/ui/image/chains/ternoa.svg | 11 ++ packages/snap/src/ui/image/chains/westend.svg | 9 ++ .../snap/src/ui/image/chains/zeitgeist.svg | 3 + packages/snap/src/ui/partials/accountDemo.tsx | 98 +++++++++--- packages/snap/src/ui/polkagateApps.tsx | 30 ++-- packages/snap/src/ui/receive.tsx | 51 ++++++ packages/snap/src/ui/welcomeScreen.tsx | 17 +- packages/snap/src/util/getApi.ts | 6 +- packages/snap/src/util/getBalance.ts | 27 +++- packages/snap/src/util/getCurrentChain.ts | 6 +- .../src/util/getCurrentChainTokenPrice.ts | 17 +- packages/snap/src/util/getEndpoint.ts | 5 +- packages/snap/src/util/getFormatted.ts | 2 +- packages/snap/src/util/getKeyPair.ts | 4 +- packages/snap/src/util/getNativeTokenPrice.ts | 25 +-- packages/snap/src/{ => util}/getPrices.ts | 42 ++--- packages/snap/src/util/handleBalancesAll.tsx | 52 +++++++ packages/snap/src/utils.ts | 7 +- scripts/publish.sh | 2 +- yarn.lock | 147 +++++++++++++++++- 49 files changed, 797 insertions(+), 260 deletions(-) delete mode 100644 packages/dapp/pro package.json create mode 100644 packages/dapp/setDappPath.js create mode 100644 packages/snap/src/constants.ts delete mode 100644 packages/snap/src/defaults.ts create mode 100644 packages/snap/src/ui/balanceDetails.tsx create mode 100644 packages/snap/src/ui/image/chains/acala.svg create mode 100644 packages/snap/src/ui/image/chains/ajuna.svg create mode 100644 packages/snap/src/ui/image/chains/assetHub.svg create mode 100644 packages/snap/src/ui/image/chains/basilisk.svg create mode 100644 packages/snap/src/ui/image/chains/bifrost.svg create mode 100644 packages/snap/src/ui/image/chains/centrifuge.svg create mode 100644 packages/snap/src/ui/image/chains/composable.svg create mode 100644 packages/snap/src/ui/image/chains/darwinia.svg create mode 100644 packages/snap/src/ui/image/chains/hydradx.svg create mode 100644 packages/snap/src/ui/image/chains/karura.svg create mode 100644 packages/snap/src/ui/image/chains/kulupu.svg create mode 100644 packages/snap/src/ui/image/chains/nodle.svg create mode 100644 packages/snap/src/ui/image/chains/picasso.svg create mode 100644 packages/snap/src/ui/image/chains/ternoa.svg create mode 100644 packages/snap/src/ui/image/chains/westend.svg create mode 100644 packages/snap/src/ui/image/chains/zeitgeist.svg create mode 100644 packages/snap/src/ui/receive.tsx rename packages/snap/src/{ => util}/getPrices.ts (68%) create mode 100644 packages/snap/src/util/handleBalancesAll.tsx diff --git a/package.json b/package.json index e757e70..863b120 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "packages/*" ], "scripts": { + "dev": "cd packages/dapp && yarn dev && cd ../.. && yarn install", + "preinstall": "cd packages/dapp && yarn preinstall", "build": "yarn workspaces foreach --parallel --topological --verbose run build", "install-chrome": "./scripts/install-chrome.sh", "lint": "yarn lint:eslint && yarn lint:misc --check", @@ -68,6 +70,7 @@ "@polkadot/ui-keyring": "^3.6.6", "@polkadot/util": "^12.6.2", "chromedriver": "latest", + "qrcode": "^1.5.4", "webdriverio": "latest" } } diff --git a/packages/dapp/package.json b/packages/dapp/package.json index 26deafa..054caf6 100644 --- a/packages/dapp/package.json +++ b/packages/dapp/package.json @@ -30,6 +30,8 @@ "web-vitals": "^2.1.4" }, "scripts": { + "preinstall": "NODE_ENV=production node ./setDappPath.js", + "dev": "NODE_ENV=development node ./setDappPath.js ", "start": "PORT=8000 react-scripts start", "build": "react-scripts build", "test": "react-scripts test", diff --git a/packages/dapp/pro package.json b/packages/dapp/pro package.json deleted file mode 100644 index 26deafa..0000000 --- a/packages/dapp/pro package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "polkamask_dapp", - "version": "0.1.0", - "private": true, - "dependencies": { - "@emotion/react": "^11.11.3", - "@emotion/styled": "^11.11.0", - "@metamask/detect-provider": "^2.0.0", - "@metamask/providers": "^14.0.2", - "@mui/icons-material": "^5.15.4", - "@mui/lab": "^5.0.0-alpha.160", - "@mui/material": "^5.15.4", - "@polkadot/api": "^12.1.1", - "@polkadot/api-derive": "^12.1.1", - "@polkadot/apps-config": "^0.141.1", - "@polkadot/extension-inject": "^0.48.2", - "@polkagate/extension-dapp": "^0.48.2", - "@testing-library/jest-dom": "^5.17.0", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^13.5.0", - "@types/jest": "^27.5.2", - "@types/node": "^16.18.66", - "@types/react": "^18.2.40", - "@types/react-dom": "^18.2.17", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-i18next": "^14.0.0", - "react-scripts": "5.0.1", - "typescript": "^4.9.5", - "web-vitals": "^2.1.4" - }, - "scripts": { - "start": "PORT=8000 react-scripts start", - "build": "react-scripts build", - "test": "react-scripts test", - "eject": "react-scripts eject", - "clean": "yarn cache clean --all" - }, - "eslintConfig": { - "extends": [ - "react-app", - "react-app/jest" - ] - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/packages/dapp/setDappPath.js b/packages/dapp/setDappPath.js new file mode 100644 index 0000000..7451b20 --- /dev/null +++ b/packages/dapp/setDappPath.js @@ -0,0 +1,9 @@ +const fs = require('fs'); +const packageJson = require('./package.json'); + +const isProduction = process.env.NODE_ENV === 'production'; +packageJson.dependencies['@polkagate/extension-dapp'] = isProduction + ? '^0.48.2' + : '../../../polkadot-js-extension/packages/extension-dapp/build'; + +fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)); \ No newline at end of file diff --git a/packages/snap/package.json b/packages/snap/package.json index 3e0126b..3a06049 100644 --- a/packages/snap/package.json +++ b/packages/snap/package.json @@ -1,6 +1,6 @@ { "name": "@polkagate/snap", - "version": "0.6.0", + "version": "0.7.0", "description": "A MetaMask Snap to interact with Polkadot ecosystem, a platform for cross-chain communication and scalability. Use your MetaMask wallet to access Polkadot dApps and tokens. No extra extension needed.", "repository": { "type": "git", @@ -56,6 +56,8 @@ "@metamask/utils": "^9.3.0", "@swc/core": "1.3.78", "@swc/jest": "^0.2.26", + "@types/react": "18.2.4", + "@types/react-dom": "18.2.4", "@typescript-eslint/eslint-plugin": "^5.42.1", "@typescript-eslint/parser": "^5.42.1", "deepmerge": "^4.2.2", diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index e89c0e4..a7f371e 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -1,5 +1,5 @@ { - "version": "0.6.0", + "version": "0.7.0", "description": "Explore Polkadot decentralized applications and manage your tokens using your MetaMask wallet. Start your journey at apps.polkagate.xyz.", "proposedName": "PolkaGate", "repository": { @@ -7,7 +7,7 @@ "url": "https://github.com/polkagate/snap.git" }, "source": { - "shasum": "kg+O7LMOr0ADHcUzkdTGhT4MPaPZKtkhFFVDELp/FGw=", + "shasum": "CRmXO1t+mMp/F99/JhIfeOnVlLw169eBsjdOTQdFSIY=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snap/src/constants.ts b/packages/snap/src/constants.ts new file mode 100644 index 0000000..94f7477 --- /dev/null +++ b/packages/snap/src/constants.ts @@ -0,0 +1,21 @@ +export const DEFAULT_NETWORK_PREFIX = 42; // 42 is for substrate +export const DEFAULT_COIN_TYPE = 354; // 354 is for Polkadot +export const DEFAULT_CHAIN_NAME = 'polkadot'; // Since Westend shares the same address prefix as Substrate, the address format for both is identical +export const CHAIN_NAMES = ['westend', 'polkadot', 'kusama']; + +export const NOT_LISTED_CHAINS = [ + '0x742a2ca70c2fda6cee4f8df98d64c4c670a052d9568058982dad9d5a7a135c5b', // Darwinia + '0x6f1a800de3daff7f5e037ddf66ab22ce03ab91874debeddb1086f5f7dbd48925', // Equilibirum + '0x81443836a9a24caaa23f1241897d1235717535711d1d3fe24eae4fdc942c092c', // Cere + '0xdaab8df776eb52ec604a5df5d388bb62a050a0aaec4556a64265b9d42755552d', // Composable + '0xe358eb1d11b31255a286c12e44fe6780b7edb171d657905a97e39f71d9c6c3ee', // Ajuna + '0xe71578b37a7c799b0ab4ee87ffa6f059a6b98f71f06fb8c84a8d88013a548ad6', // Darwinia + '0xf7a99d3cb92853d00d5275c971c132c074636256583fee53b3bbe60d7b8769ba', // Kulupu + '0xe61a41c53f5dcd0beb09df93b34402aada44cb05117b71059cce40a2723a4e97', // parallel + '0x5d3c298622d5634ed019bf61ea4b71655030015bde9beb0d6a24743714462c86', // Pendulum + '0x1bb969d85965e4bb5a651abbedf21a54b6b31a21f66b5401cc3f1e286268d736', // Phala + '0x6fbd74e5e1d0a61d52ccfe9d4adaed16dd3a7caa37c6bc4d0c2fa12e8b2f4063', // Polymesh + '0x7e4e32d0feafd4f9c9414b0be86373f9a1efa904809b683453a9af6856d38ad5', // SORA + ] + + export const PRICE_VALIDITY_PERIOD = 2 * 60 * 1000; diff --git a/packages/snap/src/defaults.ts b/packages/snap/src/defaults.ts deleted file mode 100644 index b7ed6e9..0000000 --- a/packages/snap/src/defaults.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const DEFAULT_NETWORK_PREFIX = 42; // 42 is for substrate -export const DEFAULT_COIN_TYPE = 354; // 354 is for Polkadot -export const DEFAULT_CHAIN_NAME = 'polkadot'; // Since Westend shares the same address prefix as Substrate, the address format for both is identical -export const CHAIN_NAMES = ['westend', 'polkadot', 'kusama']; diff --git a/packages/snap/src/index.tsx b/packages/snap/src/index.tsx index 495c481..777f41e 100644 --- a/packages/snap/src/index.tsx +++ b/packages/snap/src/index.tsx @@ -9,7 +9,6 @@ import type { OnUserInputHandler, } from '@metamask/snaps-sdk'; -import { getGenesisHash } from './chains'; import { getMetadataList, setMetadata, @@ -27,16 +26,16 @@ import { staking, voting } from './ui'; -import { getBalances, getKeyPair } from './util'; +import { getKeyPair } from './util'; import { POLKADOT_GENESIS } from '@polkadot/apps-config'; import { getLogo } from './ui/image/chains/getLogo'; -import { HexString } from '@polkadot/util/types'; -import { getSnapState, setSnapState, updateSnapState } from './rpc/stateManagement'; -import { getNativeTokenPrice } from './util/getNativeTokenPrice'; +import { setSnapState, updateSnapState } from './rpc/stateManagement'; import getChainName from './util/getChainName'; -import { DEFAULT_CHAIN_NAME } from './defaults'; import { welcomeScreen } from './ui/welcomeScreen'; import { showMore } from './ui/showMore'; +import { receive } from './ui/receive'; +import { balanceDetails } from './ui/balanceDetails'; +import { handleBalancesAll } from './util/handleBalancesAll'; export const onRpcRequest: OnRpcRequestHandler = async ({ origin, @@ -69,15 +68,10 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ * @returns A static panel rendered with custom UI. */ export const onHomePage: OnHomePageHandler = async () => { - const currentChainName = DEFAULT_CHAIN_NAME; // to reset chain on each new visit - const { address } = await getKeyPair(currentChainName); - const genesisHash = await getGenesisHash(currentChainName) ?? POLKADOT_GENESIS; // These will be changed when dropdown component will be available - const balances = await getBalances(genesisHash, address); - const logo = await getLogo(genesisHash); - const priceInUsd = await getNativeTokenPrice(genesisHash); + const { address, balancesAll, logos, pricesInUsd } = await handleBalancesAll() return { - content: accountDemo(address, genesisHash, balances, logo, priceInUsd), + content: accountDemo(address, balancesAll, logos, pricesInUsd), }; }; @@ -102,16 +96,22 @@ export const onInstall: OnInstallHandler = async () => { }); }; -export const onUserInput: OnUserInputHandler = async ({ id, event }) => { +export const onUserInput: OnUserInputHandler = async ({ id, event, context }) => { + + // const state = await snap.request({ + // method: 'snap_getInterfaceState', + // params: { id }, + // }); + if (event.type === UserInputEventType.ButtonClickEvent || event.type === UserInputEventType.InputChangeEvent) { switch (event.name) { case 'switchChain': { const genesisHash = event.value; const destinationChainName = await getChainName(genesisHash) - await showSpinner(id, `Switching chain to ${destinationChainName} ...`); + await showSpinner(id, `Switching format to ${destinationChainName} ...`); await updateSnapState('currentGenesisHash', genesisHash); - await accountInfo(id, genesisHash); + await receive(id, genesisHash); break; } @@ -119,6 +119,10 @@ export const onUserInput: OnUserInputHandler = async ({ id, event }) => { await polkagateApps(id); break; + case 'receive': + await receive(id); + break; + case 'more': await showMore(id); break; @@ -135,10 +139,13 @@ export const onUserInput: OnUserInputHandler = async ({ id, event }) => { await exportAccount(id); break; + case 'balanceDetails': + await balanceDetails(id, context?.show === undefined ? true : !context.show); + break; + case 'backToHome': await showSpinner(id, 'Loading, please wait ...'); - const state = await getSnapState(); - await accountInfo(id, state?.currentGenesisHash as HexString); + await accountInfo(id); break; default: diff --git a/packages/snap/src/rpc/getAddress.ts b/packages/snap/src/rpc/getAddress.ts index eeeb619..6d88948 100644 --- a/packages/snap/src/rpc/getAddress.ts +++ b/packages/snap/src/rpc/getAddress.ts @@ -1,4 +1,4 @@ -import { DEFAULT_CHAIN_NAME } from '../defaults'; +import { DEFAULT_CHAIN_NAME } from '../constants'; import { getKeyPair } from '../util/getKeyPair'; export const getAddress = async (chainName?: string): Promise => { diff --git a/packages/snap/src/rpc/stateManagement.ts b/packages/snap/src/rpc/stateManagement.ts index ad0ffb2..f794fd8 100644 --- a/packages/snap/src/rpc/stateManagement.ts +++ b/packages/snap/src/rpc/stateManagement.ts @@ -1,8 +1,9 @@ import { HexString } from "@polkadot/util/types"; import type { MetadataDef } from '@polkadot/extension-inject/types'; -import { PricesType } from "../getPrices"; +import { PricesType } from "../util/getPrices"; interface State { + balancesAll?: any; currentGenesisHash?: HexString; metadata?: Record; priceInfo?: { @@ -14,19 +15,19 @@ interface State { type StateValues = State[keyof State]; -export const getSnapState = async () => - await snap.request({ - method: 'snap_manageState', - params: { operation: 'get' }, - }) as State; - -export const setSnapState = async (newState:State) => { +export const setSnapState = async (newState: State) => { return await snap.request({ method: 'snap_manageState', params: { operation: 'update', newState }, }); }; +export const getSnapState = async () => + await snap.request({ + method: 'snap_manageState', + params: { operation: 'get' }, + }) as State; + export const updateSnapState = async (field: keyof State, data: any) => { const state = (await getSnapState()) || {}; diff --git a/packages/snap/src/ui/accountInfo.tsx b/packages/snap/src/ui/accountInfo.tsx index 02d214a..531ba3a 100644 --- a/packages/snap/src/ui/accountInfo.tsx +++ b/packages/snap/src/ui/accountInfo.tsx @@ -1,10 +1,8 @@ +// Copyright 2023-2024 @polkagate/snap authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { handleBalancesAll } from '../util/handleBalancesAll'; import { accountDemo } from './partials/accountDemo'; -import { getBalances } from '../util/getBalance'; -import { getKeyPair } from '../util/getKeyPair'; -import { HexString } from '@polkadot/util/types'; -import { getLogo } from './image/chains/getLogo'; -import { updateSnapState } from '../rpc/stateManagement'; -import { getNativeTokenPrice } from '../util/getNativeTokenPrice'; /** * Show account info on the current chain. @@ -12,24 +10,14 @@ import { getNativeTokenPrice } from '../util/getNativeTokenPrice'; * @param id - The id of current UI interface. * @param genesisHash - Chain genesisHash. */ -export async function accountInfo(id: string, genesisHash: HexString) { - console.info(`Preparing account info for ${genesisHash}`) - - const { address } = await getKeyPair(undefined, genesisHash); - const priceInUsd = await getNativeTokenPrice(genesisHash); - - - if (!genesisHash) throw new Error(`No genesis hash found for chain :${genesisHash}`) - updateSnapState('currentGenesisHash', genesisHash).catch(console.error); - - const balances = await getBalances(genesisHash, address); - const logo = await getLogo(genesisHash) - +export async function accountInfo(id: string) { + const {address, balancesAll, logos, pricesInUsd} = await handleBalancesAll() + await snap.request({ method: 'snap_updateInterface', params: { id, - ui: accountDemo(address, genesisHash, balances, logo, priceInUsd), + ui: accountDemo(address, balancesAll, logos, pricesInUsd), }, }); } diff --git a/packages/snap/src/ui/balanceDetails.tsx b/packages/snap/src/ui/balanceDetails.tsx new file mode 100644 index 0000000..270fe9e --- /dev/null +++ b/packages/snap/src/ui/balanceDetails.tsx @@ -0,0 +1,23 @@ +// Copyright 2023-2024 @polkagate/snap authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { handleBalancesAll } from '../util/handleBalancesAll'; +import { accountDemo } from './partials/accountDemo'; + +/** + * Show balance details for all tokens + * + * @param id - The id of current UI interface. + */ +export async function balanceDetails(id: string, show?:boolean) { + const { address, balancesAll, logos, pricesInUsd } = await handleBalancesAll() + + await snap.request({ + method: 'snap_updateInterface', + params: { + id, + ui: accountDemo(address, balancesAll, logos, pricesInUsd, show), + context: { show: !!show } + }, + }); +} diff --git a/packages/snap/src/ui/components/BalanceInfo.tsx b/packages/snap/src/ui/components/BalanceInfo.tsx index c2f30b3..21fa55f 100644 --- a/packages/snap/src/ui/components/BalanceInfo.tsx +++ b/packages/snap/src/ui/components/BalanceInfo.tsx @@ -1,72 +1,54 @@ // Copyright 2023-2024 @polkagate/snap authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { BN } from '@polkadot/util'; import type { Balances } from '../../util/getBalance'; -import { Box, Row, SnapComponent, Value } from "@metamask/snaps-sdk/jsx"; -import { Balance } from '@polkadot/types/interfaces'; +import { Box, Card, Divider, Text, Row, SnapComponent, Section } from "@metamask/snaps-sdk/jsx"; +import { amountToHuman } from '../../util/amountToHuman'; type Props = { balances: Balances; - price: number -} - -type BalanceRowProps = { - balance: Balance | undefined; - decimal: number; - label: string; price: number; + logo: string; + showDetail?: boolean; } -const balanceToValue = (total: BN, decimal: number, price: number) => (total.toNumber() * price / (10 ** decimal)).toFixed(1) - -const BalanceRow: SnapComponent = ({ label, balance, decimal, price }: BalanceRowProps) => { - - const value = balance ? balanceToValue(balance, decimal, price) : 0; - - return ( - - - - ); -}; - -export const BalanceInfo: SnapComponent = ({ balances, price }: Props) => { - const { total, transferable, locked, soloTotal, pooledBalance, decimal } = balances; +export const BalanceInfo: SnapComponent = ({ balances, price, logo, showDetail }: Props) => { + const { total, transferable, locked, soloTotal, pooledBalance, decimal, token } = balances; + const totalPrice = parseFloat(amountToHuman(total, decimal)) * price; return ( - - - - - + {!!showDetail && +
+ + + {`${amountToHuman(transferable, decimal)} ${token}`} + + {!locked.isZero() && + + {`${amountToHuman(locked, decimal)} ${token}`} + + } + {!!(soloTotal && !soloTotal.isZero()) && + + {`${amountToHuman(soloTotal, decimal)} ${token}`} + + } + {!!(pooledBalance && !pooledBalance.isZero()) && + + {`${amountToHuman(pooledBalance, decimal)} ${token}`} + + } +
+ }
); }; diff --git a/packages/snap/src/ui/image/chains/acala.svg b/packages/snap/src/ui/image/chains/acala.svg new file mode 100644 index 0000000..f8c4880 --- /dev/null +++ b/packages/snap/src/ui/image/chains/acala.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/ajuna.svg b/packages/snap/src/ui/image/chains/ajuna.svg new file mode 100644 index 0000000..588dddb --- /dev/null +++ b/packages/snap/src/ui/image/chains/ajuna.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/assetHub.svg b/packages/snap/src/ui/image/chains/assetHub.svg new file mode 100644 index 0000000..1b13216 --- /dev/null +++ b/packages/snap/src/ui/image/chains/assetHub.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/basilisk.svg b/packages/snap/src/ui/image/chains/basilisk.svg new file mode 100644 index 0000000..e8d659a --- /dev/null +++ b/packages/snap/src/ui/image/chains/basilisk.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/snap/src/ui/image/chains/bifrost.svg b/packages/snap/src/ui/image/chains/bifrost.svg new file mode 100644 index 0000000..b0103e4 --- /dev/null +++ b/packages/snap/src/ui/image/chains/bifrost.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/centrifuge.svg b/packages/snap/src/ui/image/chains/centrifuge.svg new file mode 100644 index 0000000..8bc004a --- /dev/null +++ b/packages/snap/src/ui/image/chains/centrifuge.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/snap/src/ui/image/chains/composable.svg b/packages/snap/src/ui/image/chains/composable.svg new file mode 100644 index 0000000..9014d62 --- /dev/null +++ b/packages/snap/src/ui/image/chains/composable.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/darwinia.svg b/packages/snap/src/ui/image/chains/darwinia.svg new file mode 100644 index 0000000..d38ddfa --- /dev/null +++ b/packages/snap/src/ui/image/chains/darwinia.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/getLogo.ts b/packages/snap/src/ui/image/chains/getLogo.ts index fa4eb59..3b6f988 100644 --- a/packages/snap/src/ui/image/chains/getLogo.ts +++ b/packages/snap/src/ui/image/chains/getLogo.ts @@ -1,6 +1,6 @@ import { HexString } from "@polkadot/util/types"; import getChainName, { sanitizeChainName } from "../../../util/getChainName"; -import { astar, bittensor, globe, kusama, polkadot } from "."; +import { assetHub, ajuna, acala, astar, bittensor, bifrost, basilisk, centrifuge, composable, darwinia, karura, kulupu, picasso, globe, hydradx, kusama, ternoa, nodle, polkadot, westend, zeitgeist } from "."; export const getLogo = async (genesisHash: HexString): Promise => { const chainName = await getChainName(genesisHash); @@ -9,12 +9,46 @@ export const getLogo = async (genesisHash: HexString): Promise => { switch (sanitizedChainName?.toLowerCase()) { case 'astar': return astar; + case 'acala': + return acala; + case 'ajuna': + return ajuna; + case 'basilisk': + return basilisk; + case 'bifrost': + return bifrost; + case 'composable': + return composable; case 'polkadot': return polkadot; + case 'centrifuge': + return centrifuge; case 'bittensor': return bittensor; + case 'darwinia': + return darwinia; + case 'hydradx': + return hydradx; + case 'karura': + return karura; + case 'kulupu': + return kulupu; case 'kusama': return kusama; + case 'picasso': + return picasso; + case 'nodle': + return nodle; + case 'ternoa': + return ternoa; + case 'westend': + return westend; + case 'zeitgeist': + return zeitgeist; + case 'westendassethub': + case 'kusamaassethub': + case 'polkadotassethub': + return assetHub; default: return globe; diff --git a/packages/snap/src/ui/image/chains/hydradx.svg b/packages/snap/src/ui/image/chains/hydradx.svg new file mode 100644 index 0000000..dcf44d3 --- /dev/null +++ b/packages/snap/src/ui/image/chains/hydradx.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/snap/src/ui/image/chains/index.tsx b/packages/snap/src/ui/image/chains/index.tsx index 4c0ae13..e6258f8 100644 --- a/packages/snap/src/ui/image/chains/index.tsx +++ b/packages/snap/src/ui/image/chains/index.tsx @@ -2,4 +2,20 @@ export { default as polkadot } from './polkadot.svg' export { default as kusama } from './kusama.svg' export { default as globe } from './globe.svg' export { default as astar } from './astar.svg' -export { default as bittensor } from './bittensor.svg' \ No newline at end of file +export { default as acala } from './acala.svg' +export { default as bittensor } from './bittensor.svg' +export { default as ajuna } from './ajuna.svg' +export { default as basilisk } from './basilisk.svg' +export { default as bifrost } from './bifrost.svg' +export { default as westend } from './westend.svg' +export { default as centrifuge } from './centrifuge.svg' +export { default as composable } from './composable.svg' +export { default as darwinia } from './darwinia.svg' +export { default as hydradx } from './hydradx.svg' +export { default as kulupu } from './kulupu.svg' +export { default as karura } from './karura.svg' +export { default as nodle } from './nodle.svg' +export { default as picasso } from './picasso.svg' +export { default as ternoa } from './ternoa.svg' +export { default as zeitgeist } from './zeitgeist.svg' +export { default as assetHub } from './assetHub.svg' \ No newline at end of file diff --git a/packages/snap/src/ui/image/chains/karura.svg b/packages/snap/src/ui/image/chains/karura.svg new file mode 100644 index 0000000..9cea17a --- /dev/null +++ b/packages/snap/src/ui/image/chains/karura.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/kulupu.svg b/packages/snap/src/ui/image/chains/kulupu.svg new file mode 100644 index 0000000..0e7c1f9 --- /dev/null +++ b/packages/snap/src/ui/image/chains/kulupu.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/snap/src/ui/image/chains/nodle.svg b/packages/snap/src/ui/image/chains/nodle.svg new file mode 100644 index 0000000..12cc224 --- /dev/null +++ b/packages/snap/src/ui/image/chains/nodle.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/snap/src/ui/image/chains/picasso.svg b/packages/snap/src/ui/image/chains/picasso.svg new file mode 100644 index 0000000..e59fcff --- /dev/null +++ b/packages/snap/src/ui/image/chains/picasso.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/snap/src/ui/image/chains/ternoa.svg b/packages/snap/src/ui/image/chains/ternoa.svg new file mode 100644 index 0000000..2f07224 --- /dev/null +++ b/packages/snap/src/ui/image/chains/ternoa.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/westend.svg b/packages/snap/src/ui/image/chains/westend.svg new file mode 100644 index 0000000..8d9df51 --- /dev/null +++ b/packages/snap/src/ui/image/chains/westend.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/snap/src/ui/image/chains/zeitgeist.svg b/packages/snap/src/ui/image/chains/zeitgeist.svg new file mode 100644 index 0000000..6514f21 --- /dev/null +++ b/packages/snap/src/ui/image/chains/zeitgeist.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/snap/src/ui/partials/accountDemo.tsx b/packages/snap/src/ui/partials/accountDemo.tsx index 44ace22..dbf8f5f 100644 --- a/packages/snap/src/ui/partials/accountDemo.tsx +++ b/packages/snap/src/ui/partials/accountDemo.tsx @@ -2,36 +2,96 @@ // SPDX-License-Identifier: Apache-2.0 import type { Balances } from '../../util/getBalance'; -import { getFormatted } from '../../util/getFormatted'; -import { Copyable, Box, Heading, Section, Icon } from '@metamask/snaps-sdk/jsx'; +import { Box, Heading, Section, Icon, Text, Button } from '@metamask/snaps-sdk/jsx'; -import { BalanceInfo, ChainSwitch, MenuBar } from '../components'; -import { HexString } from '@polkadot/util/types'; +import { BalanceInfo } from '../components'; +import { amountToHuman } from '../../util/amountToHuman'; +import { PriceValue } from '../../util/getPrices'; -export const accountDemo = (address: string, genesisHash: HexString, balances: Balances, logo: string, price: number) => { - console.info(`Lets show account demo for ${address} on ${genesisHash}`) +export const accountDemo = ( + address: string, + balancesAll: Balances[], + logos: { genesisHash: string, logo: string }[], + prices: { genesisHash: string, price: PriceValue }[], + showDetail?: boolean +) => { - const formatted = genesisHash ? getFormatted(genesisHash, address) : address; + const totalBalance = balancesAll.reduce((acc, { total, decimal }, index) => acc + Number(amountToHuman(total, decimal)) * prices[index].price.value, 0) + const availableBalance = balancesAll.reduce((acc, { transferable, decimal }, index) => acc + Number(amountToHuman(transferable, decimal)) * prices[index].price.value, 0) + const nonZeroBalances = balancesAll.filter(({ total }) => !total.isZero()); + const totalBalanceChanges = balancesAll.reduce((acc, { total, decimal }, index) => { + const value = Number(amountToHuman(total, decimal)) * prices[index].price.value; + return acc + value * prices[index].price.change / 100 + }, 0) + + const signOfChanges = totalBalanceChanges > 0 ? '+' : totalBalanceChanges < 0 ? '-' : ''; + const colorOfChanges = totalBalanceChanges > 0 ? 'success' : totalBalanceChanges < 0 ? 'error' : 'default'; return (
- - - Account + + Total balance + {`$${totalBalance.toFixed(2)}`} - - -
-
- - - Balance + + {`available $${availableBalance.toFixed(2)}`} + {`${signOfChanges}$${totalBalanceChanges.toFixed(2)}`} - +
+ + + + + + + + + Send + Receive + Stake + Vote + More + +
- + + Tokens + {!!nonZeroBalances?.length && + + + + } + + {nonZeroBalances?.length + ? nonZeroBalances.map((balances, index) => { + + const logo = logos.find(({ genesisHash }) => genesisHash === balances.genesisHash)?.logo as string; + const price = prices.find(({ genesisHash }) => genesisHash === balances.genesisHash)?.price.value as number; // needs that prices be token-based when supporting multi asset chains + + return ( +
+ +
+ ) + }) + :
+ No tokens to show! +
+ }
); }; diff --git a/packages/snap/src/ui/polkagateApps.tsx b/packages/snap/src/ui/polkagateApps.tsx index 69efc42..1d4abe2 100644 --- a/packages/snap/src/ui/polkagateApps.tsx +++ b/packages/snap/src/ui/polkagateApps.tsx @@ -14,21 +14,21 @@ const ui = () => { return ( - -
- - - Send Funds! - - - With the PolkaGate app, you can send funds, stake tokens, vote on referenda, create an identity, view assets across chains, unlock tokens, and more. - - Visit: - - apps.polkagate.xyz - - -
+ +
+ + + Send Funds! + + + With the PolkaGate app, you can send funds, stake tokens, vote on referenda, create an identity, view assets across chains, unlock tokens, and more. + + Visit: + + apps.polkagate.xyz + + +
+
+
+ ); +}; \ No newline at end of file diff --git a/packages/snap/src/ui/welcomeScreen.tsx b/packages/snap/src/ui/welcomeScreen.tsx index 1ccd795..ee2138b 100644 --- a/packages/snap/src/ui/welcomeScreen.tsx +++ b/packages/snap/src/ui/welcomeScreen.tsx @@ -3,7 +3,7 @@ import { getFormatted } from '../util/getFormatted'; -import { Copyable, Box, Heading, Divider, Text, Bold } from "@metamask/snaps-sdk/jsx"; +import { Copyable, Box, Heading, Divider, Text, Bold, Link, Icon, Section } from "@metamask/snaps-sdk/jsx"; import { HexString } from '@polkadot/util/types'; @@ -14,12 +14,17 @@ export const welcomeScreen = (address: string, genesisHash: HexString, logo: str return ( - 🎉 Polkadot Account Created! 🚀 - Your account address: - + Welcome to Polkadot eco.! ✋ - Explore features like managing balances, staking, voting in governance, and more—all from the PolkaGate home screen. - To get started, open MetaMask, go to Menu → Snaps, and click the PolkaGate icon. + Explore features like managing balances, staking, voting in governance, and more—all from the PolkaGate Snap Home. +
+ To visit Home, open MetaMask, go to + + + Menu → Snaps + + Then click on PolkaGate logo. +
); }; diff --git a/packages/snap/src/util/getApi.ts b/packages/snap/src/util/getApi.ts index 1dddf3c..ca7674e 100644 --- a/packages/snap/src/util/getApi.ts +++ b/packages/snap/src/util/getApi.ts @@ -8,14 +8,16 @@ import { HexString } from '@polkadot/util/types'; * * @param genesisHash - The genesisHash of the chain will be used to find an endpoint to use. */ -export async function getApi(genesisHash: HexString): Promise { +export async function getApi(genesisHash: HexString): Promise { console.info(`Preparing API for ${genesisHash}`) const endpoint = await getEndpoint(genesisHash); console.info(`Selected Endpoint on ${genesisHash} is ${endpoint} `) if (!endpoint) { - throw new Error(`No endpoint with genesisHash: '${genesisHash}'.`); + console.error(`No endpoint with genesisHash: '${genesisHash}'.`); + + return null; } const adjustedUrl = endpoint.replace('wss://', 'https://'); // since Metamask snap does not support web sockets at the moment we use https instead const httpProvider = new HttpProvider(adjustedUrl); diff --git a/packages/snap/src/util/getBalance.ts b/packages/snap/src/util/getBalance.ts index 33e3076..7da5b95 100644 --- a/packages/snap/src/util/getBalance.ts +++ b/packages/snap/src/util/getBalance.ts @@ -19,6 +19,8 @@ export type Balances = { soloTotal?: Balance; pooledBalance?: Balance; decimal: number; + genesisHash: HexString; + token: string; }; /** @@ -32,6 +34,14 @@ export async function getBalances(genesisHash: HexString, address: string,): Pro console.info(`getting balances for ${address} on ${genesisHash}`) const api = await getApi(genesisHash); + + if (!api) { + // FixMe: + return { genesisHash, total: BN_ZERO, transferable: BN_ZERO, locked: BN_ZERO, soloTotal: BN_ZERO, pooledBalance: BN_ZERO, decimal: 10, token: 'Unit' }; + } + + const decimal = api.registry.chainDecimals[0]; + const token = api.registry.chainTokens[0]; const formatted = getFormatted(genesisHash, address); console.info(`Formatted address for ${address} is ${formatted}`) @@ -39,7 +49,14 @@ export async function getBalances(genesisHash: HexString, address: string,): Pro data: AccountData; }; + if (balances.data.free.isZero()) { + // no need to more check + const ZERO_BALANCE = api.createType('Balance', BN_ZERO) as unknown as Balance; + return { genesisHash, total: ZERO_BALANCE, transferable: ZERO_BALANCE, locked: ZERO_BALANCE, soloTotal: ZERO_BALANCE, pooledBalance: ZERO_BALANCE, decimal, token }; + } + let soloTotal; + if (api.query.staking?.ledger) { const ledger = await api.query.staking.ledger(formatted); @@ -53,7 +70,7 @@ export async function getBalances(genesisHash: HexString, address: string,): Pro let pooledBalance: Balance | undefined = undefined; - if (api.query.staking?.ledger) { + if (api.query.nominationPools?.poolMembers) { const mayBePooledBalance = await getPooledBalance(api, formatted); if (mayBePooledBalance) { @@ -66,7 +83,7 @@ export async function getBalances(genesisHash: HexString, address: string,): Pro const transferable = api.createType( 'Balance', - balances.data.free.sub(balances.data.frozen), + balances.data.free.sub(balances.data.frozen || balances.data.miscFrozen), ) as unknown as Balance; const total = api.createType( @@ -76,12 +93,10 @@ export async function getBalances(genesisHash: HexString, address: string,): Pro const locked = api.createType( 'Balance', - balances.data.frozen, + (balances.data.frozen || balances.data.miscFrozen), ) as unknown as Balance; - const decimal = api.registry.chainDecimals[0]; - - return { total, transferable, locked, soloTotal, pooledBalance, decimal }; + return { genesisHash, total, transferable, locked, soloTotal, pooledBalance, decimal, token }; } diff --git a/packages/snap/src/util/getCurrentChain.ts b/packages/snap/src/util/getCurrentChain.ts index 8829957..ee69541 100644 --- a/packages/snap/src/util/getCurrentChain.ts +++ b/packages/snap/src/util/getCurrentChain.ts @@ -1,4 +1,4 @@ -import { DEFAULT_CHAIN_NAME } from '../defaults'; +import { DEFAULT_CHAIN_NAME } from '../constants'; import { getSnapState } from '../rpc/stateManagement'; import getChainName, { sanitizeChainName } from './getChainName'; @@ -6,9 +6,9 @@ import getChainName, { sanitizeChainName } from './getChainName'; * To get the current chain name, sanitized and lower case. */ export async function getCurrentChain(): Promise { - const {currentGenesisHash} = await getSnapState(); + const { currentGenesisHash } = await getSnapState(); - if(!currentGenesisHash){ + if (!currentGenesisHash) { console.info(`current chain name is default, ${DEFAULT_CHAIN_NAME}`) return DEFAULT_CHAIN_NAME; } diff --git a/packages/snap/src/util/getCurrentChainTokenPrice.ts b/packages/snap/src/util/getCurrentChainTokenPrice.ts index 62d8446..09ad5fe 100644 --- a/packages/snap/src/util/getCurrentChainTokenPrice.ts +++ b/packages/snap/src/util/getCurrentChainTokenPrice.ts @@ -1,4 +1,4 @@ -import getPrices from '../getPrices'; +import getPrices, { PricesType } from './getPrices'; import { getSnapState } from '../rpc/stateManagement'; import { getCurrentChain } from './getCurrentChain'; @@ -22,3 +22,18 @@ export async function getCurrentChainTokenPrice(): Promise { return newPriceInfo?.prices[currentChainName]?.value || 0; } + +export async function updateTokenPrices(): Promise { + const { priceInfo } = await getSnapState(); + + console.info('price info:', priceInfo) + + if (priceInfo?.date && Date.now() - priceInfo.date < PRICE_VALIDITY_PERIOD) { + // price exists and is updated + return priceInfo.prices; + } + + const newPriceInfo = await getPrices(); + + return newPriceInfo?.prices; +} diff --git a/packages/snap/src/util/getEndpoint.ts b/packages/snap/src/util/getEndpoint.ts index 24c1379..d73caa1 100644 --- a/packages/snap/src/util/getEndpoint.ts +++ b/packages/snap/src/util/getEndpoint.ts @@ -18,7 +18,10 @@ export default async function getEndpoint(_genesisHash: HexString | undefined, i const endpoints = sanitizedChainName ? allEndpoints?.filter((e) => - e.value && (!ignoreLightClient || !e.value.startsWith('light') ) && + e.value && (!ignoreLightClient || !e.value.startsWith('light') ) && !e.value.includes('onfinality') && + // Check if e.value matches the pattern 'wss://' + !/^wss:\/\/\d+$/.test(e.value) + && ( String(e.info)?.toLowerCase() === sanitizedChainName || String(e.text)?.toLowerCase()?.includes(sanitizedChainName || '') diff --git a/packages/snap/src/util/getFormatted.ts b/packages/snap/src/util/getFormatted.ts index 991c6f0..0192b2d 100644 --- a/packages/snap/src/util/getFormatted.ts +++ b/packages/snap/src/util/getFormatted.ts @@ -3,7 +3,7 @@ import { decodeAddress, encodeAddress } from '@polkadot/util-crypto'; import { getChain } from '../chains'; -import { DEFAULT_NETWORK_PREFIX } from '../defaults'; +import { DEFAULT_NETWORK_PREFIX } from '../constants'; /** * To get the formatted address of an address. diff --git a/packages/snap/src/util/getKeyPair.ts b/packages/snap/src/util/getKeyPair.ts index 7dcf425..5ec587c 100644 --- a/packages/snap/src/util/getKeyPair.ts +++ b/packages/snap/src/util/getKeyPair.ts @@ -7,7 +7,7 @@ import type { KeyringPair } from '@polkadot/keyring/types'; import { stringToU8a } from '@polkadot/util'; import { getChain, getChainFromMetadata } from '../chains'; -import { DEFAULT_CHAIN_NAME, DEFAULT_COIN_TYPE, DEFAULT_NETWORK_PREFIX } from '../defaults'; +import { DEFAULT_CHAIN_NAME, DEFAULT_COIN_TYPE, DEFAULT_NETWORK_PREFIX } from '../constants'; import { HexString } from '@polkadot/util/types'; export const getKeyPair = async ( @@ -16,7 +16,7 @@ export const getKeyPair = async ( ): Promise => { console.info(`Getting keypair for ${genesisHash}`); - + let prefix: number | undefined; prefix = getChain(genesisHash ?? chainName)?.prefix; diff --git a/packages/snap/src/util/getNativeTokenPrice.ts b/packages/snap/src/util/getNativeTokenPrice.ts index dc31d43..f9d62d1 100644 --- a/packages/snap/src/util/getNativeTokenPrice.ts +++ b/packages/snap/src/util/getNativeTokenPrice.ts @@ -1,34 +1,35 @@ import { HexString } from '@polkadot/util/types'; -import getPrices from '../getPrices'; +import { PriceValue } from './getPrices'; import { getSnapState } from '../rpc/stateManagement'; import getChainName, { sanitizeChainName } from './getChainName'; -import { getCurrentChain } from './getCurrentChain'; const PRICE_VALIDITY_PERIOD = 5 * 60 * 1000; + +const DEFAULT_PRICE_VALUE = { + value: 0, + change: 0 +} /** * To get the chain's native token price. */ -export async function getNativeTokenPrice(genesisHash:HexString): Promise { +export async function getNativeTokenPrice(genesisHash: HexString): Promise<{genesisHash:HexString, price:PriceValue}> { const chainName = await getChainName(genesisHash); const priceId = sanitizeChainName(chainName)?.toLowerCase(); - if(!priceId){ + if (!priceId) { console.info('No priceId for genesisHash:', genesisHash) - - return 0; + + return {genesisHash, price: DEFAULT_PRICE_VALUE}; } const { priceInfo } = await getSnapState(); - console.info('chain name in getNativeTokenPrice is:', priceId) - console.info('price info:', priceInfo) - + let price = DEFAULT_PRICE_VALUE; if (priceInfo?.date && Date.now() - priceInfo.date < PRICE_VALIDITY_PERIOD && priceInfo.prices[priceId]) { // price exists and is updated - return priceInfo.prices[priceId].value || 0; + price= priceInfo.prices[priceId]; } - const newPriceInfo = await getPrices(); - return newPriceInfo?.prices[priceId]?.value || 0; + return {genesisHash, price}; } diff --git a/packages/snap/src/getPrices.ts b/packages/snap/src/util/getPrices.ts similarity index 68% rename from packages/snap/src/getPrices.ts rename to packages/snap/src/util/getPrices.ts index 206ef56..2113057 100644 --- a/packages/snap/src/getPrices.ts +++ b/packages/snap/src/util/getPrices.ts @@ -1,9 +1,9 @@ -import { getAllChains } from "./chains"; -import { updateSnapState } from "./rpc/stateManagement"; -import { sanitizeChainName } from "./util/getChainName"; +import { getAllChains } from "../chains"; +import { updateSnapState } from "../rpc/stateManagement"; +import { sanitizeChainName } from "./getChainName"; import { createAssets } from '@polkagate/apps-config/assets'; -interface PriceValue { +export interface PriceValue { value: number; change: number; } @@ -44,36 +44,36 @@ const getPriceIds = () => { return nonDuplicatedPriceIds.size ? [...nonDuplicatedPriceIds] as string[] : null; } -export default async function getPrices( currencyCode = 'usd') { +export default async function getPrices(currencyCode = 'usd') { const priceIds = getPriceIds(); - if(!priceIds){ + if (!priceIds) { console.error('No price ids!'); return; } console.log('getting prices for:', priceIds.sort()); const revisedPriceIds = priceIds.map((item) => (EXTRA_PRICE_IDS[item] || item)); -try{ - const prices = await getReq(`https://api.coingecko.com/api/v3/simple/price?ids=${revisedPriceIds.join(',')}&vs_currencies=${currencyCode}&include_24hr_change=true`); + try { + const prices = await getReq(`https://api.coingecko.com/api/v3/simple/price?ids=${revisedPriceIds.join(',')}&vs_currencies=${currencyCode}&include_24hr_change=true`); - const outputObjectPrices: PricesType = {}; + const outputObjectPrices: PricesType = {}; - for (const [key, value] of Object.entries(prices)) { - outputObjectPrices[key] = { - change: value[`${currencyCode}_24h_change`] as number, - value: value[currencyCode] as number, - }; - } + for (const [key, value] of Object.entries(prices)) { + outputObjectPrices[key] = { + change: value[`${currencyCode}_24h_change`] as number, + value: value[currencyCode] as number, + }; + } - const price = { currencyCode, date: Date.now(), prices: outputObjectPrices }; + const price = { currencyCode, date: Date.now(), prices: outputObjectPrices }; - await updateSnapState('priceInfo', price).catch(console.error); + await updateSnapState('priceInfo', price).catch(console.error); - return price; -}catch{ - console.log('Something went wrong while getting prices! Try again later.') -} + return price; + } catch { + console.log('Something went wrong while getting prices! Try again later.') + } } async function getReq(api: string): Promise> { diff --git a/packages/snap/src/util/handleBalancesAll.tsx b/packages/snap/src/util/handleBalancesAll.tsx new file mode 100644 index 0000000..37bdfc9 --- /dev/null +++ b/packages/snap/src/util/handleBalancesAll.tsx @@ -0,0 +1,52 @@ +// Copyright 2023-2024 @polkagate/snap authors & contributors +// SPDX-License-Identifier: Apache-2.0 + + +import { getChainOptions } from '../chains'; +import { getBalances, getKeyPair } from '.'; +import { getLogo } from '../ui/image/chains/getLogo'; +import { HexString } from '@polkadot/util/types'; +import { getSnapState, updateSnapState } from '../rpc/stateManagement'; +import { getNativeTokenPrice } from './getNativeTokenPrice'; +import { DEFAULT_CHAIN_NAME, NOT_LISTED_CHAINS, PRICE_VALIDITY_PERIOD } from '../constants'; +import { updateTokenPrices } from './getCurrentChainTokenPrice'; +import { isHexToBn } from '../utils'; + +export const handleBalancesAll = async () => { + const options = getChainOptions() + const selectedOptions = options.filter(({ value }) => !NOT_LISTED_CHAINS.includes(value));//.slice(0, 2); + + const currentChainName = DEFAULT_CHAIN_NAME; // to reset chain on each new visit + const { address } = await getKeyPair(currentChainName); + let balancesAll; + const savedBalancesAll = await getSnapState(); + + const logoList = await Promise.all(selectedOptions.map(({ value }) => getLogo(value as HexString))); + + const logos =selectedOptions.map(({ value }, index) => { + return { genesisHash: value, logo: logoList[index] } + }); + + if (savedBalancesAll.balancesAll && Date.now() - Number(savedBalancesAll.balancesAll.date) < PRICE_VALIDITY_PERIOD) { + const temp = JSON.parse(savedBalancesAll.balancesAll.data); + + temp.forEach((item) => { + item.total = isHexToBn(item.total) + item.transferable = isHexToBn(item.transferable) + item.locked = isHexToBn(item.locked) + item.soloTotal = isHexToBn(item.soloTotal) + item.pooledBalance = isHexToBn(item.pooledBalance); + }) + balancesAll = temp; + } else { + const balancesAllPromises = selectedOptions.map(({ value }) => getBalances(value as HexString, address)) + balancesAll = await Promise.all(balancesAllPromises) + + await updateSnapState('balancesAll', { date: Date.now(), data: JSON.stringify(balancesAll) }); + } + + await updateTokenPrices(); + const pricesInUsd = await Promise.all(selectedOptions.map(({ value }) => getNativeTokenPrice(value as HexString))); + + return { address, balancesAll, logos, pricesInUsd } +} \ No newline at end of file diff --git a/packages/snap/src/utils.ts b/packages/snap/src/utils.ts index a7c2a6a..da58eca 100644 --- a/packages/snap/src/utils.ts +++ b/packages/snap/src/utils.ts @@ -1,4 +1,5 @@ import { assert } from '@metamask/utils'; +import { BN, hexToBn, isHex } from '@polkadot/util'; /** * Get the current count from the Snap state. @@ -41,4 +42,8 @@ export async function increment() { }); return newState.count; -} \ No newline at end of file +} + +export const isHexToBn = (i?: string): BN | undefined => i + ? isHex(i) ? hexToBn(i) : new BN(i) + : undefined; diff --git a/scripts/publish.sh b/scripts/publish.sh index 62d92a7..1340223 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -13,7 +13,7 @@ cd packages/snap/ # $1: patch | minor | major (passed as an argument) npm version "$1" -# Step 4-1: Apply version in snao,manifest,json and update the shasum +# Step 4-1: Apply version in snap,manifest,json and update the shasum yarn yarn build diff --git a/yarn.lock b/yarn.lock index 1283a9b..1cc65e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6861,6 +6861,8 @@ __metadata: "@polkagate/apps-config": ^0.140.3 "@swc/core": 1.3.78 "@swc/jest": ^0.2.26 + "@types/react": 18.2.4 + "@types/react-dom": 18.2.4 "@typescript-eslint/eslint-plugin": ^5.42.1 "@typescript-eslint/parser": ^5.42.1 buffer: ^6.0.3 @@ -8251,6 +8253,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:18.2.4": + version: 18.2.4 + resolution: "@types/react-dom@npm:18.2.4" + dependencies: + "@types/react": "*" + checksum: 8301f35cf1cbfec8c723e9477aecf87774e3c168bd457d353b23c45064737213d3e8008b067c6767b7b08e4f2b3823ee239242a6c225fc91e7f8725ef8734124 + languageName: node + linkType: hard + "@types/react-dom@npm:^18.0.0, @types/react-dom@npm:^18.2.17": version: 18.3.0 resolution: "@types/react-dom@npm:18.3.0" @@ -8279,6 +8290,17 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:18.2.4": + version: 18.2.4 + resolution: "@types/react@npm:18.2.4" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: d920fc93832fe50d5e8175a0ba233086c97a9e238ff7327c8319b8dec57409618f491d6f71be2374c3132f40a8fc428b3e406c1e2a5f1dc32ccd6d47051786d2 + languageName: node + linkType: hard + "@types/resolve@npm:1.17.1": version: 1.17.1 resolution: "@types/resolve@npm:1.17.1" @@ -8295,6 +8317,13 @@ __metadata: languageName: node linkType: hard +"@types/scheduler@npm:*": + version: 0.23.0 + resolution: "@types/scheduler@npm:0.23.0" + checksum: 874d753aa65c17760dfc460a91e6df24009bde37bfd427a031577b30262f7770c1b8f71a21366c7dbc76111967384cf4090a31d65315155180ef14bd7acccb32 + languageName: node + linkType: hard + "@types/semver@npm:^7.3.12": version: 7.5.8 resolution: "@types/semver@npm:7.5.8" @@ -10529,7 +10558,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^5.3.1": +"camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": version: 5.3.1 resolution: "camelcase@npm:5.3.1" checksum: e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b @@ -10761,6 +10790,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^6.0.0": + version: 6.0.0 + resolution: "cliui@npm:6.0.0" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.0 + wrap-ansi: ^6.2.0 + checksum: 4fcfd26d292c9f00238117f39fc797608292ae36bac2168cfee4c85923817d0607fe21b3329a8621e01aedf512c99b7eaa60e363a671ffd378df6649fb48ae42 + languageName: node + linkType: hard + "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -11790,6 +11830,13 @@ __metadata: languageName: node linkType: hard +"decamelize@npm:^1.2.0": + version: 1.2.0 + resolution: "decamelize@npm:1.2.0" + checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa + languageName: node + linkType: hard + "decamelize@npm:^6.0.0": version: 6.0.0 resolution: "decamelize@npm:6.0.0" @@ -12185,6 +12232,13 @@ __metadata: languageName: node linkType: hard +"dijkstrajs@npm:^1.0.1": + version: 1.0.3 + resolution: "dijkstrajs@npm:1.0.3" + checksum: 82ff2c6633f235dd5e6bed04ec62cdfb1f327b4d7534557bd52f18991313f864ee50654543072fff4384a92b643ada4d5452f006b7098dbdfad6c8744a8c9e08 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -14229,7 +14283,7 @@ __metadata: languageName: node linkType: hard -"get-caller-file@npm:^2.0.5": +"get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" checksum: b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 @@ -18921,6 +18975,13 @@ __metadata: languageName: node linkType: hard +"pngjs@npm:^5.0.0": + version: 5.0.0 + resolution: "pngjs@npm:5.0.0" + checksum: 04e912cc45fb9601564e2284efaf0c5d20d131d9b596244f8a6789fc6cdb6b18d2975a6bbf7a001858d7e159d5c5c5dd7b11592e97629b7137f7f5cef05904c8 + languageName: node + linkType: hard + "pnglib@npm:0.0.1": version: 0.0.1 resolution: "pnglib@npm:0.0.1" @@ -18957,6 +19018,7 @@ __metadata: jest: ^29.7.0 prettier: ^2.7.1 prettier-plugin-packagejson: ^2.2.18 + qrcode: ^1.5.4 ts-jest: ^29.1.0 tsup: ^8.0.1 typescript: ^4.8.4 @@ -20202,6 +20264,19 @@ __metadata: languageName: node linkType: hard +"qrcode@npm:^1.5.4": + version: 1.5.4 + resolution: "qrcode@npm:1.5.4" + dependencies: + dijkstrajs: ^1.0.1 + pngjs: ^5.0.0 + yargs: ^15.3.1 + bin: + qrcode: bin/qrcode + checksum: 0a162822e12c02b0333315462fd4ccad22255002130f86806773be7592aec5ef295efaffa3eb148cbf00e290839c7b610f63b0d62a0c5efc5bc52a68f4189684 + languageName: node + linkType: hard + "qs@npm:6.11.0": version: 6.11.0 resolution: "qs@npm:6.11.0" @@ -20824,6 +20899,13 @@ __metadata: languageName: node linkType: hard +"require-main-filename@npm:^2.0.0": + version: 2.0.0 + resolution: "require-main-filename@npm:2.0.0" + checksum: e9e294695fea08b076457e9ddff854e81bffbe248ed34c1eec348b7abbd22a0d02e8d75506559e2265e96978f3c4720bd77a6dad84755de8162b357eb6c778c7 + languageName: node + linkType: hard + "require-package-name@npm:^2.0.1": version: 2.0.1 resolution: "require-package-name@npm:2.0.1" @@ -21490,6 +21572,13 @@ __metadata: languageName: node linkType: hard +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 + languageName: node + linkType: hard + "set-function-length@npm:^1.2.1": version: 1.2.2 resolution: "set-function-length@npm:1.2.2" @@ -24063,6 +24152,13 @@ __metadata: languageName: node linkType: hard +"which-module@npm:^2.0.0": + version: 2.0.1 + resolution: "which-module@npm:2.0.1" + checksum: 1967b7ce17a2485544a4fdd9063599f0f773959cca24176dbe8f405e55472d748b7c549cd7920ff6abb8f1ab7db0b0f1b36de1a21c57a8ff741f4f1e792c52be + languageName: node + linkType: hard + "which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14, which-typed-array@npm:^1.1.15, which-typed-array@npm:^1.1.2, which-typed-array@npm:^1.1.9": version: 1.1.15 resolution: "which-typed-array@npm:1.1.15" @@ -24339,6 +24435,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^6.2.0": + version: 6.2.0 + resolution: "wrap-ansi@npm:6.2.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: 6cd96a410161ff617b63581a08376f0cb9162375adeb7956e10c8cd397821f7eb2a6de24eb22a0b28401300bf228c86e50617cd568209b5f6775b93c97d2fe3a + languageName: node + linkType: hard + "wrap-ansi@npm:^8.1.0": version: 8.1.0 resolution: "wrap-ansi@npm:8.1.0" @@ -24464,6 +24571,13 @@ __metadata: languageName: node linkType: hard +"y18n@npm:^4.0.0": + version: 4.0.3 + resolution: "y18n@npm:4.0.3" + checksum: 014dfcd9b5f4105c3bb397c1c8c6429a9df004aa560964fb36732bfb999bfe83d45ae40aeda5b55d21b1ee53d8291580a32a756a443e064317953f08025b1aa4 + languageName: node + linkType: hard + "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8" @@ -24508,6 +24622,16 @@ __metadata: languageName: node linkType: hard +"yargs-parser@npm:^18.1.2": + version: 18.1.3 + resolution: "yargs-parser@npm:18.1.3" + dependencies: + camelcase: ^5.0.0 + decamelize: ^1.2.0 + checksum: 60e8c7d1b85814594d3719300ecad4e6ae3796748b0926137bfec1f3042581b8646d67e83c6fc80a692ef08b8390f21ddcacb9464476c39bbdf52e34961dd4d9 + languageName: node + linkType: hard + "yargs-parser@npm:^20.2.2": version: 20.2.9 resolution: "yargs-parser@npm:20.2.9" @@ -24552,6 +24676,25 @@ __metadata: languageName: node linkType: hard +"yargs@npm:^15.3.1": + version: 15.4.1 + resolution: "yargs@npm:15.4.1" + dependencies: + cliui: ^6.0.0 + decamelize: ^1.2.0 + find-up: ^4.1.0 + get-caller-file: ^2.0.1 + require-directory: ^2.1.1 + require-main-filename: ^2.0.0 + set-blocking: ^2.0.0 + string-width: ^4.2.0 + which-module: ^2.0.0 + y18n: ^4.0.0 + yargs-parser: ^18.1.2 + checksum: 40b974f508d8aed28598087720e086ecd32a5fd3e945e95ea4457da04ee9bdb8bdd17fd91acff36dc5b7f0595a735929c514c40c402416bbb87c03f6fb782373 + languageName: node + linkType: hard + "yargs@npm:^16.2.0": version: 16.2.0 resolution: "yargs@npm:16.2.0"