diff --git a/.eslintrc b/.eslintrc index 173f32078..a8c51ed2e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -85,6 +85,7 @@ "linebreak-style": ["error", "unix"], "quotes": ["error", "single"], "semi": ["error", "always"], + "prefer-object-spread": 2, "object-curly-newline": "off", "arrow-body-style": "off", "react/jsx-props-no-spreading": "off", diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a8ed1781..792804350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,34 +7,67 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [[v2.29.0-beta.4]](https://github.com/multiversx/mx-sdk-dapp/pull/1073)] - 2024-03-14 +- [Fixed different address than account address in account info](https://github.com/multiversx/mx-sdk-dapp/pull/1076) +- [Changed 2FA signing to use wallet cross-window provider](https://github.com/multiversx/mx-sdk-dapp/pull/1075) + +## [[v2.29.0-beta.3]](https://github.com/multiversx/mx-sdk-dapp/pull/1073)] - 2024-03-12 +- [Fixed WebWallet logout not working](https://github.com/multiversx/mx-sdk-dapp/pull/1074) + +## [[v2.29.0-beta.2]](https://github.com/multiversx/mx-sdk-dapp/pull/1073)] - 2024-03-12 +- [Move axios in peerDependencies](https://github.com/multiversx/mx-sdk-dapp/pull/1023) +- [Upgrade sdk packages](https://github.com/multiversx/mx-sdk-dapp/pull/1072) + +## [[v2.29.0-beta.1]](https://github.com/multiversx/mx-sdk-dapp/pull/1071)] - 2024-03-12 +- [Added getOperationsDetails for retrieving visible operations](https://github.com/multiversx/mx-sdk-dapp/pull/1054) +- [Fix experimental webview provider initialization](https://github.com/multiversx/mx-sdk-dapp/pull/1070) + +## [[v2.29.0-beta.0]](https://github.com/multiversx/mx-sdk-dapp/pull/1069)] - 2024-03-11 +- [Fixed types and file names](https://github.com/multiversx/mx-sdk-dapp/pull/1068) +- [Add support for the new cross window functionality in web wallet](https://github.com/multiversx/mx-sdk-dapp/pull/1067) +- [Update WalletConnect package and functionality](https://github.com/multiversx/mx-sdk-dapp/pull/1064) +- [Fixed sign step signing and labels](https://github.com/multiversx/mx-sdk-dapp/pull/1060) +- [Fixed signing multiple transactions with guarded ledger](https://github.com/multiversx/mx-sdk-dapp/pull/1059) +- [Added getHasNativeAuth in order to see if nativeAuth has been configured on development mode](https://github.com/multiversx/mx-sdk-dapp/pull/1051) +- [Added support for nativeAuth impersonate](https://github.com/multiversx/mx-sdk-dapp/pull/1049) +- [Updated WalletConnectV2 account provider to be updated on new or existing session](https://github.com/multiversx/mx-sdk-dapp/pull/1050) + ## [[v2.28.8]](https://github.com/multiversx/mx-sdk-dapp/pull/1062)] - 2024-03-07 + - [Fixed base64 utils conversion](https://github.com/multiversx/mx-sdk-dapp/pull/1061) ## [[v2.28.7]](https://github.com/multiversx/mx-sdk-dapp/pull/1048)] - 2024-02-13 + - [Updated AddressRow data-testids](https://github.com/multiversx/mx-sdk-dapp/pull/1047) ## [[v2.28.6]](https://github.com/multiversx/mx-sdk-dapp/pull/1044)] - 2024-02-08 + - [Added option to access URL search param from application load time in `useParseSignedTransactions`](https://github.com/multiversx/mx-sdk-dapp/pull/1042) - [Fixed wallet connect breaks login with other providers](https://github.com/multiversx/mx-sdk-dapp/pull/1043) - [Fixed possibly undefined payload on custom toasts](https://github.com/multiversx/mx-sdk-dapp/pull/1036) ## [[v2.28.5]](https://github.com/multiversx/mx-sdk-dapp/pull/1036)] - 2024-02-01 + - [Fixed logout with web wallet infinite loop](https://github.com/multiversx/mx-sdk-dapp/pull/1036) ## [[v2.28.4]](https://github.com/multiversx/mx-sdk-dapp/pull/1035)] - 2024-02-01 + - [Reverted setting walletconnectV2 `accountProvider` on init](https://github.com/multiversx/mx-sdk-dapp/pull/1036) - [Fixed setting `loginToken` in `nativeAuthService` losing previous state](https://github.com/multiversx/mx-sdk-dapp/pull/1034) - [Fixed setting walletconnectV2 `accountProvider` on init](https://github.com/multiversx/mx-sdk-dapp/pull/1033) ## [[v2.28.3]](https://github.com/multiversx/mx-sdk-dapp/pull/1032)] - 2024-01-30 + - [Added transaction toast wrapper id](https://github.com/multiversx/mx-sdk-dapp/pull/1031) ## [[v2.28.2]](https://github.com/multiversx/mx-sdk-dapp/pull/1030)] - 2024-01-26 + - [Added support for `checkIsValidSender` with array option](https://github.com/multiversx/mx-sdk-dapp/pull/1029) ## [[v2.28.1]](https://github.com/multiversx/mx-sdk-dapp/pull/1028)] - 2024-01-25 -- [Added support for Web Wallet multisig token login](https://github.com/multiversx/mx-sdk-dapp/pull/1027) +- [Added support for Web Wallet multisig token login](https://github.com/multiversx/mx-sdk-dapp/pull/1027) +- [Changed postMessage payload from string to plain object](https://github.com/multiversx/mx-sdk-dapp/pull/1025) ## [[v2.28.0]](https://github.com/multiversx/mx-sdk-dapp/pull/1022)] - 2024-01-11 diff --git a/README.md b/README.md index 9696663f7..96dae1929 100644 --- a/README.md +++ b/README.md @@ -956,7 +956,12 @@ The sdk-dapp library exposes bundles for both CommonJS and ESModules, however, i moduleNameMapper: { '@multiversx/sdk-dapp/(.*)': '/node_modules/@multiversx/sdk-dapp/__commonjs/$1.js' -} +}, +``` + +You may need in your setupJest.js file do: +```javascript +import 'isomorphic-fetch'; ``` # sdk-dapp exports diff --git a/package.json b/package.json index d7aeb6b76..974605252 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-dapp", - "version": "2.28.8", + "version": "2.29.0-beta.4", "description": "A library to hold the main logic for a dapp on the MultiversX blockchain", "author": "MultiversX", "license": "GPL-3.0-or-later", @@ -57,6 +57,7 @@ "@types/qs": "6.9.7", "@typescript-eslint/eslint-plugin": "5.0.0", "@typescript-eslint/parser": "5.14.0", + "axios": "1.6.5", "axios-mock-adapter": "1.21.2", "babel-loader": "8.3.0", "css-loader": "5.2.6", @@ -83,6 +84,7 @@ "history": "5.3.0", "husky": "8.0.1", "identity-obj-proxy": "3.0.0", + "isomorphic-fetch": "3.0.0", "jest": "28.1.1", "jest-environment-jsdom": "28.1.3", "jest-mock": "29.3.1", @@ -113,6 +115,7 @@ "peerDependencies": { "@types/react": "^18.0.24", "@types/react-dom": "^18.0.8", + "axios": ">=1.6.5", "react": "^18.2.0", "react-dom": "^18.2.0" }, @@ -142,17 +145,17 @@ "**/*.d.ts" ], "dependencies": { - "@multiversx/sdk-core": "12.18.0", + "@lifeomic/axios-fetch": "3.0.1", + "@multiversx/sdk-core": "13.0.0-beta.4", "@multiversx/sdk-extension-provider": "3.0.0", "@multiversx/sdk-hw-provider": "6.4.0", "@multiversx/sdk-native-auth-client": "1.0.7", "@multiversx/sdk-opera-provider": "1.0.0-alpha.1", "@multiversx/sdk-wallet": "4.2.0", - "@multiversx/sdk-wallet-connect-provider": "4.1.0", - "@multiversx/sdk-web-wallet-cross-window-provider": "0.0.14", + "@multiversx/sdk-wallet-connect-provider": "4.1.1", + "@multiversx/sdk-web-wallet-cross-window-provider": "0.0.26", "@multiversx/sdk-web-wallet-provider": "3.2.1", "@reduxjs/toolkit": "1.8.2", - "axios": "1.6.5", "bignumber.js": "9.x", "linkify-react": "4.0.2", "linkifyjs": "4.0.2", diff --git a/src/UI/Balance/Balance.tsx b/src/UI/Balance/Balance.tsx new file mode 100644 index 000000000..f737feb23 --- /dev/null +++ b/src/UI/Balance/Balance.tsx @@ -0,0 +1,112 @@ +import React from 'react'; +import classNames from 'classnames'; + +import MultiversXSymbol from 'assets/icons/mvx-icon-simple.svg'; +import { withStyles } from 'hocs/withStyles'; +import { WithClassnameType } from 'UI/types'; + +import { WithStylesImportType } from '../../hocs/useStyles'; + +interface BalancePropsType extends WithClassnameType, WithStylesImportType { + amount: string; + addEqualSign?: boolean; + displayAsUsd?: boolean; + egldIcon?: boolean; + showTokenLabel?: boolean; + tokenLabel?: string; + showTokenLabelSup?: boolean; +} + +export const BalanceComponent = ({ + amount, + displayAsUsd, + addEqualSign, + egldIcon, + className, + showTokenLabel = true, + styles, + tokenLabel, + showTokenLabelSup, + 'data-testid': dataTestId +}: BalancePropsType) => { + const [mainBalance, decimalBalance] = amount.split('.'); + const processedMainBalance = + displayAsUsd && mainBalance.indexOf('$') < 0 + ? `$${mainBalance}` + : mainBalance; + + const getBalancePayload = () => { + const balancePayload: Record = { processedMainBalance }; + + if (addEqualSign && displayAsUsd) { + balancePayload.approximation = '≈'; + } + + if (decimalBalance) { + balancePayload.decimalBalance = `.${decimalBalance}`; + } + + if (!displayAsUsd && showTokenLabel) { + balancePayload.tokenLabel = ` ${tokenLabel}`; + } + + return balancePayload; + }; + + const balancePayload = getBalancePayload(); + const dataValues = [ + balancePayload.approximation, + balancePayload.processedMainBalance, + balancePayload.decimalBalance, + balancePayload.tokenLabel + ]; + + const dataBalanceValue = dataValues.reduce( + (totalDataValue, dataValueItem) => + dataValueItem ? totalDataValue.concat(dataValueItem) : totalDataValue, + '' + ); + + return ( +
+ {egldIcon && !displayAsUsd && ( + + )} + + {balancePayload.approximation && ( + + {balancePayload.approximation} + + )} + + {balancePayload.processedMainBalance && ( + {processedMainBalance} + )} + + {balancePayload.decimalBalance && ( + + {balancePayload.decimalBalance} + + )} + + {balancePayload.tokenLabel && ( + + {balancePayload.tokenLabel} + + )} +
+ ); +}; + +export const Balance = withStyles(BalanceComponent, { + ssrStyles: () => import('UI/Balance/balanceStyles.scss'), + clientStyles: () => require('UI/Balance/balanceStyles.scss').default +}); diff --git a/src/UI/Balance/balanceStyles.scss b/src/UI/Balance/balanceStyles.scss new file mode 100644 index 000000000..82c1f1906 --- /dev/null +++ b/src/UI/Balance/balanceStyles.scss @@ -0,0 +1,38 @@ +.balance { + display: flex; + align-items: center; + line-height: 1; + gap: 4px; + + .balanceApproximation { + opacity: 0.75; + } + + .balanceSymbol { + width: auto; + height: 0.666em; + position: relative; + top: 0.05em; + + path { + fill: currentColor; + } + } + + .balanceDecimals { + opacity: 0.75; + margin-left: -4px; + } + + .balanceSuffix { + opacity: 0.75; + + &.balanceSuffixSup { + font-size: 66%; + position: relative; + top: -0.125em; + vertical-align: unset; + white-space: nowrap; + } + } +} diff --git a/src/UI/Balance/index.ts b/src/UI/Balance/index.ts new file mode 100644 index 000000000..de9bb0894 --- /dev/null +++ b/src/UI/Balance/index.ts @@ -0,0 +1 @@ +export * from './Balance'; diff --git a/src/UI/SignTransactionsModals/SignTransactionsModals.tsx b/src/UI/SignTransactionsModals/SignTransactionsModals.tsx index 3c5263d5b..c7f537f0b 100644 --- a/src/UI/SignTransactionsModals/SignTransactionsModals.tsx +++ b/src/UI/SignTransactionsModals/SignTransactionsModals.tsx @@ -4,6 +4,7 @@ import { useGetLoginInfo } from 'hooks'; import { LoginMethodsEnum } from 'types'; import { ConfirmationScreen, DeviceConfirmationScreen } from './components'; +import { SignWithCrossWindowWalletModal } from './SignWithCrossWindowWalletModal'; import { SignWithExtensionModal } from './SignWithExtensionModal'; import { SignWithExtraModal } from './SignWithExtraModal'; import { SignWithLedgerModal } from './SignWithLedgerModal'; @@ -36,6 +37,8 @@ export const SignTransactionsModals = ({ CustomConfirmScreens?.WalletConnect ?? SignWithWalletConnectModal, Extension: CustomConfirmScreens?.Extension ?? SignWithExtensionModal, Opera: CustomConfirmScreens?.Opera ?? SignWithOperaModal, + CrossWindow: + CustomConfirmScreens?.CrossWindow ?? SignWithCrossWindowWalletModal, // The purpose of having this is to have a consistent flow of transaction signing. // The logic for redirecting to the web wallet is placed in the ConfirmationScreen component, // so we have to render that component when we are logged in with the web wallet provider @@ -60,7 +63,6 @@ export const SignTransactionsModals = ({ }, [verifyReceiverScam, className] ); - switch (loginMethod) { case LoginMethodsEnum.ledger: return renderScreen({ Screen: ConfirmScreens.Ledger, isDevice: true }); @@ -70,6 +72,8 @@ export const SignTransactionsModals = ({ return renderScreen({ Screen: ConfirmScreens.Extension }); case LoginMethodsEnum.opera: return renderScreen({ Screen: ConfirmScreens.Opera }); + case LoginMethodsEnum.crossWindow: + return renderScreen({ Screen: ConfirmScreens.CrossWindow }); case LoginMethodsEnum.wallet: return renderScreen({ Screen: ConfirmScreens.Wallet }); case LoginMethodsEnum.extra: diff --git a/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/SignWithCrossWindowWalletModal.tsx b/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/SignWithCrossWindowWalletModal.tsx new file mode 100644 index 000000000..a208d583d --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/SignWithCrossWindowWalletModal.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +import { useSelector } from 'reduxStore/DappProviderContext'; +import { networkSelector } from 'reduxStore/selectors'; +import { SignModalPropsType } from 'types'; +import { + SignWaitingScreenModal, + SignWaitingScreenModalPropsType +} from '../components'; + +export const SignWithCrossWindowWalletModal = (props: SignModalPropsType) => { + const { walletAddress } = useSelector(networkSelector); + + const description = props.error + ? props.error + : props.transactions?.length > 1 + ? 'Check your MultiversX Wallet to sign the transactions' + : 'Check your MultiversX Wallet to sign the transaction'; + + const waitingScreenProps: SignWaitingScreenModalPropsType = { + ...props, + description, + title: `Confirm on ${walletAddress}` + }; + + return ; +}; diff --git a/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/index.tsx b/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/index.tsx new file mode 100644 index 000000000..808a2d5f5 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/index.tsx @@ -0,0 +1 @@ +export * from './SignWithCrossWindowWalletModal'; diff --git a/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/signWithCrossWindowWalletModal.styles.scss b/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/signWithCrossWindowWalletModal.styles.scss new file mode 100644 index 000000000..73aa4674f --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithCrossWindowWalletModal/signWithCrossWindowWalletModal.styles.scss @@ -0,0 +1,11 @@ +.extension-modal { + color: inherit; +} + +.modal-container { + color: inherit; +} + +.extension { + color: inherit; +} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/SignStep.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/SignStep.tsx index 8c192a027..11434247f 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/SignStep.tsx +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/SignStep.tsx @@ -87,8 +87,8 @@ const SignStepComponent = (props: SignStepType & WithStylesImportType) => { const signLastTransaction = isLastTransaction && !waitingForDevice; - const onSubmit = () => { - onSignTransaction(); + const onSubmit = async () => { + await onSignTransaction(); if (signLastTransaction && GuardianScreen) { return setShowGuardianScreen(true); } diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/SignStepBody.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/SignStepBody.tsx index 89376fcee..69219cc7a 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/SignStepBody.tsx +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/SignStepBody.tsx @@ -1,21 +1,13 @@ import React from 'react'; import { Address } from '@multiversx/sdk-core/out'; import classNames from 'classnames'; + import { withStyles, WithStylesImportType } from 'hocs/withStyles'; -import { useGetEgldPrice, useGetNetworkConfig } from 'hooks'; -import { useGetTokenDetails } from 'hooks/transactions/useGetTokenDetails'; import { ActiveLedgerTransactionType, MultiSignTransactionType } from 'types'; -import { NftEnumType } from 'types/tokens.types'; import { TransactionData } from 'UI/TransactionData'; -import { getEgldLabel } from 'utils/network/getEgldLabel'; -import { formatAmount } from 'utils/operations/formatAmount'; -import { isTokenTransfer } from 'utils/transactions/isTokenTransfer'; -import { getIdentifierType } from 'utils/validation/getIdentifierType'; + import { useSignStepsClasses } from '../hooks'; -import { ConfirmAmount } from './components/ConfirmAmount'; -import { ConfirmFee } from './components/ConfirmFee'; -import { ConfirmReceiver } from './components/ConfirmReceiver'; -import { NftSftPreviewComponent } from './components/NftSftPreviewComponent'; +import { ConfirmAmount, ConfirmFee, ConfirmReceiver } from './components'; export interface SignStepInnerClassesType { buttonsWrapperClassName?: string; @@ -45,13 +37,10 @@ const SignStepBodyComponent = ({ globalStyles, styles }: SignStepBodyPropsType & WithStylesImportType) => { - const egldLabel = getEgldLabel(); - if (!currentTransaction) { return null; } - const { network } = useGetNetworkConfig(); const { inputGroupClassName, inputLabelClassName, @@ -59,119 +48,41 @@ const SignStepBodyComponent = ({ errorClassName } = signStepInnerClasses || {}; - const { tokenId, nonce, amount, multiTxData, receiver } = + const { tokenId, multiTxData, receiver, amount } = currentTransaction.transactionTokenInfo; - const isTokenTransaction = Boolean( - tokenId && isTokenTransfer({ tokenId, erdLabel: egldLabel }) - ); - - const { isNft, isEgld, isEsdt } = getIdentifierType(tokenId); - - // If the token has a nonce means that this is an NFT. Eg: TokenId=TOKEN-1hfr, nonce=123 => NFT id=TOKEN-1hfr-123 - const appendedNonce = nonce ? `-${nonce}` : ''; - const nftId = `${tokenId}${appendedNonce}`; - - const { tokenDecimals, tokenAvatar, tokenLabel, type, esdtPrice } = - useGetTokenDetails({ - tokenId: nonce && nonce?.length > 0 ? nftId : tokenId - }); - const transactionReceiver = multiTxData ? new Address(receiver).bech32() : currentTransaction.transaction.getReceiver().toString(); - const getFormattedAmount = ({ addCommas }: { addCommas: boolean }) => - formatAmount({ - input: isTokenTransaction - ? amount - : currentTransaction.transaction.getValue().toString(), - decimals: isTokenTransaction ? tokenDecimals : Number(network.decimals), - digits: Number(network.digits), - showLastNonZeroDecimal: false, - addCommas - }); - - const formattedAmount = getFormattedAmount({ addCommas: true }); - const rawAmount = getFormattedAmount({ addCommas: false }); - const scamReport = currentTransaction.receiverScamInfo; const classes = useSignStepsClasses(scamReport, globalStyles); - - const token = isNft ? nftId : tokenId || egldLabel; - const shownAmount = isNft ? amount : formattedAmount; - - const { price: egldPrice } = useGetEgldPrice(); - let tokenPrice; - - if (isEgld && egldPrice) { - tokenPrice = egldPrice; - } - - if (isNft) { - tokenPrice = null; - } - - if (isEsdt && type) { - tokenPrice = esdtPrice ?? null; - } - - const shouldShowAmount = - isEgld || isEsdt || (Boolean(type) && type !== NftEnumType.NonFungibleESDT); - const data = currentTransaction.transaction.getData().toString(); return (
- {isNft && type && ( - - )} + -
- {shouldShowAmount && ( -
- -
- )} - -
- -
-
+ {data && ( )} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/ConfirmAmount.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/ConfirmAmount.tsx index fd07d121a..56fee7eaf 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/ConfirmAmount.tsx +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/ConfirmAmount.tsx @@ -1,56 +1,102 @@ import React from 'react'; -import { DataTestIdsEnum } from 'constants/index'; -import { withStyles, WithStylesImportType } from 'hocs/withStyles'; +import classNames from 'classnames'; + +import { withStyles } from 'hocs/withStyles'; +import { useGetEgldPrice, useGetTokenDetails } from 'hooks'; +import { ActiveLedgerTransactionType } from 'types'; +import { NftEnumType } from 'types/tokens.types'; import { LoadingDots } from 'UI/LoadingDots'; -import { TokenDetails } from 'UI/TokenDetails'; -import { UsdValue } from 'UI/UsdValue'; -import { TokenAvatar, TokenAvatarPropsType } from '../TokenAvatar'; -export interface ConfirmAmountPropsType { - token: string; - formattedAmount: string; - rawAmount: string; - tokenAvatar?: string; - tokenType: TokenAvatarPropsType['type']; - tokenPrice?: number | null; +import { WithStylesImportType } from '../../../../../../hocs/useStyles'; + +import { + ConfirmAmountData, + ConfirmAmountLabel, + ConfirmAmountNftSft +} from './components'; +import { useHandleAmountReference } from './hooks'; + +export interface ConfirmAmountPropsType extends WithStylesImportType { + currentTransaction: ActiveLedgerTransactionType; } const ConfirmAmountComponent = ({ - token, - tokenAvatar, - tokenType, - formattedAmount, - rawAmount, - tokenPrice, - styles -}: ConfirmAmountPropsType & WithStylesImportType) => { - const isValidTokenPrice = tokenPrice != null; - const isLoadingTokenPrice = !isValidTokenPrice && tokenPrice !== null; + styles, + currentTransaction +}: ConfirmAmountPropsType) => { + const { tokenId, nonce, amount } = currentTransaction.transactionTokenInfo; + const { isFontSizeLoading, handleAmountReference } = + useHandleAmountReference(); + + // If the token has a nonce means that this is an NFT. Eg: TokenId=TOKEN-1hfr, nonce=123 => NFT id=TOKEN-1hfr-123 + const tokenIdForTokenDetails = + nonce && nonce.length > 0 ? `${tokenId}-${nonce}` : tokenId; + + const tokenDetails = useGetTokenDetails({ + tokenId: tokenIdForTokenDetails + }); + + const { price: egldPrice } = useGetEgldPrice(); + const { + type, + esdtPrice, + isLoading: isTokenDetailsLoading, + identifier + } = tokenDetails; + + const isEgld = !tokenId; + const tokenPrice = isEgld ? egldPrice : esdtPrice; + const isNftOrSft = type + ? [NftEnumType.SemiFungibleESDT, NftEnumType.NonFungibleESDT].includes(type) + : false; return ( -
- Amount +
+
+ {isTokenDetailsLoading ? ( + + ) : ( + + )} +
-
- +
+
- {formattedAmount} + {isNftOrSft ? ( + + ) : ( + + )}
- - {isLoadingTokenPrice && } - {isValidTokenPrice && ( - - )}
); }; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/ConfirmAmountData.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/ConfirmAmountData.tsx new file mode 100644 index 000000000..32ec7c925 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/ConfirmAmountData.tsx @@ -0,0 +1,114 @@ +import React from 'react'; +import { faCoins } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import classNames from 'classnames'; + +import { DataTestIdsEnum } from 'constants/index'; +import { withStyles } from 'hocs/withStyles'; +import { TokenOptionType, useGetNetworkConfig } from 'hooks'; +import { ActiveLedgerTransactionType } from 'types'; +import { Balance } from 'UI/Balance'; +import { UsdValue } from 'UI/UsdValue'; +import { formatAmount } from 'utils'; + +import { WithStylesImportType } from '../../../../../../../../hocs/useStyles'; + +export interface ConfirmAmountDataPropsType extends WithStylesImportType { + isEgld: boolean; + tokenPrice?: number; + isNftOrSft: boolean; + amount: string; + handleReference: (element: HTMLElement | null) => void; + currentTransaction: ActiveLedgerTransactionType; + tokenDetails: TokenOptionType; +} + +const ConfirmAmountDataComponent = ({ + isEgld, + styles, + tokenPrice, + isNftOrSft, + handleReference, + currentTransaction, + amount, + tokenDetails +}: ConfirmAmountDataPropsType) => { + const { network } = useGetNetworkConfig(); + const { tokenAvatar, tokenDecimals, identifier } = tokenDetails; + + const getFormattedAmount = ({ addCommas }: { addCommas: boolean }) => + formatAmount({ + input: isEgld + ? currentTransaction.transaction.getValue().toString() + : amount, + decimals: isEgld ? Number(network.decimals) : tokenDecimals, + digits: Number(network.digits), + showLastNonZeroDecimal: false, + addCommas + }); + + const formattedAmount = getFormattedAmount({ addCommas: true }); + const rawAmount = getFormattedAmount({ addCommas: false }); + + return ( +
+
+ {!isEgld && tokenAvatar && ( + + )} + + {!isEgld && !tokenAvatar && ( +
+ +
+ )} + +
+ +
+
+ + {!isNftOrSft && tokenPrice && ( + + )} + + {!isNftOrSft && !tokenPrice && ( +
Price Unknown
+ )} +
+ ); +}; + +export const ConfirmAmountData = withStyles(ConfirmAmountDataComponent, { + ssrStyles: () => + import( + 'UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/confirmAmountDataStyles.scss' + ), + clientStyles: () => + require('UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/confirmAmountDataStyles.scss') + .default +}); diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/confirmAmountDataStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/confirmAmountDataStyles.scss new file mode 100644 index 000000000..519771fd3 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/confirmAmountDataStyles.scss @@ -0,0 +1,48 @@ +.confirmAmountData { + display: flex; + flex-direction: column; + height: 48px; + justify-content: space-between; + + .confirmAmountDataWrapper { + display: flex; + align-items: center; + gap: 4px; + + .confirmAmountDataIcon { + width: 32px; + height: 32px; + + &.confirmAmountDataIconDefault { + padding: 10px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + border: 1px solid #737373; + } + + .confirmAmountDataIconDefaultIcon { + color: #ffffff; + } + } + + .confirmAmountDataBalanceWrapper { + flex: 1; + min-width: 0; + display: flex; + + .confirmAmountDataBalance { + display: inline-flex; + color: #0ac2ae; + font-weight: 500; + font-size: 32px; + } + } + } + + .confirmAmountDataPrice { + color: #737373; + font-size: 1rem; + } +} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/index.ts new file mode 100644 index 000000000..d9c1276b5 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountData/index.ts @@ -0,0 +1 @@ +export * from './ConfirmAmountData'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/ConfirmAmountLabel.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/ConfirmAmountLabel.tsx new file mode 100644 index 000000000..f59f59fc5 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/ConfirmAmountLabel.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import BigNumber from 'bignumber.js'; + +import { DataTestIdsEnum } from 'constants/index'; +import { withStyles } from 'hocs/withStyles'; +import { NftEnumType } from 'types/tokens.types'; + +import { WithStylesImportType } from '../../../../../../../../hocs/useStyles'; + +interface ConfirmAmountLabelPropsType extends WithStylesImportType { + type?: NftEnumType; + amount: string; + identifier?: string; +} + +const ConfirmAmountLabelComponent = ({ + amount, + styles, + type, + identifier +}: ConfirmAmountLabelPropsType) => { + const amountBigNumber = new BigNumber(amount); + const isAmountZero = amountBigNumber.isZero(); + const sftLabel = amountBigNumber.isEqualTo(1) ? 'SFT' : 'SFTs'; + const amountToLocaleString = amountBigNumber.toNumber().toLocaleString('en'); + const dataValue = `${amountToLocaleString} ${identifier}`; + + if (isAmountZero) { + return
You are using
; + } + + if (type === NftEnumType.NonFungibleESDT) { + return ( +
You are sending an NFT
+ ); + } + + if (type === NftEnumType.SemiFungibleESDT) { + return ( +
+ You are sending + + {amountToLocaleString} {sftLabel} + +
+ ); + } + + return
You are sending
; +}; + +export const ConfirmAmountLabel = withStyles(ConfirmAmountLabelComponent, { + ssrStyles: () => + import( + 'UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/confirmAmountLabelStyles.scss' + ), + clientStyles: () => + require('UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/confirmAmountLabelStyles.scss') + .default +}); diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/confirmAmountLabelStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/confirmAmountLabelStyles.scss new file mode 100644 index 000000000..acca1f19b --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/confirmAmountLabelStyles.scss @@ -0,0 +1,10 @@ +.confirmAmountLabel { + color: #a3a3a3; + display: flex; + align-items: center; + gap: 4px; + + .confirmAmountLabelValue { + color: #a5fcf0; + } +} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/index.ts new file mode 100644 index 000000000..3125dc2b2 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountLabel/index.ts @@ -0,0 +1 @@ +export * from './ConfirmAmountLabel'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/ConfirmAmountNftSft.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/ConfirmAmountNftSft.tsx new file mode 100644 index 000000000..67824b69a --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/ConfirmAmountNftSft.tsx @@ -0,0 +1,119 @@ +import React, { MouseEvent } from 'react'; +import { faArrowUpRightFromSquare } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { safeWindow } from '@multiversx/sdk-web-wallet-cross-window-provider/out/constants'; +import classNames from 'classnames'; + +import { DataTestIdsEnum } from 'constants/index'; +import { withStyles } from 'hocs/withStyles'; +import { TokenOptionType, useGetNetworkConfig } from 'hooks'; +import { NftEnumType } from 'types/tokens.types'; +import { + explorerUrlBuilder, + getExplorerLink +} from 'utils/transactions/getInterpretedTransaction/helpers'; + +import { WithStylesImportType } from '../../../../../../../../hocs/useStyles'; + +export interface ConfirmAmountNftSftPropsType extends WithStylesImportType { + type?: NftEnumType; + amount: string; + tokenDetails: TokenOptionType; +} + +const ConfirmAmountNftSftComponent = ({ + styles, + amount, + type, + tokenDetails +}: ConfirmAmountNftSftPropsType) => { + const { network } = useGetNetworkConfig(); + const { identifier, tokenAvatar, name } = tokenDetails; + + const isSft = NftEnumType.SemiFungibleESDT === type; + const mirroredSftAvatarsCount = 4; + const duplicatedSftAvatars = Array( + Math.min(mirroredSftAvatarsCount, Number(amount)) + ).fill(null); + + const handleNftSftClick = (event: MouseEvent) => { + if (!identifier) { + return; + } + + const explorerLink = getExplorerLink({ + explorerAddress: network.explorerAddress, + to: explorerUrlBuilder.nftDetails(identifier) + }); + + event.preventDefault(); + event.stopPropagation(); + + if (safeWindow.open) { + safeWindow.open(explorerLink); + } + }; + + return ( +
+
+ {tokenAvatar ? ( +
+ {duplicatedSftAvatars.map((_, index) => ( + {type} + ))} +
+ ) : ( +
+
+ {isSft ? 'SFT' : 'NFT'} +
+
+ )} + +
+
+ {name} +
+ +
+ {identifier} +
+
+
+ + +
+ ); +}; + +export const ConfirmAmountNftSft = withStyles(ConfirmAmountNftSftComponent, { + ssrStyles: () => + import( + 'UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/confirmAmountNftSftStyles.scss' + ), + clientStyles: () => + require('UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/confirmAmountNftSftStyles.scss') + .default +}); diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/confirmAmountNftSftStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/confirmAmountNftSftStyles.scss new file mode 100644 index 000000000..c3d413835 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/confirmAmountNftSftStyles.scss @@ -0,0 +1,68 @@ +.confirmAmountNftSft { + display: flex; + cursor: pointer; + justify-content: space-between; + align-items: center; + + &:hover .confirmAmountNftSftIcon { + color: #0ac2ae; + } + + .confirmAmountNftSftWrapper { + gap: 8px; + display: flex; + align-items: center; + + .confirmAmountNftSftIconWrapper { + position: relative; + + .confirmAmountNftSftIcon { + position: absolute; + top: 0; + left: 0; + width: 48px; + overflow: hidden; + border-radius: 8px; + height: 48px; + border: 1px solid #2e2e2e; + + &.confirmAmountNftSftIconRelative { + position: relative; + } + } + + .confirmAmountNftSftIconText { + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + border: 1px solid #737373; + color: #ffffff; + } + } + + .confirmAmountNftSftContent { + display: flex; + flex-direction: column; + gap: 4px; + + .confirmAmountNftSftName { + font-size: 16px; + color: #e5e5e5; + line-height: 1; + } + + .confirmAmountNftSftTicker { + color: #525252; + font-size: 10px; + } + } + } + + .confirmAmountNftSftIcon { + color: #525252; + transition: all 200ms ease-out; + } +} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/index.ts new file mode 100644 index 000000000..f23336fa6 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/ConfirmAmountNftSft/index.ts @@ -0,0 +1 @@ +export * from './ConfirmAmountNftSft'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/index.ts new file mode 100644 index 000000000..82bba3c33 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/components/index.ts @@ -0,0 +1,3 @@ +export * from './ConfirmAmountLabel'; +export * from './ConfirmAmountData'; +export * from './ConfirmAmountNftSft'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/confirmAmountStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/confirmAmountStyles.scss index 350a06ca6..a591bbc5c 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/confirmAmountStyles.scss +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/confirmAmountStyles.scss @@ -1,33 +1,38 @@ -.amount { +.confirmAmount { display: flex; flex-direction: column; - text-align: left; + line-height: 1; + gap: 8px; - .label { - margin-bottom: 8px; - color: #a3a3a3; - font-size: 12px; - line-height: 1; - font-weight: 400; - display: block; - } + .confirmAmountWrapper { + height: 48px; + position: relative; - .token { - display: flex; - align-items: center; + .confirmAmountLoading { + opacity: 0; + pointer-events: none; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; - .value { - font-size: 14px; - font-weight: 500; - color: #e5e5e5; - line-height: 1.5; + &.confirmAmountLoadingVisible { + opacity: 1; + } } - } - .price { - font-size: 12px; - color: #737373; - line-height: 1; - margin-top: 8px; + .confirmAmountContent { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + + &.confirmAmountContentHidden { + pointer-events: none; + opacity: 0; + } + } } } diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/hooks/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/hooks/index.ts new file mode 100644 index 000000000..7f4a1dea0 --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/hooks/index.ts @@ -0,0 +1 @@ +export * from './useHandleAmountReference'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/hooks/useHandleAmountReference.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/hooks/useHandleAmountReference.ts new file mode 100644 index 000000000..6f810480e --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmAmount/hooks/useHandleAmountReference.ts @@ -0,0 +1,47 @@ +import { useState } from 'react'; + +/* + * Hook that handles the font size of the amount, scaling it down based on the available size, and updating the state when it finished. + */ + +export const useHandleAmountReference = () => { + const [isFontSizeLoading, setIsFontSizeLoading] = useState(true); + + const getElementWidth = (element: HTMLElement) => + element.getBoundingClientRect().width; + + const getFontSize = (element: HTMLElement) => + parseInt(getComputedStyle(element).getPropertyValue('font-size')); + + const handleAmountReference = (element: HTMLElement | null) => { + if (!element) { + return; + } + + const firstChild = element.firstChild as HTMLElement; + const sizes = { + parent: element.offsetWidth, + firstChild: getFontSize(firstChild) + }; + + if (!firstChild) { + return; + } + + while (sizes.parent < getElementWidth(firstChild)) { + const updatedSize = sizes.firstChild - 0.1; + const updatedFontSize = { fontSize: `${updatedSize}px` }; + const updatedSizesObject = { firstChild: updatedSize }; + + Object.assign(firstChild.style, updatedFontSize); + Object.assign(sizes, updatedSizesObject); + } + + setIsFontSizeLoading(false); + }; + + return { + isFontSizeLoading, + handleAmountReference + }; +}; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmFee/ConfirmFee.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmFee/ConfirmFee.tsx index eba0d2a61..67a08934f 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmFee/ConfirmFee.tsx +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmFee/ConfirmFee.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Transaction } from '@multiversx/sdk-core/out'; + import { DataTestIdsEnum, GAS_PER_DATA_BYTE, @@ -7,25 +8,26 @@ import { } from 'constants/index'; import { withStyles, WithStylesImportType } from 'hocs/withStyles'; import { useGetEgldPrice } from 'hooks'; -import { FormatAmount } from 'UI/FormatAmount'; +import { Balance } from 'UI/Balance'; import { LoadingDots } from 'UI/LoadingDots'; -import { calculateFeeInFiat, calculateFeeLimit } from 'utils/operations'; -import { TokenAvatar } from '../TokenAvatar'; +import { getEgldLabel } from 'utils'; +import { + calculateFeeInFiat, + calculateFeeLimit, + formatAmount +} from 'utils/operations'; export interface FeePropsType { transaction: Transaction; - egldLabel: string; - tokenAvatar?: string; } const ConfirmFeeComponent = ({ transaction, - tokenAvatar, - egldLabel, styles }: FeePropsType & WithStylesImportType) => { const { price } = useGetEgldPrice(); + const egldLabel = getEgldLabel(); const feeLimit = calculateFeeLimit({ gasPerDataByte: String(GAS_PER_DATA_BYTE), gasPriceModifier: String(GAS_PRICE_MODIFIER), @@ -35,33 +37,51 @@ const ConfirmFeeComponent = ({ chainId: transaction.getChainID().valueOf() }); - return ( -
- Fee + const feeLimitFormatted = formatAmount({ + input: feeLimit, + showLastNonZeroDecimal: true + }); -
- + const feeInFiatLimit = price + ? calculateFeeInFiat({ + feeLimit, + egldPriceInUsd: price, + hideEqualSign: true + }) + : null; -
- -
-
+ return ( +
+
Transaction Fee
- - {price ? ( - calculateFeeInFiat({ - feeLimit, - egldPriceInUsd: price - }) +
+ + + {feeInFiatLimit ? ( + + ( + + ) + ) : ( - + + + )} - +
); }; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmFee/confirmFeeStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmFee/confirmFeeStyles.scss index 6b7f37c32..b6a838c7e 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmFee/confirmFeeStyles.scss +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmFee/confirmFeeStyles.scss @@ -1,33 +1,30 @@ -.fee { +.confirmFee { display: flex; flex-direction: column; - text-align: left; + line-height: 1; + gap: 8px; - .label { - margin-bottom: 8px; + .confirmFeeLabel { color: #a3a3a3; - font-size: 12px; - line-height: 1; - font-weight: 400; - display: block; } - .token { + .confirmFeeData { display: flex; align-items: center; + gap: 8px; - .value { - font-size: 14px; - font-weight: 500; - line-height: 1.5; - color: #e5e5e5; - } - } + .confirmFeeDataPriceWrapper { + display: flex; + align-items: center; + color: #737373; - .price { - font-size: 12px; - color: #737373; - line-height: 1; - margin-top: 8px; + .confirmFeeDataPrice { + color: #737373; + + * { + opacity: 1 !important; + } + } + } } } diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/ConfirmReceiver.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/ConfirmReceiver.tsx index 9b3389e2f..cefa709c5 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/ConfirmReceiver.tsx +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/ConfirmReceiver.tsx @@ -1,30 +1,39 @@ import React from 'react'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import classNames from 'classnames'; +import BigNumber from 'bignumber.js'; + +import { ACCOUNTS_ENDPOINT } from 'apiCalls'; +import MultiversXIconSimple from 'assets/icons/mvx-icon-simple.svg'; import { DataTestIdsEnum } from 'constants/index'; -import { withStyles, WithStylesImportType } from 'hocs/withStyles'; +import { withStyles } from 'hocs/withStyles'; import { useGetAccountFromApi } from 'hooks'; +import { trimUsernameDomain } from 'hooks/account/helpers'; +import { CopyButton } from 'UI/CopyButton'; +import { ExplorerLink } from 'UI/ExplorerLink'; import { LoadingDots } from 'UI/LoadingDots'; -import { isContract } from 'utils/smartContracts'; -import { ReceiverSubValue } from './components/ReceiverSubValue'; -import { ReceiverValue } from './components/ReceiverValue'; +import { Trim } from 'UI/Trim'; +import { isContract } from 'utils'; + +import { WithStylesImportType } from '../../../../../../hocs/useStyles'; -export interface ConfirmReceiverPropsType { +export interface ConfirmReceiverPropsType extends WithStylesImportType { receiver: string; scamReport: string | null; receiverUsername?: string; + amount: string; } const ConfirmReceiverComponent = ({ receiver, scamReport, receiverUsername, + amount, styles -}: ConfirmReceiverPropsType & WithStylesImportType) => { +}: ConfirmReceiverPropsType) => { const isSmartContract = isContract(receiver); - const skipFetchingAccount = Boolean(isSmartContract || receiverUsername); + const isAmountZero = new BigNumber(amount).isZero(); const { account: usernameAccount, @@ -40,43 +49,64 @@ const ConfirmReceiverComponent = ({ return (
- Receiver +
+
+ {isAmountZero ? 'To interact with' : 'To'} +
+ + {scamReport && ( +
+ + {scamReport} + + + +
+ )} +
{usernameAccountLoading ? ( - +
+ +
) : ( - - - - )} + - {usernameAccountLoading ? ( - - ) : ( - - )} + {hasUsername && !isSmartContract && ( + + ( + + {trimUsernameDomain(receiverValue)} + + ) + + )} - {scamReport && ( -
- - + {isSmartContract && ( + + ( + + Smart Contract + + ) + + )} - - {scamReport} - - + +
)}
diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/ReceiverSubValue.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/ReceiverSubValue.tsx deleted file mode 100644 index 33fa7c297..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/ReceiverSubValue.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { ACCOUNTS_ENDPOINT } from 'apiCalls'; -import { withStyles, WithStylesImportType } from 'hocs/withStyles'; -import { CopyButton } from 'UI/CopyButton'; -import { ExplorerLink } from 'UI/ExplorerLink'; -import { Trim } from 'UI/Trim'; -import { isContract } from 'utils'; - -export interface ReceiverSubValuePropsType { - hasUsername: boolean; - receiver: string; -} - -const ReceiverSubValueComponent = ({ - hasUsername, - receiver, - styles -}: ReceiverSubValuePropsType & WithStylesImportType) => { - const isSmartContract = isContract(receiver); - - if (hasUsername) { - return ( - - - - - ); - } - - if (isSmartContract) { - return ( - - - - - - ); - } - - return ( - - - - - - ); -}; - -export const ReceiverSubValue = withStyles(ReceiverSubValueComponent, { - ssrStyles: () => - import( - 'UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/receiverSubValueStyles.scss' - ), - clientStyles: () => - require('UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/receiverSubValueStyles.scss') - .default -}); diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/index.ts deleted file mode 100644 index 2ae5ec689..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ReceiverSubValue'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/receiverSubValueStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/receiverSubValueStyles.scss deleted file mode 100644 index 4b9c62332..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverSubValue/receiverSubValueStyles.scss +++ /dev/null @@ -1,42 +0,0 @@ -.subValue { - display: flex; - align-items: center; - gap: 8px; - height: 16px; - position: absolute; - right: 0; - bottom: 0; - left: 0; - - .subValueTrim { - line-height: 1; - color: var(--dapp-form-placeholder-color); - font-size: 12px; - display: flex; - align-items: flex-end; - max-width: none; - - [class*='left'] *, - [class*='right'] * { - color: var(--dapp-form-placeholder-color); - font-size: 12px !important; - } - } - - .subValueCopy { - font-size: 12px; - } - - .subValueExplorer { - margin-left: 0; - font-size: 12px; - - svg { - color: var(--dapp-form-placeholder-color) !important; - } - - &:hover svg { - color: var(--dapp-form-btn-bg) !important; - } - } -} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/ReceiverValue.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/ReceiverValue.tsx deleted file mode 100644 index 5533c15f8..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/ReceiverValue.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import { ACCOUNTS_ENDPOINT } from 'apiCalls'; -import MultiversXIconSimple from 'assets/icons/mvx-icon-simple.svg'; -import { withStyles, WithStylesImportType } from 'hocs/withStyles'; -import { trimUsernameDomain } from 'hooks/account/helpers'; -import { CopyButton } from 'UI/CopyButton'; -import { ExplorerLink } from 'UI/ExplorerLink'; -import { Trim } from 'UI/Trim'; - -export interface ReceiverValuePropsType { - hasUsername: boolean; - receiverValue: string; - receiverAddress: string; -} - -const ReceiverValueComponent = ({ - hasUsername, - receiverValue, - receiverAddress, - styles -}: ReceiverValuePropsType & WithStylesImportType) => { - if (hasUsername) { - return ( - - - {trimUsernameDomain(receiverValue)} - - - - ); - } - - return ( - - - - - ); -}; - -export const ReceiverValue = withStyles(ReceiverValueComponent, { - ssrStyles: () => - import( - 'UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/receiverValueStyles.scss' - ), - clientStyles: () => - require('UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/receiverValueStyles.scss') - .default -}); diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/index.ts deleted file mode 100644 index a598659cc..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ReceiverValue'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/receiverValueStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/receiverValueStyles.scss deleted file mode 100644 index b93c70b77..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/components/ReceiverValue/receiverValueStyles.scss +++ /dev/null @@ -1,37 +0,0 @@ -.receiverValue { - word-break: break-all; - line-height: 1.5; - height: 16px; - gap: 8px; - display: flex; - align-items: center; - - &.shrunk { - line-height: 1; - height: auto; - } - - .receiverValueIcon { - height: 16px; - position: relative; - width: auto; - top: 1px; - } - - .receiverValueCopy { - font-size: 12px; - } - - .receiverValueExplorer { - margin-left: 0; - font-size: 12px; - - svg { - color: var(--dapp-form-placeholder-color) !important; - } - - &:hover svg { - color: var(--dapp-form-btn-bg) !important; - } - } -} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/confirmReceiverStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/confirmReceiverStyles.scss index eede6395b..326cd8092 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/confirmReceiverStyles.scss +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/ConfirmReceiver/confirmReceiverStyles.scss @@ -1,41 +1,83 @@ +@import 'assets/sass/variables/variables'; + .receiver { - text-align: left; display: flex; flex-direction: column; + line-height: 1; gap: 8px; - position: relative; - padding-bottom: 24px; - width: 100%; - .valueWrapper { - font-size: 14px; - } + .receiverLabelWrapper { + display: flex; + gap: 8px; - .label { - color: #a3a3a3; - font-size: 12px; - line-height: 1; - font-weight: 400; - display: block; - } + .receiverLabel { + color: #a3a3a3; + } + + .receiverLabelScam { + color: $danger; + line-height: 1; + display: flex; + align-items: center; + gap: 4px; + position: relative; + padding-left: 16px; - .icon { - height: 16px; - position: relative; - width: auto; - top: 1px; + &:before { + top: 50%; + transform: translateY(-50%); + left: 0; + width: 10px; + content: ''; + position: absolute; + height: 1px; + background-color: #a3a3a3; + } + + .receiverLabelScamIcon { + color: $danger; + } + } } - .loadingDots { - font-size: 16px; + .receiverWrapper { + display: flex; + align-items: center; line-height: 1; - height: 16px; + white-space: nowrap; + gap: 8px; + + .receiverData { + color: #737373; + display: flex; + gap: calc(8px / 4); + align-items: center; + + .receiverDataIcon { + width: 10px; + height: auto; + margin-bottom: calc((8px / 4) * -1); + } + } + + .receiverCopy { + color: #737373; + min-width: 1rem; + max-height: 1rem; + } + + .receiverExternal { + margin: 0; + max-height: 1rem; + + &:hover * { + color: #0ac2ae !important; + } - &.absolute { - left: 0; - right: 0; - bottom: 0; - position: absolute; + * { + transition: all 200ms ease; + color: #737373 !important; + } } } } diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/NftSftPreviewComponent.styles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/NftSftPreviewComponent.styles.scss deleted file mode 100644 index 98937652f..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/NftSftPreviewComponent.styles.scss +++ /dev/null @@ -1,93 +0,0 @@ -.preview { - border-radius: 8px; - display: flex; - align-items: center; - cursor: pointer; - overflow: hidden; - background-color: #262626; - - .image { - min-width: 64px; - min-height: 64px; - max-width: 64px; - max-height: 64px; - height: auto; - width: auto; - } - - .content { - padding: 12px; - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - - .left { - gap: 8px; - line-height: 1; - display: flex; - flex-direction: column; - - .name { - font-size: 12px; - font-weight: 600; - text-align: left; - color: #e5e5e5; - } - - .identifier { - color: #737373; - font-size: 12px; - } - } - - .badge { - line-height: 1; - font-weight: 500; - font-size: 12px; - padding: 4px 8px; - text-transform: uppercase; - border-radius: 4px; - background-color: #171717; - position: relative; - - &:before { - content: ''; - border-radius: 4px; - position: absolute; - inset: 0; - padding: 1px; - mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - -webkit-mask: linear-gradient(#fff 0 0) content-box, - linear-gradient(#fff 0 0); - mask-composite: exclude; - pointer-events: none; - } - - &.nft { - color: #fef08a; - - &:before { - background: linear-gradient( - 90deg, - rgba(240, 152, 25, 0.4), - rgba(237, 222, 93, 0.4) - ); - } - } - - &.sft { - color: #fed7aa; - - &:before { - background: linear-gradient( - 90deg, - rgba(240, 77, 25, 0.4), - rgba(237, 136, 93, 0.4) - ); - } - } - } - } -} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/NftSftPreviewComponent.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/NftSftPreviewComponent.tsx deleted file mode 100644 index 4fd059f2b..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/NftSftPreviewComponent.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import { DataTestIdsEnum } from 'constants/index'; -import { withStyles, WithStylesImportType } from 'hocs/withStyles'; -import { useGetNetworkConfig } from 'hooks'; -import { NftEnumType } from 'types/tokens.types'; -import { - explorerUrlBuilder, - getExplorerLink -} from 'utils/transactions/getInterpretedTransaction/helpers'; - -export interface NftSftPreviewPropsType { - txType: NftEnumType; - tokenLabel: string; - tokenId: string; - tokenAvatar: string; -} - -export const NftSftPreview = ({ - txType, - tokenLabel, - tokenId, - tokenAvatar, - styles -}: NftSftPreviewPropsType & WithStylesImportType) => { - const { - network: { explorerAddress } - } = useGetNetworkConfig(); - - const badge = txType === NftEnumType.NonFungibleESDT ? 'NFT' : 'SFT'; - const explorerLink = getExplorerLink({ - explorerAddress, - to: explorerUrlBuilder.nftDetails(tokenId) - }); - - return ( - - {tokenLabel} - -
-
-
- {tokenLabel} -
- -
- {tokenId} -
-
-
-
- {badge} -
-
-
-
- ); -}; - -export const NftSftPreviewComponent = withStyles(NftSftPreview, { - ssrStyles: () => - import( - 'UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/NftSftPreviewComponent.styles.scss' - ), - clientStyles: () => - require('UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/NftSftPreviewComponent.styles.scss') - .default -}); diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/index.ts deleted file mode 100644 index a3a2fcb5a..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/NftSftPreviewComponent/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './NftSftPreviewComponent'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/TokenAvatar.tsx b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/TokenAvatar.tsx deleted file mode 100644 index 14fcc304e..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/TokenAvatar.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import { faDiamond } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import classNames from 'classnames'; -import MultiversXIcon from 'assets/icons/EGLD.svg'; -import { withStyles, WithStylesImportType } from 'hocs/withStyles'; -import { NftEnumType } from 'types/tokens.types'; -import { getEgldLabel } from 'utils/network'; - -export interface TokenAvatarPropsType { - type?: NftEnumType | string; - avatar?: string; -} - -const TokenAvatarComponent = ( - props: TokenAvatarPropsType & WithStylesImportType -) => { - const { avatar, type, styles } = props; - - const egldLabel = getEgldLabel(); - const isNft = type === NftEnumType.NonFungibleESDT; - const isSft = type === NftEnumType.SemiFungibleESDT; - const isEgld = type === egldLabel; - - const tokenIcon = avatar ? ( - {type} - ) : ( - - ); - - if (isNft || isSft) { - return ( -
- {isNft ? 'NFT' : 'SFT'} -
- ); - } - - return ( -
- {isEgld ? : tokenIcon} -
- ); -}; - -export const TokenAvatar = withStyles(TokenAvatarComponent, { - ssrStyles: () => - import( - 'UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/tokenAvatarStyles.scss' - ), - clientStyles: () => - require('UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/tokenAvatarStyles.scss') - .default -}); diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/index.ts deleted file mode 100644 index cc6924d99..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './TokenAvatar'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/tokenAvatarStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/tokenAvatarStyles.scss deleted file mode 100644 index fcf698954..000000000 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/TokenAvatar/tokenAvatarStyles.scss +++ /dev/null @@ -1,61 +0,0 @@ -.tokenAvatar { - width: 20px; - height: 20px; - display: flex; - align-items: center; - justify-content: center; - line-height: 1; - text-transform: uppercase; - border-radius: 50%; - min-width: 20px; - min-height: 20px; - border: 1px solid #737373; - margin-right: 4px; - position: relative; - font-size: 6px; - - &:before { - content: ''; - border-radius: 50%; - position: absolute; - inset: 0; - padding: 1px; - mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - -webkit-mask-composite: xor; - -webkit-mask: linear-gradient(#fff 0 0) content-box, - linear-gradient(#fff 0 0); - mask-composite: exclude; - pointer-events: none; - } - - img { - max-width: 100%; - height: auto; - } - - &.tokenAvatarNft { - border: none; - color: #fef08a; - - &:before { - background: linear-gradient( - 90deg, - rgba(240, 152, 25, 0.4), - rgba(237, 222, 93, 0.4) - ); - } - } - - &.tokenAvatarSft { - border: none; - color: #fed7aa; - - &:before { - background: linear-gradient( - 90deg, - rgba(240, 77, 25, 0.4), - rgba(237, 136, 93, 0.4) - ); - } - } -} diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/index.ts b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/index.ts new file mode 100644 index 000000000..94d58db4e --- /dev/null +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/components/index.ts @@ -0,0 +1,3 @@ +export * from './ConfirmAmount'; +export * from './ConfirmFee'; +export * from './ConfirmReceiver'; diff --git a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/signStepBodyStyles.scss b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/signStepBodyStyles.scss index 70d19367b..db7e871dc 100644 --- a/src/UI/SignTransactionsModals/SignWithDeviceModal/components/signStepBodyStyles.scss +++ b/src/UI/SignTransactionsModals/SignWithDeviceModal/components/signStepBodyStyles.scss @@ -8,19 +8,6 @@ gap: 32px; display: flex; flex-direction: column; - - .columns { - display: flex; - column-gap: 16px; - row-gap: 32px; - flex-wrap: wrap; - align-items: flex-start; - - .column { - min-width: calc(50% - 8px); - text-align: left; - } - } } .buttons { diff --git a/src/UI/SignTransactionsModals/types/signTransactionsModals.types.ts b/src/UI/SignTransactionsModals/types/signTransactionsModals.types.ts index 609fe0c46..45ca2e5fe 100644 --- a/src/UI/SignTransactionsModals/types/signTransactionsModals.types.ts +++ b/src/UI/SignTransactionsModals/types/signTransactionsModals.types.ts @@ -21,6 +21,7 @@ export interface CustomConfirmScreensType { Ledger?: ScreenType; Extension?: ScreenType; Opera?: ScreenType; + CrossWindow?: ScreenType; WalletConnect?: ScreenType; Wallet?: ScreenType; Extra?: ScreenType; diff --git a/src/UI/TransactionData/TransactionData.tsx b/src/UI/TransactionData/TransactionData.tsx index f0a2b588f..3bedc7f4a 100644 --- a/src/UI/TransactionData/TransactionData.tsx +++ b/src/UI/TransactionData/TransactionData.tsx @@ -2,6 +2,7 @@ import React from 'react'; import classNames from 'classnames'; import { DataTestIdsEnum, N_A } from 'constants/index'; import { withStyles, WithStylesImportType } from 'hocs/withStyles'; +import { CopyButton } from 'UI/CopyButton'; import { decodePart } from 'utils/decoders/decodePart'; import { getUnHighlightedDataFieldParts } from 'utils/transactions/getUnHighlightedDataFieldParts'; import { WithClassnameType } from '../types'; @@ -49,6 +50,14 @@ const TransactionDataComponent = ({ const occurrences = isHighlightedData ? allOccurences(data, highlight) : []; const showHighlight = isHighlightedData && occurrences.length > 0; + const handleElementReference = (element: HTMLElement | null) => { + if (!element) { + return; + } + + element.scrollIntoView(); + }; + if (showHighlight) { switch (true) { case data.startsWith(highlight): { @@ -56,7 +65,7 @@ const TransactionDataComponent = ({ output = ( <> - {highlight} + {highlight} {rest} ); @@ -68,7 +77,12 @@ const TransactionDataComponent = ({ output = ( <> {rest} - {highlight} + + {highlight} + ); break; @@ -85,7 +99,12 @@ const TransactionDataComponent = ({ output = ( <> {start} - {highlight} + + {highlight} + {end} ); @@ -94,49 +113,76 @@ const TransactionDataComponent = ({ } } + const decodedScCall = [ + decodePart(encodedScCall), + ...remainingDataFields + ].join('@'); + return ( <> {encodedScCall && ( -
+
- SC Call + Smart Contract Call -
- {[decodePart(encodedScCall), ...remainingDataFields].join('@')} +
+
+ + {decodedScCall} + + + {data && ( + + )} +
)} -
+
Data -
- {data ? output : N_A} +
+
+ + {data ? output : N_A} + + + {data && ( + + )} +
diff --git a/src/UI/TransactionData/TransactionDataStyles.scss b/src/UI/TransactionData/TransactionDataStyles.scss index 367192fda..85ddde9f6 100644 --- a/src/UI/TransactionData/TransactionDataStyles.scss +++ b/src/UI/TransactionData/TransactionDataStyles.scss @@ -1,24 +1,85 @@ -.data { +.transactionData { display: flex; flex-direction: column; - text-align: left; + line-height: 1; + gap: 8px; - .label { - margin-bottom: 8px; + .transactionDataLabel { color: #a3a3a3; - font-size: 12px; - line-height: 1; - font-weight: 400; - display: block; } - .value { - min-height: 80px; - font-size: 14px; - padding: 12px; - line-height: 1.25; - border-radius: 6px; + .transactionDataValueWrapper { + border-radius: 8px; border: 1px solid #262626; - word-break: break-all; + padding: 4px; + + .transactionDataValue { + min-height: 60px; + line-height: 1.25; + max-height: 60px; + overflow-y: auto; + word-break: break-all; + display: flex; + align-items: flex-start; + gap: 8px; + display: flex; + align-items: flex-start; + padding: 4px; + + &::-webkit-scrollbar, + &::-webkit-scrollbar-track, + &::-webkit-scrollbar-track:hover, + &::-webkit-scrollbar-corner { + background-color: transparent; + } + + &::-webkit-scrollbar { + width: calc(0.5rem + 8px); + } + + &::-webkit-scrollbar-track, + &::-webkit-scrollbar-track:hover { + border: 4px solid transparent; + background-clip: padding-box; + border-radius: 9999px; + background-color: transparent; + } + + &::-webkit-scrollbar-thumb, + &::-webkit-scrollbar-thumb:hover { + border: 4px solid transparent; + background-clip: padding-box; + border-radius: 9999px; + background-color: #404040; + } + + &::-webkit-scrollbar-button { + display: none; + } + + &::-webkit-resizer { + background-color: transparent; + } + + .transactionDataValueText { + flex: 1; + } + + .transactionDataValueCopy { + position: sticky; + min-width: 1rem; + max-width: 1rem; + top: 0; + + &:hover path { + color: #0ac2ae; + } + + path { + color: #737373; + transition: all 200ms ease; + } + } + } } } diff --git a/src/UI/TransactionInfo/components/AddressDetailItem/AddressDetailitem.tsx b/src/UI/TransactionInfo/components/AddressDetailItem/AddressDetailitem.tsx index be3903ed4..53c4ffcbb 100644 --- a/src/UI/TransactionInfo/components/AddressDetailItem/AddressDetailitem.tsx +++ b/src/UI/TransactionInfo/components/AddressDetailItem/AddressDetailitem.tsx @@ -12,7 +12,7 @@ const AddressDetailItemComponent = ({ address, styles }: { address: string } & WithStylesImportType) => ( - +
diff --git a/src/UI/TransactionInfo/components/ScResultsList/ScResultsList.tsx b/src/UI/TransactionInfo/components/ScResultsList/ScResultsList.tsx index 18e9f09e5..aef92cbbc 100644 --- a/src/UI/TransactionInfo/components/ScResultsList/ScResultsList.tsx +++ b/src/UI/TransactionInfo/components/ScResultsList/ScResultsList.tsx @@ -81,10 +81,7 @@ const ScResultsListComponent = ({ {result.value != null && ( - + )} diff --git a/src/UI/TransactionInfo/components/ScrDetailItem/ScrDetailItem.tsx b/src/UI/TransactionInfo/components/ScrDetailItem/ScrDetailItem.tsx index dd4a27983..85f7dd9ba 100644 --- a/src/UI/TransactionInfo/components/ScrDetailItem/ScrDetailItem.tsx +++ b/src/UI/TransactionInfo/components/ScrDetailItem/ScrDetailItem.tsx @@ -13,7 +13,7 @@ const ScrDetailItemComponent = ({ result, styles }: { result: ResultType } & WithStylesImportType) => ( - +
diff --git a/src/UI/TransactionInfo/components/TransactionAction/components/ActionText/index.tsx b/src/UI/TransactionInfo/components/TransactionAction/components/ActionText/index.tsx index 617a515f0..1c4f2d95c 100644 --- a/src/UI/TransactionInfo/components/TransactionAction/components/ActionText/index.tsx +++ b/src/UI/TransactionInfo/components/TransactionAction/components/ActionText/index.tsx @@ -68,7 +68,7 @@ const ActionTextComponent = ({ [styles?.spaced ?? '']: entry.token.length > 1 })} > - + {index < entry.token.length - 1 && ( , diff --git a/src/UI/TransactionInfo/components/TransactionAction/components/ActionToken.tsx b/src/UI/TransactionInfo/components/TransactionAction/components/ActionToken.tsx index 5fa07e262..955ecc495 100644 --- a/src/UI/TransactionInfo/components/TransactionAction/components/ActionToken.tsx +++ b/src/UI/TransactionInfo/components/TransactionAction/components/ActionToken.tsx @@ -82,9 +82,7 @@ const ActionTokenComponent = ({ showLastNonZeroDecimal }); - return ( - - ); + return ; } }; diff --git a/src/UI/TransactionInfo/components/transactionInfoFields/TransactionInfoAge/TransactionInfoAge.tsx b/src/UI/TransactionInfo/components/transactionInfoFields/TransactionInfoAge/TransactionInfoAge.tsx index 8a9f4cf11..eff59e302 100644 --- a/src/UI/TransactionInfo/components/transactionInfoFields/TransactionInfoAge/TransactionInfoAge.tsx +++ b/src/UI/TransactionInfo/components/transactionInfoFields/TransactionInfoAge/TransactionInfoAge.tsx @@ -48,7 +48,7 @@ const TransactionInfoAgeComponent = ({ /> )} - + (true); - - const onCloseModal = async () => { + const onCloseModal = () => { onClose?.(); }; @@ -28,7 +25,7 @@ const WalletConnectLoginContainerComponent = ( } if (!wrapContentInsideModal) { - return ; + return ; } return ( @@ -47,7 +44,7 @@ const WalletConnectLoginContainerComponent = ( onClose={onCloseModal} visible={showLoginModal} > - + ); }; diff --git a/src/UI/walletConnect/WalletConnectLoginContainer/WalletConnectLoginContent/WalletConnectLoginContent.tsx b/src/UI/walletConnect/WalletConnectLoginContainer/WalletConnectLoginContent/WalletConnectLoginContent.tsx index 502502331..c3e339f8d 100644 --- a/src/UI/walletConnect/WalletConnectLoginContainer/WalletConnectLoginContent/WalletConnectLoginContent.tsx +++ b/src/UI/walletConnect/WalletConnectLoginContainer/WalletConnectLoginContent/WalletConnectLoginContent.tsx @@ -27,7 +27,6 @@ const WalletConnectLoginContentComponent = ({ showScamPhishingAlert = true, title = 'Login with the xPortal App', token, - canLoginRef, globalStyles, styles, customRequestMethods = [] @@ -48,7 +47,6 @@ const WalletConnectLoginContentComponent = ({ nativeAuth, onLoginRedirect, logoutRoute, - canLoginRef, customRequestMethods }); @@ -98,12 +96,8 @@ const WalletConnectLoginContentComponent = ({ }, [walletConnectUriV2]); useEffect(() => { - if (canLoginRef?.current === false) { - return; - } - initLoginWithWalletConnectV2(); - }, [canLoginRef?.current]); + }, []); const authorizationInfo = showScamPhishingAlert ? getAuthorizationInfo(token, containerScamPhishingAlertClassName) diff --git a/src/UI/walletConnect/WalletConnectLoginContainer/types.ts b/src/UI/walletConnect/WalletConnectLoginContainer/types.ts index 8b68c11f0..f7dfb61b1 100644 --- a/src/UI/walletConnect/WalletConnectLoginContainer/types.ts +++ b/src/UI/walletConnect/WalletConnectLoginContainer/types.ts @@ -1,16 +1,15 @@ import { ReactNode, MutableRefObject } from 'react'; -import { OnProviderLoginType } from '../../../types'; +import { OnProviderLoginType } from '../../../types/login.types'; import { WithClassnameType } from '../../types'; import { InnerWalletConnectComponentsClassesType } from '../types'; export interface WalletConnectLoginModalPropsType extends OnProviderLoginType, WithClassnameType { - loginButtonText: string; + loginButtonText: ReactNode; customSpinnerComponent?: ReactNode; innerWalletConnectComponentsClasses?: InnerWalletConnectComponentsClassesType; - isWalletConnectV2?: boolean; lead?: string; legacyMessage?: string; logoutRoute?: string; @@ -20,6 +19,8 @@ export interface WalletConnectLoginModalPropsType showScamPhishingAlert?: boolean; title?: string; wrapContentInsideModal?: boolean; - canLoginRef?: MutableRefObject; customRequestMethods?: Array; + // deprecated/not used - kept for compatibility + isWalletConnectV2?: boolean; + canLoginRef?: MutableRefObject; } diff --git a/src/UI/webWallet/CrossWindowLoginButton/CrossWindowLoginButton.tsx b/src/UI/webWallet/CrossWindowLoginButton/CrossWindowLoginButton.tsx new file mode 100644 index 000000000..17161b02d --- /dev/null +++ b/src/UI/webWallet/CrossWindowLoginButton/CrossWindowLoginButton.tsx @@ -0,0 +1,56 @@ +import React, { ReactNode } from 'react'; +import { DataTestIdsEnum } from 'constants/dataTestIds.enum'; +import { useCrossWindowLogin } from 'hooks'; +import { getIsNativeAuthSingingForbidden } from 'services/nativeAuth/helpers'; +import { LoginButton } from 'UI/LoginButton/LoginButton'; +import { OnProviderLoginType } from '../../../types'; +import { WithClassnameType } from '../../types'; + +export interface CrossWindowLoginButtonPropsType + extends WithClassnameType, + OnProviderLoginType { + children?: ReactNode; + buttonClassName?: string; + loginButtonText?: string; + disabled?: boolean; +} + +export const CrossWindowLoginButton: ( + props: CrossWindowLoginButtonPropsType +) => JSX.Element = ({ + token, + className = 'dapp-window-wallet-login', + children, + callbackRoute, + buttonClassName, + nativeAuth, + loginButtonText = 'Window Wallet', + onLoginRedirect, + disabled, + 'data-testid': dataTestId = DataTestIdsEnum.accessCrossWindowWalletBtn +}) => { + const disabledConnectButton = getIsNativeAuthSingingForbidden(token); + const [onInitiateLogin] = useCrossWindowLogin({ + callbackRoute, + token, + onLoginRedirect, + nativeAuth + }); + + const handleLogin = () => { + onInitiateLogin(); + }; + + return ( + + {children} + + ); +}; diff --git a/src/UI/webWallet/CrossWindowLoginButton/index.tsx b/src/UI/webWallet/CrossWindowLoginButton/index.tsx new file mode 100644 index 000000000..6073e729d --- /dev/null +++ b/src/UI/webWallet/CrossWindowLoginButton/index.tsx @@ -0,0 +1 @@ +export * from './CrossWindowLoginButton'; diff --git a/src/apiCalls/accounts/getAccountFromApi.ts b/src/apiCalls/accounts/getAccountFromApi.ts index 434ba3610..51d77a426 100644 --- a/src/apiCalls/accounts/getAccountFromApi.ts +++ b/src/apiCalls/accounts/getAccountFromApi.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import { axiosInstance } from 'apiCalls/axiosInstance'; import { ACCOUNTS_ENDPOINT } from 'apiCalls/endpoints'; import { getCleanApiAddress } from 'apiCalls/utils'; import { AccountType } from 'types'; @@ -6,7 +6,7 @@ import { AccountType } from 'types'; export const accountFetcher = (address: string | null) => { const apiAddress = getCleanApiAddress(); const url = `${apiAddress}/${ACCOUNTS_ENDPOINT}/${address}?withGuardianInfo=true`; - return axios.get(url); + return axiosInstance.get(url); }; export async function getAccountFromApi(address?: string) { diff --git a/src/apiCalls/axiosInstance.ts b/src/apiCalls/axiosInstance.ts new file mode 100644 index 000000000..135c09660 --- /dev/null +++ b/src/apiCalls/axiosInstance.ts @@ -0,0 +1,99 @@ +import { buildAxiosFetch } from '@lifeomic/axios-fetch'; +import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; + +// Needs to be used beacause an async call made after cross-window user interaction makes the dapp unresponsive + +const fetch = buildAxiosFetch(axios); + +const getFormattedAxiosResponse = async (response: Response, config?: T) => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + // Clone the response to be able to read it twice (for status and data) + const clonedResponse = response.clone(); + + // Parse the JSON body asynchronously + const jsonPromise = clonedResponse.json(); + + // Return the standardized response object + const [responseData] = await Promise.all([jsonPromise]); + return { + data: responseData, + status: response.status, + statusText: response.statusText, + headers: response.headers, + config + }; +}; + +async function customPost, D = any>( + url: string, + data?: D, + config?: AxiosRequestConfig | undefined +): Promise { + try { + const response = await fetch(url, { + method: 'POST', + body: data ? JSON.stringify(data) : undefined, + headers: { + 'Content-Type': 'application/json', + ...(config?.headers || {}) + }, + ...config + } as RequestInit); + + return getFormattedAxiosResponse(response, config) as unknown as Promise; + } catch (error) { + console.error('Fetch Error:', error); + throw error; + } +} + +async function customGet, D = any>( + url: string, + config?: AxiosRequestConfig | undefined +): Promise { + try { + const response = await fetch(url, config as RequestInit); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + return getFormattedAxiosResponse(response, config) as unknown as Promise; + } catch (error) { + console.error('Fetch Error:', error); + throw error; + } +} + +async function customPatch, D = any>( + url: string, + data?: D, + config?: AxiosRequestConfig | undefined +): Promise { + try { + const response = await fetch(url, { + method: 'PATCH', + body: data ? JSON.stringify(data) : undefined, + headers: config?.headers || {}, + ...config + } as RequestInit); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + return getFormattedAxiosResponse(response, config) as unknown as Promise; + } catch (error) { + console.error('Fetch Error:', error); + throw error; + } +} + +const axiosInstance = axios.create(); +axiosInstance.get = customGet; +axiosInstance.post = customPost; +axiosInstance.patch = customPatch; + +export { axiosInstance }; diff --git a/src/components/ProviderInitializer/ProviderInitializer.tsx b/src/components/ProviderInitializer/ProviderInitializer.tsx index 69420e943..e7b394bba 100644 --- a/src/components/ProviderInitializer/ProviderInitializer.tsx +++ b/src/components/ProviderInitializer/ProviderInitializer.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import { getNetworkConfigFromApi } from 'apiCalls'; +import { getEnvironmentForChainId, getNetworkConfigFromApi } from 'apiCalls'; import { useLoginService } from 'hooks/login/useLoginService'; import { useWalletConnectV2Login } from 'hooks/login/useWalletConnectV2Login'; import { @@ -21,6 +21,7 @@ import { tokenLoginSelector } from 'reduxStore/selectors/loginInfoSelectors'; import { + chainIDSelector, networkSelector, walletAddressSelector } from 'reduxStore/selectors/networkConfigSelectors'; @@ -31,7 +32,9 @@ import { setLedgerAccount, setWalletLogin, setChainID, - setTokenLogin + setTokenLogin, + setIsWalletConnectV2Initialized, + setAddress } from 'reduxStore/slices'; import { LoginMethodsEnum } from 'types/enums.types'; import { @@ -43,7 +46,12 @@ import { } from 'utils/account'; import { parseNavigationParams } from 'utils/parseNavigationParams'; import { useWebViewLogin } from '../../hooks/login/useWebViewLogin'; -import { getExtensionProvider, getMultiSigLoginToken } from './helpers'; +import { + getOperaProvider, + getCrossWindowProvider, + getExtensionProvider, + processModifiedAccount +} from './helpers'; import { useSetLedgerProvider } from './hooks'; let initalizingLedger = false; @@ -58,6 +66,7 @@ export function ProviderInitializer() { const ledgerAccount = useSelector(ledgerAccountSelector); const ledgerLogin = useSelector(ledgerLoginSelector); const isLoggedIn = useSelector(isLoggedInSelector); + const chainID = useSelector(chainIDSelector); const tokenLogin = useSelector(tokenLoginSelector); const nativeAuthConfig = tokenLogin?.nativeAuthConfig; @@ -85,7 +94,7 @@ export function ProviderInitializer() { useEffect(() => { initializeProvider(); - }, [loginMethod]); + }, [loginMethod, chainID]); useEffect(() => { fetchAccount(); @@ -141,12 +150,16 @@ export function ProviderInitializer() { nonce: account.nonce.valueOf() }) ); + } else if (!isLoggedIn) { + // Clear the address and publicKey if account is not found + dispatch(setAddress('')); } } catch (e) { dispatch(setAccountLoadingError('Failed getting account')); console.error('Failed getting account ', e); } } + dispatch(setIsAccountLoading(false)); } @@ -162,12 +175,13 @@ export function ProviderInitializer() { const address = await getAddress(); const { clearNavigationHistory, - remainingParams: { signature, multisig } + remainingParams: { signature, multisig, impersonate } } = parseNavigationParams([ 'signature', 'loginToken', 'address', - 'multisig' + 'multisig', + 'impersonate' ]); if (!address) { @@ -177,29 +191,24 @@ export function ProviderInitializer() { return clearNavigationHistory(); } - const loginToken = await getMultiSigLoginToken({ + const account = await processModifiedAccount({ loginToken: tokenLogin?.loginToken, - multisig + extraInfoData: { + multisig, + impersonate + }, + address, + signature, + loginService }); - const accountAddress = loginToken != null ? multisig : address; - - if (loginToken != null) { - loginService.setLoginToken(loginToken); - } - - if (signature) { - loginService.setTokenLoginInfo({ signature, address }); - } - - const account = await getAccount(accountAddress); if (account) { initializedAccountRef.current = true; dispatch(setIsAccountLoading(true)); dispatch( loginAction({ - address: accountAddress, + address: account.address, loginMethod: LoginMethodsEnum.wallet }) ); @@ -231,14 +240,39 @@ export function ProviderInitializer() { async function setOperaProvider() { const address = await getAddress(); - const provider = await getExtensionProvider(address); + const provider = await getOperaProvider(address); + if (provider) { + setAccountProvider(provider); + } + } + + async function setCrossWindowProvider() { + const address = await getAddress(); + const provider = await getCrossWindowProvider({ + address, + walletUrl: network.walletAddress + }); if (provider) { setAccountProvider(provider); } } + async function setWalletConnectV2Provider() { + try { + // Trigger loader until wallet connect has been initialized + dispatch(setIsWalletConnectV2Initialized(true)); + await initWalletConnectV2LoginProvider(false); + } catch { + console.error('Could not initialize WalletConnect'); + } finally { + dispatch(setIsWalletConnectV2Initialized(false)); + } + } + async function initializeProvider() { - if (loginMethod == null || initalizingLedger) { + const isValidEnvironment = getEnvironmentForChainId(chainID); + + if (loginMethod == null || initalizingLedger || !isValidEnvironment) { return; } @@ -251,7 +285,7 @@ export function ProviderInitializer() { } case LoginMethodsEnum.walletconnectv2: { - initWalletConnectV2LoginProvider(false); + setWalletConnectV2Provider(); break; } @@ -265,6 +299,11 @@ export function ProviderInitializer() { break; } + case LoginMethodsEnum.crossWindow: { + setCrossWindowProvider(); + break; + } + case LoginMethodsEnum.extra: { setExternalProviderAsAccountProvider(); break; diff --git a/src/components/ProviderInitializer/helpers/getCrossWindowProvider.ts b/src/components/ProviderInitializer/helpers/getCrossWindowProvider.ts new file mode 100644 index 000000000..89be004ee --- /dev/null +++ b/src/components/ProviderInitializer/helpers/getCrossWindowProvider.ts @@ -0,0 +1,26 @@ +import { CrossWindowProvider } from '@multiversx/sdk-web-wallet-cross-window-provider'; + +export async function getCrossWindowProvider({ + address, + walletUrl +}: { + address: string; + walletUrl: string; +}) { + try { + const provider = CrossWindowProvider.getInstance() + .setAddress(address) + .setWalletUrl(walletUrl); + + const success = await provider.init(); + + if (success) { + return provider; + } else { + console.error('Could not initialise Cross Window Wallet provider'); + } + } catch (err) { + console.error('Unable to login to CrossWindowWalletProvider', err); + } + return null; +} diff --git a/src/components/ProviderInitializer/helpers/getMultiSigLoginToken.ts b/src/components/ProviderInitializer/helpers/getModifiedLoginToken.ts similarity index 77% rename from src/components/ProviderInitializer/helpers/getMultiSigLoginToken.ts rename to src/components/ProviderInitializer/helpers/getModifiedLoginToken.ts index ecc4b4209..3c92e3252 100644 --- a/src/components/ProviderInitializer/helpers/getMultiSigLoginToken.ts +++ b/src/components/ProviderInitializer/helpers/getModifiedLoginToken.ts @@ -4,14 +4,17 @@ import { nativeAuth } from 'services/nativeAuth/nativeAuth'; export interface GetMultiSigLoginTokenType { loginToken?: string; - multisig?: string; + extraInfoData: { + multisig?: string; + impersonate?: string; + }; } -export const getMultiSigLoginToken = async ({ +export const getModifiedLoginToken = async ({ loginToken, - multisig + extraInfoData }: GetMultiSigLoginTokenType) => { - if (loginToken == null || multisig == null) { + if (loginToken == null || Object.keys(extraInfoData).length === 0) { return null; } @@ -29,7 +32,7 @@ export const getMultiSigLoginToken = async ({ }; const tokenLogin = await nativeAuth({ - extraInfo: { ...rest, multisig }, + extraInfo: { ...rest, ...extraInfoData }, expirySeconds: data?.ttl, origin: data?.origin }).initialize({ diff --git a/src/components/ProviderInitializer/helpers/index.ts b/src/components/ProviderInitializer/helpers/index.ts index 43791fb14..7c63611e1 100644 --- a/src/components/ProviderInitializer/helpers/index.ts +++ b/src/components/ProviderInitializer/helpers/index.ts @@ -1,3 +1,5 @@ export * from './getOperaProvider'; export * from './getExtensionProvider'; -export * from './getMultiSigLoginToken'; +export * from './getCrossWindowProvider'; +export * from './processModifiedAccount'; +export * from './getModifiedLoginToken'; diff --git a/src/components/ProviderInitializer/helpers/processModifiedAccount.ts b/src/components/ProviderInitializer/helpers/processModifiedAccount.ts new file mode 100644 index 000000000..a4330198a --- /dev/null +++ b/src/components/ProviderInitializer/helpers/processModifiedAccount.ts @@ -0,0 +1,52 @@ +import { getAccount } from 'utils/account/getAccount'; +import { + getModifiedLoginToken, + GetMultiSigLoginTokenType +} from './getModifiedLoginToken'; + +interface SetMultisigLoginToken extends GetMultiSigLoginTokenType { + signature?: string; + address: string; + loginService: T; +} + +export const processModifiedAccount = async < + T extends { + setLoginToken: (loginToken: string) => void; + setTokenLoginInfo: ({ + address, + signature + }: { + address: string; + signature: string; + }) => string | undefined; + } +>({ + loginToken: token, + extraInfoData, + address, + signature, + loginService +}: SetMultisigLoginToken) => { + const loginToken = await getModifiedLoginToken({ + loginToken: token, + extraInfoData + }); + + const tokenAddress = + extraInfoData.multisig || extraInfoData.impersonate || address; + + const accountAddress = loginToken != null ? tokenAddress : address; + + if (loginToken != null) { + loginService.setLoginToken(loginToken); + } + + if (signature) { + loginService.setTokenLoginInfo({ signature, address }); + } + + const account = await getAccount(accountAddress); + + return account; +}; diff --git a/src/components/ProviderInitializer/helpers/tests/getMultiSigLoginToken.test.ts b/src/components/ProviderInitializer/helpers/tests/getModifiedLoginToken.test.ts similarity index 61% rename from src/components/ProviderInitializer/helpers/tests/getMultiSigLoginToken.test.ts rename to src/components/ProviderInitializer/helpers/tests/getModifiedLoginToken.test.ts index ee07d7e51..d5323da76 100644 --- a/src/components/ProviderInitializer/helpers/tests/getMultiSigLoginToken.test.ts +++ b/src/components/ProviderInitializer/helpers/tests/getModifiedLoginToken.test.ts @@ -1,19 +1,25 @@ -import { getMultiSigLoginToken } from '../getMultiSigLoginToken'; +import { getModifiedLoginToken } from '../getModifiedLoginToken'; describe('getMultiSigLoginToken', () => { test('returns null if loginToken or multisig is null', async () => { - const result = await getMultiSigLoginToken({ + const result = await getModifiedLoginToken({ loginToken: undefined, - multisig: 'erd1qqqqqqqqqqqqqpgq944h7h6mckw6q0d3g223cjz4ytvken86u00sz7carw' + extraInfoData: { + multisig: + 'erd1qqqqqqqqqqqqqpgq944h7h6mckw6q0d3g223cjz4ytvken86u00sz7carw' + } }); expect(result).toBeNull(); }); test('returns null if data or timestamp is missing', async () => { - const result = await getMultiSigLoginToken({ + const result = await getModifiedLoginToken({ loginToken: 'invalidLoginToken', - multisig: 'erd1qqqqqqqqqqqqqpgq944h7h6mckw6q0d3g223cjz4ytvken86u00sz7carw' + extraInfoData: { + multisig: + 'erd1qqqqqqqqqqqqqpgq944h7h6mckw6q0d3g223cjz4ytvken86u00sz7carw' + } }); expect(result).toBeNull(); @@ -23,10 +29,13 @@ describe('getMultiSigLoginToken', () => { const validInput = { loginToken: 'aHR0cHM6Ly9kZXZuZXQueGV4Y2hhbmdlLmNvbQ.d9ee880c609d5fe482a675826eb7e74f707c882e796ec191913a6c18d762685d.86400.eyJ0aW1lc3RhbXAiOjE3MDYxODAwMjd9', - multisig: 'erd1qqqqqqqqqqqqqpgq944h7h6mckw6q0d3g223cjz4ytvken86u00sz7carw' + extraInfoData: { + multisig: + 'erd1qqqqqqqqqqqqqpgq944h7h6mckw6q0d3g223cjz4ytvken86u00sz7carw' + } }; - const result = await getMultiSigLoginToken(validInput); + const result = await getModifiedLoginToken(validInput); const expectedTokenLogin = 'aHR0cHM6Ly9kZXZuZXQueGV4Y2hhbmdlLmNvbQ.d9ee880c609d5fe482a675826eb7e74f707c882e796ec191913a6c18d762685d.86400.eyJtdWx0aXNpZyI6ImVyZDFxcXFxcXFxcXFxcXFxcGdxOTQ0aDdoNm1ja3c2cTBkM2cyMjNjano0eXR2a2VuODZ1MDBzejdjYXJ3IiwidGltZXN0YW1wIjoxNzA2MTgwMDI3fQ'; diff --git a/src/constants/dataTestIds.enum.ts b/src/constants/dataTestIds.enum.ts index 34960b36b..fdb7046cf 100644 --- a/src/constants/dataTestIds.enum.ts +++ b/src/constants/dataTestIds.enum.ts @@ -1,5 +1,6 @@ export enum DataTestIdsEnum { accessWalletBtn = 'accessWalletBtn', + accessCrossWindowWalletBtn = 'accessCrossWindowWalletBtn', addressTableContainer = 'addressTableContainer', closeButton = 'closeButton', confirmAmount = 'confirmAmount', diff --git a/src/hooks/account/index.ts b/src/hooks/account/index.ts index 7b268aa94..55506a8d0 100644 --- a/src/hooks/account/index.ts +++ b/src/hooks/account/index.ts @@ -5,3 +5,4 @@ export * from './useGetIsLoggedIn'; export * from './useGetAccountProvider'; export * from './useGetWebsocketEvent'; export * from './useGetAccountFromApi'; +export * from './useGetIsWalletConnectV2Initialized'; diff --git a/src/hooks/account/useGetIsWalletConnectV2Initialized.ts b/src/hooks/account/useGetIsWalletConnectV2Initialized.ts new file mode 100644 index 000000000..80d6cae41 --- /dev/null +++ b/src/hooks/account/useGetIsWalletConnectV2Initialized.ts @@ -0,0 +1,6 @@ +import { useSelector } from 'reduxStore/DappProviderContext'; +import { isWalletConnectV2InitializedSelector } from 'reduxStore/selectors'; + +export const useGetIsWalletConnectV2Initialized = () => { + return useSelector(isWalletConnectV2InitializedSelector); +}; diff --git a/src/hooks/login/index.ts b/src/hooks/login/index.ts index 4619e41b8..de6d082c6 100644 --- a/src/hooks/login/index.ts +++ b/src/hooks/login/index.ts @@ -1,7 +1,8 @@ export { useExtensionLogin } from './useExtensionLogin'; +export { useOperaLogin } from './useOperaLogin'; +export { useCrossWindowLogin } from './useCrossWindowLogin'; +export { useWebWalletLogin } from './useWebWalletLogin'; export { useGetModalLoginMethods } from './useGetModalLoginMethods'; export { useLedgerLogin } from './useLedgerLogin'; -export { useOperaLogin } from './useOperaLogin'; export { useXaliasLogin } from './useXaliasLogin'; export { useWalletConnectV2Login } from './useWalletConnectV2Login'; -export { useWebWalletLogin } from './useWebWalletLogin'; diff --git a/src/hooks/login/useCrossWindowLogin.ts b/src/hooks/login/useCrossWindowLogin.ts new file mode 100644 index 000000000..3067d63db --- /dev/null +++ b/src/hooks/login/useCrossWindowLogin.ts @@ -0,0 +1,149 @@ +import { useState } from 'react'; + +import { CrossWindowProvider } from '@multiversx/sdk-web-wallet-cross-window-provider'; +import { processModifiedAccount } from 'components/ProviderInitializer/helpers/processModifiedAccount'; +import { SECOND_LOGIN_ATTEMPT_ERROR } from 'constants/errorsMessages'; +import { setAccountProvider } from 'providers/accountProvider'; +import { loginAction } from 'reduxStore/commonActions'; +import { useDispatch, useSelector } from 'reduxStore/DappProviderContext'; +import { networkSelector } from 'reduxStore/selectors/networkConfigSelectors'; +import { setAccount } from 'reduxStore/slices'; +import { + InitiateLoginFunctionType, + LoginHookGenericStateType, + OnProviderLoginType +} from 'types'; +import { LoginMethodsEnum } from 'types/enums.types'; +import { getLatestNonce } from 'utils/account/getLatestNonce'; +import { getIsLoggedIn } from 'utils/getIsLoggedIn'; +import { optionalRedirect } from 'utils/internal'; +import { getWindowLocation } from 'utils/window/getWindowLocation'; +import { useLoginService } from './useLoginService'; + +export type UseCrossWindowLoginReturnType = [ + InitiateLoginFunctionType, + LoginHookGenericStateType +]; + +export const useCrossWindowLogin = ({ + callbackRoute, + token: tokenToSign, + nativeAuth, + onLoginRedirect +}: OnProviderLoginType): UseCrossWindowLoginReturnType => { + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const hasNativeAuth = nativeAuth != null; + const loginService = useLoginService(nativeAuth); + let token = tokenToSign; + const network = useSelector(networkSelector); + + const dispatch = useDispatch(); + const isLoggedIn = getIsLoggedIn(); + + async function initiateLogin() { + if (isLoggedIn) { + throw new Error(SECOND_LOGIN_ATTEMPT_ERROR); + } + + setIsLoading(true); + const provider: CrossWindowProvider = + CrossWindowProvider.getInstance().setWalletUrl(network.walletAddress); + + try { + const isSuccessfullyInitialized: boolean = await provider.init(); + + if (!isSuccessfullyInitialized) { + console.warn( + 'Something went wrong trying to redirect to wallet login..' + ); + return; + } + + const { origin, pathname } = getWindowLocation(); + const callbackUrl: string = encodeURIComponent( + `${origin}${callbackRoute ?? pathname}` + ); + + if (hasNativeAuth && !token) { + token = await loginService.getNativeAuthLoginToken(); + + // Fetching block failed + if (!token) { + console.warn('Fetching block failed. Login cancelled.'); + return; + } + } + + if (token) { + loginService.setLoginToken(token); + } + + const providerLoginData = { + callbackUrl, + ...(token && { token }) + }; + + const { signature, address, multisig, impersonate } = + await provider.login(providerLoginData); + + setAccountProvider(provider); + + if (!address) { + setIsLoading(false); + console.warn('Login cancelled.'); + return; + } + + const account = await processModifiedAccount({ + loginToken: token, + extraInfoData: { multisig, impersonate }, + address, + signature, + loginService + }); + + if (!account) { + return; + } + + dispatch( + loginAction({ + address: account.address, + loginMethod: LoginMethodsEnum.crossWindow + }) + ); + + dispatch( + setAccount({ + ...account, + nonce: getLatestNonce(account) + }) + ); + + optionalRedirect({ + callbackRoute, + onLoginRedirect, + options: { signature, address: account.address } + }); + } catch (error) { + console.error('error loging in', error); + // TODO: can be any or typed error + setError('error logging in' + (error as any).message); + } finally { + setIsLoading(false); + } + } + + const loginFailed = Boolean(error); + + return [ + initiateLogin, + { + loginFailed, + error, + isLoading: isLoading && !loginFailed, + isLoggedIn: isLoggedIn && !loginFailed + } + ]; +}; diff --git a/src/hooks/login/useWalletConnectV2Login.ts b/src/hooks/login/useWalletConnectV2Login.ts index 7555681ae..1e48fd6ce 100644 --- a/src/hooks/login/useWalletConnectV2Login.ts +++ b/src/hooks/login/useWalletConnectV2Login.ts @@ -1,4 +1,4 @@ -import { MutableRefObject, useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useGetAccountProvider } from 'hooks/account'; import { useUpdateEffect } from 'hooks/useUpdateEffect'; @@ -6,9 +6,8 @@ import { setAccountProvider } from 'providers/accountProvider'; import { emptyProvider, getProviderType } from 'providers/utils'; import { loginAction } from 'reduxStore/commonActions'; import { useDispatch, useSelector } from 'reduxStore/DappProviderContext'; -import { logoutRouteSelector } from 'reduxStore/selectors'; +import { loginMethodSelector, logoutRouteSelector } from 'reduxStore/selectors'; import { - chainIDSelector, walletConnectDeepLinkSelector, walletConnectV2OptionsSelector, walletConnectV2ProjectIdSelector, @@ -16,13 +15,11 @@ import { } from 'reduxStore/selectors/networkConfigSelectors'; import { setWalletConnectLogin } from 'reduxStore/slices'; import { LoginMethodsEnum } from 'types/enums.types'; -import { - LoginHookGenericStateType, - OnProviderLoginType -} from 'types/login.types'; +import { getHasNativeAuth } from 'utils/getHasNativeAuth'; import { getIsLoggedIn } from 'utils/getIsLoggedIn'; import { optionalRedirect } from 'utils/internal'; import { logout } from 'utils/logout'; +import { waitForChainID } from 'utils/waitForChainID'; import { PairingTypes, SessionEventTypes, @@ -30,6 +27,10 @@ import { WalletConnectV2Provider } from 'utils/walletconnect/__sdkWalletconnectProvider'; import { getWindowLocation } from 'utils/window/getWindowLocation'; +import { + LoginHookGenericStateType, + OnProviderLoginType +} from '../../types/login.types'; import { useLoginService } from './useLoginService'; export enum WalletConnectV2Error { @@ -40,12 +41,12 @@ export enum WalletConnectV2Error { connectError = 'Unable to connect', userRejected = 'User rejected connection proposal', userRejectedExisting = 'User rejected existing connection proposal', - errorLogout = 'Unable to remove existing pairing' + errorLogout = 'Unable to remove existing pairing', + invalidChainID = 'Invalid chainID' } export interface InitWalletConnectV2Type extends OnProviderLoginType { logoutRoute?: string; - canLoginRef?: MutableRefObject; customRequestMethods?: Array; } @@ -59,7 +60,7 @@ export interface WalletConnectV2LoginHookCustomStateType { } export type WalletConnectV2LoginHookReturnType = [ - (loginProvider?: boolean) => void, + (loginProvider?: boolean) => Promise, LoginHookGenericStateType, WalletConnectV2LoginHookCustomStateType ]; @@ -70,12 +71,11 @@ export const useWalletConnectV2Login = ({ nativeAuth, onLoginRedirect, logoutRoute: providerLogoutRoute, - canLoginRef: parentCanLoginRef, customRequestMethods = [] }: InitWalletConnectV2Type): WalletConnectV2LoginHookReturnType => { const dispatch = useDispatch(); - const hasNativeAuth = nativeAuth != null; - const loginService = useLoginService(nativeAuth); + const hasNativeAuth = getHasNativeAuth(nativeAuth); + const loginService = useLoginService(hasNativeAuth ? nativeAuth : false); let token = tokenToSign; const [error, setError] = useState(''); @@ -84,19 +84,21 @@ export const useWalletConnectV2Login = ({ const [wcPairings, setWcPairings] = useState< PairingTypes.Struct[] | undefined >([]); + const [sessionProvider, setSessionProvider] = + useState(null); - const { provider, providerType } = useGetAccountProvider(); + const { provider } = useGetAccountProvider(); const walletConnectV2RelayAddress = useSelector(walletConnectV2RelaySelector); const walletConnectV2ProjectId = useSelector( walletConnectV2ProjectIdSelector ); const walletConnectV2Options = useSelector(walletConnectV2OptionsSelector); - const chainId = useSelector(chainIDSelector); const walletConnectDeepLink = useSelector(walletConnectDeepLinkSelector); const dappLogoutRoute = useSelector(logoutRouteSelector); + const loginMethod = useSelector(loginMethodSelector); const providerRef = useRef(provider); - const canLoginRef = parentCanLoginRef ?? useRef(false); const isInitialisingRef = useRef(false); + const mounted = useRef(false); const logoutRoute = providerLogoutRoute ?? dappLogoutRoute ?? '/'; const dappMethods: string[] = [ @@ -129,21 +131,11 @@ export const useWalletConnectV2Login = ({ const handleOnLogin = async () => { try { - const isLoggedIn = getIsLoggedIn(); - const isLoggedInWithDifferentProvider = - isLoggedIn && - providerType && - providerType !== LoginMethodsEnum.walletconnectv2; - - if ( - isInitialisingRef.current || - providerRef.current == null || - isLoggedInWithDifferentProvider - ) { + if (isInitialisingRef.current || providerRef.current == null) { return; } - if (!canLoginRef.current) { + if (!mounted.current) { try { await providerRef.current?.logout(); } catch { @@ -191,10 +183,6 @@ export const useWalletConnectV2Login = ({ }; const cancelLogin = async () => { - if (canLoginRef.current) { - canLoginRef.current = false; - } - const providerType = getProviderType(providerRef.current); if (providerType !== LoginMethodsEnum.walletconnectv2) { @@ -210,7 +198,7 @@ export const useWalletConnectV2Login = ({ } providerRef.current = emptyProvider; - setAccountProvider(emptyProvider); + setSessionProvider(null); } catch { console.warn('Unable to logout'); } @@ -281,41 +269,38 @@ export const useWalletConnectV2Login = ({ }; async function initiateLogin(loginProvider = true) { + const chainId = await waitForChainID({ maxRetries: 15 }); + + if (!chainId) { + setError(WalletConnectV2Error.invalidChainID); + return; + } + if (!walletConnectV2ProjectId || !walletConnectV2RelayAddress) { setError(WalletConnectV2Error.invalidConfig); return; } const isLoggedIn = getIsLoggedIn(); - const isLoggedInWithDifferentProvider = - isLoggedIn && - providerType && - providerType !== LoginMethodsEnum.walletconnectv2; - const cannotLogin = canLoginRef.current === false && !isLoggedIn; + const cannotLogin = mounted.current === false && !isLoggedIn; const isInitialized = providerRef.current?.isInitialized?.(); - if ( - isInitialisingRef.current || - cannotLogin || - isLoggedInWithDifferentProvider || - isInitialized - ) { + if (isInitialisingRef.current || cannotLogin || isInitialized) { return; } isInitialisingRef.current = true; if (providerRef.current?.walletConnector) { - providerRef.current.init(); + await providerRef.current.init(); - setAccountProvider(providerRef.current); + setSessionProvider(providerRef.current); isInitialisingRef.current = false; - canLoginRef.current = true; if (loginProvider) { - generateWcUri(); + await generateWcUri(); } return; @@ -336,14 +321,13 @@ export const useWalletConnectV2Login = ({ ); await newProvider.init(); - setAccountProvider(newProvider); + setSessionProvider(newProvider); providerRef.current = newProvider; isInitialisingRef.current = false; - canLoginRef.current = true; if (loginProvider) { setWcPairings(newProvider.pairings); - generateWcUri(); + await generateWcUri(); } return; @@ -356,6 +340,11 @@ export const useWalletConnectV2Login = ({ } try { + // Do not do any other actions if component is not mounted + if (!mounted.current) { + return; + } + const { uri, approval } = await providerRef.current?.connect({ methods: dappMethods }); @@ -398,28 +387,38 @@ export const useWalletConnectV2Login = ({ } } - useUpdateEffect(() => { - if (!tokenToSign || !providerRef.current?.connect) { - return; - } - - generateWcUri(); - }, [tokenToSign, providerRef.current?.connect]); - - useUpdateEffect(() => { - if (canLoginRef?.current === false) { - cancelLogin(); - } - }, [canLoginRef.current]); - useUpdateEffect(() => { providerRef.current = provider; }, [provider]); + useEffect(() => { + mounted.current = true; + + return () => { + mounted.current = false; + }; + }, []); + useEffect(() => { setIsLoading(!Boolean(wcUri)); }, [wcUri]); + useEffect(() => { + if (!sessionProvider) { + return; + } + + // Check if a new session has been created is already connected + const isConnected = + Boolean(sessionProvider.session) || + loginMethod === LoginMethodsEnum.walletconnectv2; + + // Set new provider only if account is logged in and if walletConnect session is available + if (isConnected && isLoggedIn) { + setAccountProvider(sessionProvider); + } + }, [sessionProvider, isLoggedIn]); + return [ initiateLogin, { diff --git a/src/hooks/transactions/helpers/checkNeedsGuardianSigning.ts b/src/hooks/transactions/helpers/checkNeedsGuardianSigning.ts index 5e47ae8a1..bbb506c1f 100644 --- a/src/hooks/transactions/helpers/checkNeedsGuardianSigning.ts +++ b/src/hooks/transactions/helpers/checkNeedsGuardianSigning.ts @@ -1,5 +1,6 @@ import { Transaction } from '@multiversx/sdk-core'; import { getEnvironmentForChainId } from 'apiCalls/configuration'; +import { getCrossWindowProvider } from 'components/ProviderInitializer/helpers'; import { WALLET_SIGN_SESSION, fallbackNetworkConfigurations @@ -32,17 +33,19 @@ export const checkNeedsGuardianSigning = ({ transactions }); + const chainId = transactions[0].getChainID().valueOf(); + const environment = getEnvironmentForChainId(chainId); + const walletProviderAddress = + walletAddress ?? fallbackNetworkConfigurations[environment].walletAddress; + /** + * @deprecated Since version 2.29.0, use {@link guardTransactions} instead. * Redirect to wallet for signing if: * - account is guarded & * - 2FA will not be provided locally & * - transactions were not signed by guardian */ const sendTransactionsToGuardian = () => { - const chainId = transactions[0].getChainID().valueOf(); - const environment = getEnvironmentForChainId(chainId); - const walletProviderAddress = - walletAddress ?? fallbackNetworkConfigurations[environment].walletAddress; const walletProvider = newWalletProvider(walletProviderAddress); const urlParams = { [WALLET_SIGN_SESSION]: String(sessionId) }; const { origin } = getWindowLocation(); @@ -56,11 +59,23 @@ export const checkNeedsGuardianSigning = ({ }); }; + const guardTransactions = async () => { + const provider = await getCrossWindowProvider({ + address: transactions[0].getSender().toString(), + walletUrl: walletProviderAddress + }); + const transactionsSignedByGuardian = await provider?.guardTransactions( + transactions + ); + return transactionsSignedByGuardian ?? []; + }; + const needs2FaSigning = !hasGuardianScreen && !allSignedByGuardian && sessionId; return { needs2FaSigning: isGuarded ? needs2FaSigning : false, - sendTransactionsToGuardian + sendTransactionsToGuardian, + guardTransactions }; }; diff --git a/src/hooks/transactions/helpers/getAreAllTransactionsSignedByGuardian.ts b/src/hooks/transactions/helpers/getAreAllTransactionsSignedByGuardian.ts index e71e380d6..3a6dd71cf 100644 --- a/src/hooks/transactions/helpers/getAreAllTransactionsSignedByGuardian.ts +++ b/src/hooks/transactions/helpers/getAreAllTransactionsSignedByGuardian.ts @@ -7,10 +7,15 @@ export const getAreAllTransactionsSignedByGuardian = ({ transactions: Transaction[]; isGuarded?: boolean; }) => { - const allSignedByGuardian = isGuarded - ? transactions.every((tx) => - Boolean(tx.getGuardianSignature().toString('hex')) - ) - : true; - return allSignedByGuardian; + if (!isGuarded) { + return true; + } + + if (transactions.length === 0) { + return false; + } + + return transactions.every((tx) => + Boolean(tx.getGuardianSignature().toString('hex')) + ); }; diff --git a/src/hooks/transactions/useGetTokenDetails.tsx b/src/hooks/transactions/useGetTokenDetails.tsx index 2fddd2a85..fc79a825c 100644 --- a/src/hooks/transactions/useGetTokenDetails.tsx +++ b/src/hooks/transactions/useGetTokenDetails.tsx @@ -25,7 +25,7 @@ export interface TokenMediaType { fileSize?: number; } -interface TokenOptionType { +export interface TokenOptionType { tokenLabel: string; tokenDecimals: number; tokenAvatar: string; @@ -33,6 +33,10 @@ interface TokenOptionType { type?: NftEnumType; error?: string; esdtPrice?: number; + ticker?: string; + identifier?: string; + name?: string; + isLoading?: boolean; } interface TokenInfoResponse { @@ -46,6 +50,12 @@ interface TokenInfoResponse { price: number; } +interface TokenInfoResponseDataType { + data?: TokenInfoResponse; + error?: string; + isLoading?: boolean; +} + const fetcher = (url: string) => axios.get(url).then((response) => response.data); @@ -62,8 +72,9 @@ export function useGetTokenDetails({ const { data: selectedToken, - error - }: { data?: TokenInfoResponse; error?: string } = useSwr( + error, + isLoading + }: TokenInfoResponseDataType = useSwr( Boolean(tokenIdentifier) ? `${network.apiAddress}/${tokenEndpoint}/${tokenIdentifier}` : null, @@ -87,12 +98,16 @@ export function useGetTokenDetails({ : ''; return { + isLoading, tokenDecimals: tokenDecimals, tokenLabel, type: selectedToken?.type, tokenAvatar, + identifier: selectedToken?.identifier, assets: selectedToken?.assets, esdtPrice: selectedToken?.price, + ticker: selectedToken?.ticker, + name: selectedToken?.name, error }; } diff --git a/src/hooks/transactions/useSignMultipleTransactions.tsx b/src/hooks/transactions/useSignMultipleTransactions.tsx index 6b7dff1a4..e57aad6ae 100644 --- a/src/hooks/transactions/useSignMultipleTransactions.tsx +++ b/src/hooks/transactions/useSignMultipleTransactions.tsx @@ -157,6 +157,19 @@ export function useSignMultipleTransactions({ } async function sign() { + const existingSignedTransactions = Object.values(signedTransactions ?? {}); + + const alreadySignedByGuardian = getAreAllTransactionsSignedByGuardian({ + isGuarded, + transactions: existingSignedTransactions + }); + + if (isGuarded && alreadySignedByGuardian) { + onTransactionsSignSuccess(existingSignedTransactions); + reset(); + return; + } + if (currentTransaction == null) { return; } @@ -174,7 +187,8 @@ export function useSignMultipleTransactions({ : null; reset(); - return onTransactionsSignError(errorMessage ?? message); + onTransactionsSignError(errorMessage ?? message); + return; } if (!signedTx) { @@ -212,7 +226,7 @@ export function useSignMultipleTransactions({ reset(); } - function signTx() { + async function signTx() { try { if (currentTransaction == null) { return; @@ -224,10 +238,10 @@ export function useSignMultipleTransactions({ return; } - sign(); + await sign(); } catch { // the only way to check if tx has signature is with try catch - sign(); + await sign(); } } @@ -249,12 +263,12 @@ export function useSignMultipleTransactions({ ) ); - function handleSignTransaction() { + async function handleSignTransaction() { if (shouldContinueWithoutSigning) { setCurrentStep((exising) => exising + 1); - } else { - signTx(); + return; } + await signTx(); } function onNext() { diff --git a/src/hooks/transactions/useSignTransactions.tsx b/src/hooks/transactions/useSignTransactions.tsx index d2e91c1b3..ee56348c0 100644 --- a/src/hooks/transactions/useSignTransactions.tsx +++ b/src/hooks/transactions/useSignTransactions.tsx @@ -6,6 +6,7 @@ import { } from '@multiversx/sdk-core'; import { ExtensionProvider } from '@multiversx/sdk-extension-provider'; +import { CrossWindowProvider } from '@multiversx/sdk-web-wallet-cross-window-provider'; import uniq from 'lodash/uniq'; import { ERROR_SIGNING, @@ -80,18 +81,25 @@ export const useSignTransactions = () => { function clearSignInfo(sessionId?: string) { const isExtensionProvider = provider instanceof ExtensionProvider; + const isCrossWindowProvider = provider instanceof CrossWindowProvider; dispatch(clearAllTransactionsToSign()); dispatch(clearTransactionsInfoForSessionId(sessionId)); isSigningRef.current = false; - if (!isExtensionProvider) { + if (!isExtensionProvider && !isCrossWindowProvider) { return; } clearTransactionStatusMessage(); - ExtensionProvider.getInstance()?.cancelAction?.(); + + if (isExtensionProvider) { + ExtensionProvider.getInstance()?.cancelAction?.(); + } + if (isCrossWindowProvider) { + CrossWindowProvider.getInstance()?.cancelAction?.(); + } } const onCancel = (errorMessage: string, sessionId?: string) => { @@ -153,6 +161,7 @@ export const useSignTransactions = () => { callbackRoute, customTransactionInformation } = transactionsToSign; + const { redirectAfterSign } = customTransactionInformation; const defaultCallbackUrl = getDefaultCallbackUrl(); const redirectRoute = callbackRoute || defaultCallbackUrl; @@ -200,23 +209,24 @@ export const useSignTransactions = () => { return; } - const signedTransactionsArray = Object.values(signedTransactions).map( - (tx) => parseTransactionAfterSigning(tx) - ); + const { needs2FaSigning, guardTransactions } = checkNeedsGuardianSigning({ + transactions: signedTransactions, + sessionId, + callbackRoute, + isGuarded: isGuarded && allowGuardian, + walletAddress + }); - const { needs2FaSigning, sendTransactionsToGuardian } = - checkNeedsGuardianSigning({ - transactions: signedTransactions, - sessionId, - callbackRoute, - isGuarded: isGuarded && allowGuardian, - walletAddress - }); + let finalizedTransactions = signedTransactions; if (needs2FaSigning) { - return sendTransactionsToGuardian(); + finalizedTransactions = await guardTransactions(); } + const signedTransactionsArray = Object.values(finalizedTransactions).map( + (tx) => parseTransactionAfterSigning(tx) + ); + const payload: MoveTransactionsToSignedStatePayloadType = { sessionId, transactions: signedTransactionsArray, diff --git a/src/hooks/transactions/useSignTransactionsCommonData.tsx b/src/hooks/transactions/useSignTransactionsCommonData.tsx index d9c49bf22..564131f5e 100644 --- a/src/hooks/transactions/useSignTransactionsCommonData.tsx +++ b/src/hooks/transactions/useSignTransactionsCommonData.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import { Transaction } from '@multiversx/sdk-core/out'; import { ExtensionProvider } from '@multiversx/sdk-extension-provider'; +import { CrossWindowProvider } from '@multiversx/sdk-web-wallet-cross-window-provider'; import { useGetAccount } from 'hooks/account'; import { useGetAccountProvider } from 'hooks/account/useGetAccountProvider'; import { useParseSignedTransactions } from 'hooks/transactions/useParseSignedTransactions'; @@ -65,16 +66,24 @@ export const useSignTransactionsCommonData = () => { function clearSignInfo(sessionId?: string) { const isExtensionProvider = provider instanceof ExtensionProvider; + const isCrossWindowProvider = provider instanceof CrossWindowProvider; dispatch(clearAllTransactionsToSign()); dispatch(clearTransactionsInfoForSessionId(sessionId)); - if (!isExtensionProvider) { + if (!isExtensionProvider && !isCrossWindowProvider) { return; } clearTransactionStatusMessage(); - ExtensionProvider.getInstance()?.cancelAction?.(); + + if (isExtensionProvider) { + ExtensionProvider.getInstance()?.cancelAction?.(); + } + + if (isCrossWindowProvider) { + CrossWindowProvider.getInstance()?.cancelAction?.(); + } } return { diff --git a/src/hooks/transactions/useSignTransactionsWithDevice.tsx b/src/hooks/transactions/useSignTransactionsWithDevice.tsx index 1fd625f99..33df90d54 100644 --- a/src/hooks/transactions/useSignTransactionsWithDevice.tsx +++ b/src/hooks/transactions/useSignTransactionsWithDevice.tsx @@ -37,7 +37,7 @@ export interface UseSignTransactionsWithDevicePropsType { export interface UseSignTransactionsWithDeviceReturnType { allTransactions: MultiSignTransactionType[]; - onSignTransaction: () => void; + onSignTransaction: () => Promise; onNext: () => void; onPrev: () => void; onAbort: () => void; @@ -96,7 +96,9 @@ export function useSignTransactionsWithDevice( const allowGuardian = !customTransactionInformation?.skipGuardian; - function handleTransactionsSignSuccess(newSignedTransactions: Transaction[]) { + async function handleTransactionsSignSuccess( + newSignedTransactions: Transaction[] + ) { const shouldMoveTransactionsToSignedState = getShouldMoveTransactionsToSignedState(newSignedTransactions); @@ -104,19 +106,19 @@ export function useSignTransactionsWithDevice( return; } - const { needs2FaSigning, sendTransactionsToGuardian } = - checkNeedsGuardianSigning({ - transactions: newSignedTransactions, - sessionId, - callbackRoute, - hasGuardianScreen, - isGuarded: isGuarded && allowGuardian, - walletAddress - }); + const { needs2FaSigning, guardTransactions } = checkNeedsGuardianSigning({ + transactions: newSignedTransactions, + sessionId, + callbackRoute, + hasGuardianScreen, + isGuarded: isGuarded && allowGuardian, + walletAddress + }); + let finalizedTransactions = newSignedTransactions; if (needs2FaSigning) { setIsSignDisabled(true); // prevent user from pressing sign button again while page is redirecting - return sendTransactionsToGuardian(); + finalizedTransactions = await guardTransactions(); } if (sessionId) { @@ -124,7 +126,7 @@ export function useSignTransactionsWithDevice( moveTransactionsToSignedState({ sessionId: sessionId, status: TransactionBatchStatusesEnum.signed, - transactions: newSignedTransactions.map((tx) => + transactions: finalizedTransactions.map((tx) => parseTransactionAfterSigning(tx) ) }) @@ -145,7 +147,9 @@ export function useSignTransactionsWithDevice( clearTransactionsToSignWithWarning(sessionId); } - async function handleSignTransaction(transaction: Nullable) { + async function handleSignTransaction( + transaction: Nullable + ): Promise> { const connectedProvider = providerType !== LoginMethodsEnum.ledger ? provider diff --git a/src/providers/accountProvider.ts b/src/providers/accountProvider.ts index 29c4c8112..4543e4184 100644 --- a/src/providers/accountProvider.ts +++ b/src/providers/accountProvider.ts @@ -1,6 +1,7 @@ import { ExtensionProvider } from '@multiversx/sdk-extension-provider'; import { HWProvider } from '@multiversx/sdk-hw-provider'; import { OperaProvider } from '@multiversx/sdk-opera-provider'; +import { CrossWindowProvider } from '@multiversx/sdk-web-wallet-cross-window-provider'; import { WalletProvider } from '@multiversx/sdk-web-wallet-provider'; import { IDappProvider } from 'types'; import { WalletConnectV2Provider } from 'utils/walletconnect/__sdkWalletconnectProvider'; @@ -10,6 +11,7 @@ export type ProvidersType = | IDappProvider | ExtensionProvider | WalletProvider + | CrossWindowProvider | HWProvider | OperaProvider | WalletConnectV2Provider; diff --git a/src/providers/experimentalWebViewProvider/ExperimentalWebviewProvider.ts b/src/providers/experimentalWebViewProvider/ExperimentalWebviewProvider.ts index f21c96149..df48273c5 100644 --- a/src/providers/experimentalWebViewProvider/ExperimentalWebviewProvider.ts +++ b/src/providers/experimentalWebViewProvider/ExperimentalWebviewProvider.ts @@ -3,24 +3,20 @@ import { responseTypeMap } from '@multiversx/sdk-web-wallet-cross-window-provide import { CrossWindowProviderRequestEnums, CrossWindowProviderResponseEnums, - ReplyWithPostMessageType, - RequestPayloadType, - ResponseTypeMap, + PostMessageParamsType, + PostMessageReturnType, + ReplyWithPostMessagePayloadType, SignMessageStatusEnum } from '@multiversx/sdk-web-wallet-cross-window-provider/out/types'; +import { logoutAction } from 'reduxStore/commonActions'; +import { store } from 'reduxStore/store'; import { loginWithNativeAuthToken } from 'services/nativeAuth/helpers/loginWithNativeAuthToken'; import { IDappProvider } from 'types/dappProvider.types'; +import { logout } from 'utils/logout'; import { setExternalProviderAsAccountProvider } from '../accountProvider'; import { getTargetOrigin } from './helpers/getTargetOrigin'; - -type SendPostMessageType = { - type: T; - payload?: RequestPayloadType[keyof RequestPayloadType]; -}; - -const notInitializedError = (caller: string) => () => { - throw new Error(`Unable to perform ${caller}, Provider not initialized`); -}; +import { notInitializedError } from './helpers/notInitializedError'; +import { webviewProviderEventHandler } from './helpers/webviewProviderEventHandler'; /** * This is an experimental provider that uses `postMessage` to communicate with the parent. @@ -37,7 +33,41 @@ export class ExperimentalWebviewProvider implements IDappProvider { return ExperimentalWebviewProvider.instance; } + constructor() { + this.resetState(); + } + + private resetState = () => { + const safeWindow = typeof window !== 'undefined' ? window : ({} as any); + + safeWindow.addEventListener( + 'message', + webviewProviderEventHandler( + CrossWindowProviderResponseEnums.resetStateResponse, + (data) => { + if ( + data.type === CrossWindowProviderResponseEnums.resetStateResponse + ) { + store.dispatch(logoutAction()); + + setTimeout(() => { + this.sendPostMessage({ + type: CrossWindowProviderRequestEnums.finalizeResetStateRequest, + payload: undefined + }); + }, 500); + } + } + ) + ); + }; + init = async () => { + this.sendPostMessage({ + type: CrossWindowProviderRequestEnums.finalizeHandshakeRequest, + payload: undefined + }); + return true; }; @@ -47,15 +77,19 @@ export class ExperimentalWebviewProvider implements IDappProvider { logout = async () => { const response = await this.sendPostMessage({ - type: CrossWindowProviderRequestEnums.logoutRequest + type: CrossWindowProviderRequestEnums.logoutRequest, + payload: undefined }); + await logout(); + return Boolean(response.payload.data); }; relogin = async () => { const response = await this.sendPostMessage({ - type: CrossWindowProviderRequestEnums.loginRequest + type: CrossWindowProviderRequestEnums.loginRequest, + payload: undefined }); const { data, error } = response.payload; @@ -84,15 +118,14 @@ export class ExperimentalWebviewProvider implements IDappProvider { payload: transactionsToSign.map((tx) => tx.toPlainObject()) }); - const { data, error } = response.payload; + const { data: signedTransactions, error } = response.payload; - if (error || !data) { + if (error || !signedTransactions) { console.error('Unable to sign transactions'); return null; } - const transactions = data; - return transactions.map((tx) => Transaction.fromPlainObject(tx)); + return signedTransactions.map((tx) => Transaction.fromPlainObject(tx)); }; signTransaction = async (transaction: Transaction) => { @@ -103,7 +136,7 @@ export class ExperimentalWebviewProvider implements IDappProvider { async signMessage(message: SignableMessage): Promise { const response = await this.sendPostMessage({ type: CrossWindowProviderRequestEnums.signMessageRequest, - payload: message + payload: { message: message.message.toString() } }); const { data, error } = response.payload; @@ -123,51 +156,29 @@ export class ExperimentalWebviewProvider implements IDappProvider { return message; } - async waitingForResponse( + isInitialized = () => true; + + isConnected = async () => true; + + getAddress = notInitializedError('getAddress'); + + private async waitingForResponse( action: T ): Promise<{ type: T; - payload: ReplyWithPostMessageType['payload']; + payload: ReplyWithPostMessagePayloadType; }> { return await new Promise((resolve) => { window.addEventListener( 'message', - async function eventHandler( - event: MessageEvent<{ - type: T; - payload: ReplyWithPostMessageType['payload']; - }> - ) { - const { type, payload } = event.data; - - if (event.origin != getTargetOrigin()) { - console.error('origin not accepted', { - eventOrigin: event.origin - }); - return; - } - - const isCurrentAction = - action === type || - type === CrossWindowProviderResponseEnums.cancelResponse; - - if (!isCurrentAction) { - return; - } - - window.removeEventListener('message', eventHandler); - resolve({ type, payload }); - } + webviewProviderEventHandler(action, resolve) ); }); } - sendPostMessage = async ( - message: SendPostMessageType - ): Promise<{ - type: ResponseTypeMap[T] | CrossWindowProviderResponseEnums.cancelResponse; - payload: ReplyWithPostMessageType['payload']; - }> => { + private sendPostMessage = async ( + message: PostMessageParamsType + ): Promise> => { const safeWindow = typeof window !== 'undefined' ? window : ({} as any); if (safeWindow.ReactNativeWebView) { @@ -186,8 +197,4 @@ export class ExperimentalWebviewProvider implements IDappProvider { return await this.waitingForResponse(responseTypeMap[message.type]); }; - - isInitialized = () => true; - isConnected = async () => true; - getAddress = notInitializedError('getAddress'); } diff --git a/src/providers/experimentalWebViewProvider/helpers/notInitializedError.ts b/src/providers/experimentalWebViewProvider/helpers/notInitializedError.ts new file mode 100644 index 000000000..ae1a841b8 --- /dev/null +++ b/src/providers/experimentalWebViewProvider/helpers/notInitializedError.ts @@ -0,0 +1,3 @@ +export const notInitializedError = (caller: string) => () => { + throw new Error(`Unable to perform ${caller}, Provider not initialized`); +}; diff --git a/src/providers/experimentalWebViewProvider/helpers/webviewProviderEventHandler.ts b/src/providers/experimentalWebViewProvider/helpers/webviewProviderEventHandler.ts new file mode 100644 index 000000000..648f19b15 --- /dev/null +++ b/src/providers/experimentalWebViewProvider/helpers/webviewProviderEventHandler.ts @@ -0,0 +1,43 @@ +import { + CrossWindowProviderResponseEnums, + ReplyWithPostMessagePayloadType +} from '@multiversx/sdk-web-wallet-cross-window-provider/out/types'; +import { getTargetOrigin } from './getTargetOrigin'; + +export const webviewProviderEventHandler = + ( + action: T, + resolve: (value: { + type: T; + payload: ReplyWithPostMessagePayloadType; + }) => void + ) => + ( + event: MessageEvent<{ + type: T; + payload: ReplyWithPostMessagePayloadType; + }> + ) => { + const { type, payload } = event.data; + + if (event.origin != getTargetOrigin()) { + console.error('origin not accepted', { + eventOrigin: event.origin + }); + return; + } + + const isCurrentAction = + action === type || + type === CrossWindowProviderResponseEnums.cancelResponse; + + if (!isCurrentAction) { + return; + } + + window.removeEventListener( + 'message', + webviewProviderEventHandler(action, resolve) + ); + resolve({ type, payload }); + }; diff --git a/src/providers/utils.ts b/src/providers/utils.ts index 4df334da6..632266408 100644 --- a/src/providers/utils.ts +++ b/src/providers/utils.ts @@ -3,6 +3,7 @@ import { ExtensionProvider } from '@multiversx/sdk-extension-provider'; import { HWProvider } from '@multiversx/sdk-hw-provider'; import { IHWWalletApp } from '@multiversx/sdk-hw-provider/out/interface'; import { OperaProvider } from '@multiversx/sdk-opera-provider'; +import { CrossWindowProvider } from '@multiversx/sdk-web-wallet-cross-window-provider'; import { WalletProvider } from '@multiversx/sdk-web-wallet-provider'; import { LEDGER_CONTRACT_DATA_ENABLED_VALUE } from 'constants/index'; import { IDappProvider } from 'types'; @@ -28,6 +29,8 @@ export const getProviderType = ( return LoginMethodsEnum.extension; case OperaProvider: return LoginMethodsEnum.opera; + case CrossWindowProvider: + return LoginMethodsEnum.crossWindow; case EmptyProvider: return LoginMethodsEnum.none; default: diff --git a/src/reduxStore/selectors/accountInfoSelectors.ts b/src/reduxStore/selectors/accountInfoSelectors.ts index e0b6c5aef..45abda9c4 100644 --- a/src/reduxStore/selectors/accountInfoSelectors.ts +++ b/src/reduxStore/selectors/accountInfoSelectors.ts @@ -23,7 +23,8 @@ export const accountInfoSelector = createDeepEqualSelector( (state, account) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { accounts, ...info } = state; - return { ...info, account }; + + return { ...info, address: account.address, account }; } ); diff --git a/src/reduxStore/selectors/loginInfoSelectors.ts b/src/reduxStore/selectors/loginInfoSelectors.ts index 9bda6aeec..4e46a1498 100644 --- a/src/reduxStore/selectors/loginInfoSelectors.ts +++ b/src/reduxStore/selectors/loginInfoSelectors.ts @@ -44,3 +44,8 @@ export const logoutRouteSelector = createDeepEqualSelector( loginInfoSelector, (state) => state.logoutRoute ); + +export const isWalletConnectV2InitializedSelector = createDeepEqualSelector( + loginInfoSelector, + (state) => state.isWalletConnectV2Initialized +); diff --git a/src/reduxStore/slices/accountInfoSlice.ts b/src/reduxStore/slices/accountInfoSlice.ts index 6b32eb3ae..580bea53a 100644 --- a/src/reduxStore/slices/accountInfoSlice.ts +++ b/src/reduxStore/slices/accountInfoSlice.ts @@ -76,7 +76,7 @@ export const accountInfoSlice = createSlice({ ) => { const address = action.payload; state.address = address; - state.publicKey = new Address(address).hex(); + state.publicKey = address ? new Address(address).hex() : ''; }, setAccount: ( state: AccountInfoSliceType, diff --git a/src/reduxStore/slices/loginInfoSlice.ts b/src/reduxStore/slices/loginInfoSlice.ts index 469b6e099..2a2f09439 100644 --- a/src/reduxStore/slices/loginInfoSlice.ts +++ b/src/reduxStore/slices/loginInfoSlice.ts @@ -32,8 +32,10 @@ export interface LoginInfoStateType { walletLogin: LoginInfoType | null; extensionLogin: LoginInfoType | null; operaLogin: LoginInfoType | null; + crossWindowLogin: LoginInfoType | null; isLoginSessionInvalid: boolean; logoutRoute?: string; + isWalletConnectV2Initialized?: boolean; } const initialState: LoginInfoStateType = { @@ -44,6 +46,7 @@ const initialState: LoginInfoStateType = { walletLogin: null, extensionLogin: null, operaLogin: null, + crossWindowLogin: null, isLoginSessionInvalid: false }; @@ -97,6 +100,12 @@ export const loginInfoSlice = createSlice({ action: PayloadAction ) => { state.logoutRoute = action.payload; + }, + setIsWalletConnectV2Initialized: ( + state: LoginInfoStateType, + action: PayloadAction + ) => { + state.isWalletConnectV2Initialized = action.payload; } }, extraReducers: (builder) => { @@ -125,7 +134,8 @@ export const { setTokenLoginSignature, setWalletLogin, invalidateLoginSession, - setLogoutRoute + setLogoutRoute, + setIsWalletConnectV2Initialized } = loginInfoSlice.actions; export default loginInfoSlice.reducer; diff --git a/src/setupTests.js b/src/setupTests.js index e3f010902..2228ee0ec 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -1,7 +1,7 @@ /************** * MSW config code ***************/ - +import 'isomorphic-fetch'; import { server } from './__mocks__/server'; // Establish API mocking before all tests. diff --git a/src/types/enums.types.ts b/src/types/enums.types.ts index c374fc9f3..e42528d0f 100644 --- a/src/types/enums.types.ts +++ b/src/types/enums.types.ts @@ -36,6 +36,7 @@ export enum LoginMethodsEnum { walletconnect = 'walletconnect', walletconnectv2 = 'walletconnectv2', wallet = 'wallet', + crossWindow = 'crossWindow', extension = 'extension', opera = 'opera', extra = 'extra', @@ -94,6 +95,7 @@ export enum WebViewProviderRequestBaseEnums { signTransactionsWithGuardianResponse = 'SIGN_TRANSACTIONS_WITH_GUARDIAN_RESPONSE', reloginRequest = 'RELOGIN_REQUEST' } + export const WebViewProviderRequestEnums = { ...CrossWindowProviderRequestEnums, ...WebViewProviderRequestBaseEnums diff --git a/src/types/serverTransactions.types.ts b/src/types/serverTransactions.types.ts index d52be033c..92426ef64 100644 --- a/src/types/serverTransactions.types.ts +++ b/src/types/serverTransactions.types.ts @@ -147,6 +147,7 @@ export enum HiddenTransactionOperationType { } export interface OperationType { + id?: string; action: TransactionOperationActionTypeEnum; type: VisibleTransactionOperationType | HiddenTransactionOperationType; esdtType?: NftEnumType | EsdtEnumType; @@ -154,6 +155,7 @@ export interface OperationType { name?: string; identifier?: string; sender: string; + ticker?: string; receiver: string; value: string; decimals?: number; @@ -187,6 +189,7 @@ export interface EventType { topics: string[]; order: number; data?: string; + additionalData?: string[]; } export interface ResultLogType { @@ -212,6 +215,8 @@ export interface ResultType { senderAssets?: AssetType; receiverAssets?: AssetType; miniBlockHash?: string; + function?: string; + timestamp?: number; } export interface ReceiptType { @@ -258,6 +263,7 @@ export interface ServerTransactionType { isNew?: boolean; // UI flag tokenValue?: string; tokenIdentifier?: string; + function?: string; } export enum TransferTypeEnum { diff --git a/src/types/transactions.types.ts b/src/types/transactions.types.ts index 0b1788580..0915229de 100644 --- a/src/types/transactions.types.ts +++ b/src/types/transactions.types.ts @@ -191,7 +191,7 @@ export type DeviceSignedTransactions = Record; export interface GuardianScreenType extends WithClassnameType { address: string; - onSignTransaction: () => void; + onSignTransaction: () => Promise; onPrev: () => void; title?: ReactNode; signStepInnerClasses?: SignStepInnerClassesType; diff --git a/src/utils/getHasNativeAuth/getHasNativeAuth.ts b/src/utils/getHasNativeAuth/getHasNativeAuth.ts new file mode 100644 index 000000000..6090b8ce5 --- /dev/null +++ b/src/utils/getHasNativeAuth/getHasNativeAuth.ts @@ -0,0 +1,25 @@ +import isObject from 'lodash/isObject'; +import { OnProviderLoginType } from '../../types/login.types'; + +const safeWindow = typeof window !== 'undefined' ? window : ({} as Window); +const isDevelopment = safeWindow?.location?.origin?.includes('localhost'); + +export const getHasNativeAuth = ( + nativeAuth: OnProviderLoginType['nativeAuth'] +) => { + const hasNativeAuth = isObject(nativeAuth); + + if (!hasNativeAuth) { + return Boolean(nativeAuth); + } + + const nativeAuthClone = { ...nativeAuth }; + const preventWalletConnectV2Signing = Boolean(isDevelopment); + + if (preventWalletConnectV2Signing) { + delete nativeAuthClone.origin; + } + + const isNativeAuthConfigured = Object.keys(nativeAuthClone).length > 0; + return isNativeAuthConfigured; +}; diff --git a/src/utils/getHasNativeAuth/index.ts b/src/utils/getHasNativeAuth/index.ts new file mode 100644 index 000000000..c7f6d2bd1 --- /dev/null +++ b/src/utils/getHasNativeAuth/index.ts @@ -0,0 +1 @@ +export * from './getHasNativeAuth'; diff --git a/src/utils/index.ts b/src/utils/index.ts index 239857288..a2f538ead 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -11,8 +11,8 @@ export * from './math'; export * from './network'; export * from './operations'; export * from './parseNavigationParams'; -export * from './progress'; export * from './platform'; +export * from './progress'; export * from './redirect'; export * from './removeSearchParamsFromUrl'; export * from './retryMultipleTimes'; @@ -24,3 +24,5 @@ export * from './transactions'; export * from './validation'; export * from './websocket'; export * from './window'; +export * from './getHasNativeAuth'; +export * from './waitForChainID'; diff --git a/src/utils/logout.ts b/src/utils/logout.ts index 477651186..63709c9bd 100644 --- a/src/utils/logout.ts +++ b/src/utils/logout.ts @@ -89,7 +89,6 @@ export async function logout( try { store.dispatch(logoutAction()); - if (isWalletProvider) { // Allow redux store cleanup before redirect to web wallet return setTimeout(() => { @@ -99,11 +98,12 @@ export async function logout( await provider.logout({ callbackUrl: url }); } catch (err) { - console.error('error logging out', err); } finally { - redirectToCallbackUrl({ - callbackUrl: url, - onRedirect - }); + if (!isWalletProvider) { + redirectToCallbackUrl({ + callbackUrl: url, + onRedirect + }); + } } } diff --git a/src/utils/operations/calculateFeeInFiat.ts b/src/utils/operations/calculateFeeInFiat.ts index 0b00d9c92..834bc338c 100644 --- a/src/utils/operations/calculateFeeInFiat.ts +++ b/src/utils/operations/calculateFeeInFiat.ts @@ -3,13 +3,17 @@ import { DIGITS, DECIMALS } from 'constants/index'; import { formatAmount } from './formatAmount'; import { getUsdValue } from './getUsdValue'; -export const calculateFeeInFiat = ({ - feeLimit, - egldPriceInUsd -}: { +export interface CalculateFeeInFiatType { feeLimit: string; egldPriceInUsd: number; -}) => { + hideEqualSign?: boolean; +} + +export const calculateFeeInFiat = ({ + feeLimit, + egldPriceInUsd, + hideEqualSign +}: CalculateFeeInFiatType) => { const amount = formatAmount({ input: feeLimit, decimals: DECIMALS, @@ -17,11 +21,15 @@ export const calculateFeeInFiat = ({ showLastNonZeroDecimal: true }); - const feeInFiat = `≈ ${getUsdValue({ + const feeAsUsdValue = getUsdValue({ amount, usd: egldPriceInUsd, decimals: DIGITS - })}`; + }); + + if (hideEqualSign) { + return feeAsUsdValue; + } - return feeInFiat; + return `≈ ${feeAsUsdValue}`; }; diff --git a/src/utils/platform/detectCurrentPlatform.ts b/src/utils/platform/detectCurrentPlatform.ts index 914856255..d7446764c 100644 --- a/src/utils/platform/detectCurrentPlatform.ts +++ b/src/utils/platform/detectCurrentPlatform.ts @@ -9,5 +9,6 @@ export const detectCurrentPlatform = () => { if (safeWindow.webkit) { return PlatformsEnum.ios; } + return PlatformsEnum.web; }; diff --git a/src/utils/transactions/getInterpretedTransaction/index.tsx b/src/utils/transactions/getInterpretedTransaction/index.tsx index bb7ba87ab..81608d6a8 100644 --- a/src/utils/transactions/getInterpretedTransaction/index.tsx +++ b/src/utils/transactions/getInterpretedTransaction/index.tsx @@ -1,2 +1,3 @@ export * from './getInterpretedTransaction'; export * from './constants'; +export * from './helpers'; diff --git a/src/utils/transactions/getOperationsDetails.ts b/src/utils/transactions/getOperationsDetails.ts new file mode 100644 index 000000000..262af319b --- /dev/null +++ b/src/utils/transactions/getOperationsDetails.ts @@ -0,0 +1,50 @@ +import { InterpretedTransactionType, OperationType } from 'types'; +import { getVisibleOperations } from 'utils/transactions'; + +export type OperationDetailsPropsType = { + transaction: InterpretedTransactionType; + filterBy?: { + action?: OperationType['action']; + sender?: OperationType['sender']; + receiver?: OperationType['receiver']; + }; +}; + +export const getOperationsDetails = ({ + transaction, + filterBy +}: OperationDetailsPropsType): OperationType[] => { + if (!transaction.operations) { + return []; + } + + const operations = getVisibleOperations(transaction); + + if (operations.length === 0) { + return []; + } + + if (!filterBy) { + return operations; + } + + const { action, receiver, sender } = filterBy; + + const filteredOperations = operations.filter((operation) => { + if (action && operation.action !== action) { + return false; + } + + if (sender && operation.sender !== sender) { + return false; + } + + if (receiver && operation.receiver !== receiver) { + return false; + } + + return true; + }); + + return filteredOperations; +}; diff --git a/src/utils/transactions/index.ts b/src/utils/transactions/index.ts index 640e69a5e..8685eddbe 100644 --- a/src/utils/transactions/index.ts +++ b/src/utils/transactions/index.ts @@ -12,3 +12,4 @@ export * from './parseTransactionAfterSigning'; export * from './removeTransactionParamsFromUrl'; export * from './transactionInfoHelpers'; export * from './transactionStateByStatus'; +export * from './getOperationsDetails'; diff --git a/src/utils/transactions/tests/getOperationsDetails.test.ts b/src/utils/transactions/tests/getOperationsDetails.test.ts new file mode 100644 index 000000000..19eef2d1d --- /dev/null +++ b/src/utils/transactions/tests/getOperationsDetails.test.ts @@ -0,0 +1,248 @@ +import { testAddress, testReceiver } from '__mocks__'; +import { + HiddenTransactionOperationType, + InterpretedTransactionType, + TransactionActionsEnum, + TransactionOperationActionTypeEnum, + VisibleTransactionOperationType +} from 'types'; +import { NftEnumType } from 'types/tokens.types'; +import { getOperationsDetails } from '../getOperationsDetails'; + +const TRANSACTION: InterpretedTransactionType = { + txHash: 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + gasLimit: 62800000, + gasPrice: 1000000000, + gasUsed: 41562941, + miniBlockHash: + 'e701717634203ff12f98ec5449974f9a27591d35a717aa1bc084f7deab9af5ff', + nonce: 34, + receiver: testReceiver, + receiverShard: 1, + round: 2419148, + sender: testAddress, + senderShard: 1, + signature: + 'c9b5d9156f22bfa370d66374f4508e5b987b5f94487df6e81083e608f89d37f31b1f3bfa3a6b41ff7e2d8b21c69737d31bef3f26c17dd3b4efe9afecea349205', + status: 'success', + value: '0', + fee: '582444410000000', + timestamp: 1708514888, + data: 'YnV5Q2FyZHNAMDFAMDFANTQwYTc3ODE4ODcxMDQyZDU1MDE5YzE3NTMwMjZkM2U3MTQzNDJhNDE1YjA2ZjYwMTFmMGU4MDgzNDI3ZjA1Nw==', + function: 'buyCards', + action: { + category: 'scCall', + name: TransactionActionsEnum.transfer + }, + results: [ + { + hash: '69fb01546ef00577bd2fa3aa07be315f1c52bd975a77bc343f23ab6eb7f5bbcb', + timestamp: 1708514888, + nonce: 35, + gasLimit: 0, + gasPrice: 1000000000, + value: '212370590000000', + sender: testAddress, + receiver: testReceiver, + data: 'QDZmNmJAMDAwMDAwMDEwMDAwMDAwZDQ3NDU0ZTJkMzAzNzYyNjYzNzY2MmQzMzM0MDAwMDAwMDAwMDAwMDBhOTAwMDAwMDI0MzE2NTY1NjQzMDYxMzkzNjJkMzczODMyMzUyZDM2MzgzMzMwMmQzOTYzMzMzNjJkNjUzOTM0MzY2MTM5Mzg2NjY2MzEzODM0MDAwMDAwMDc0YzY5NmQ2OTc0NjU2NDAwMDAwMDAwNjVkNWRlNDgwMDAwMDAwMTAwMDAwMDA1MDAwMDAwMGQ0NzQ1NGUyZDMwMzc2MjY2Mzc2NjJkMzMzNTAwMDAwMDAwMDAwMDAwN2QwMDAwMDAyNDMxNjU2NTY0MzA2MTM5MzYyZDM3MzgzMjM1MmQzNjM4MzMzMDJkMzk2MzMzMzYyZDY1MzkzNDM2NjEzOTM4NjY2NjMxMzgzNDAwMDAwMDA3NGM2OTZkNjk3NDY1NjQwMDAwMDAwMDY1ZDVkZTQ4MDAwMDAwMGQ0NzQ1NGUyZDMwMzc2MjY2Mzc2NjJkMzMzNjAwMDAwMDAwMDAwMDAwZDEwMDAwMDAyNDMxNjU2NTY0MzA2MTM5MzYyZDM3MzgzMjM1MmQzNjM4MzMzMDJkMzk2MzMzMzYyZDY1MzkzNDM2NjEzOTM4NjY2NjMxMzgzNDAwMDAwMDA3NGM2OTZkNjk3NDY1NjQwMDAwMDAwMDY1ZDVkZTQ4MDAwMDAwMGQ0NzQ1NGUyZDMwMzc2MjY2Mzc2NjJkMzMzNzAwMDAwMDAwMDAwMDAwMmQwMDAwMDAyNDMxNjU2NTY0MzA2MTM5MzYyZDM3MzgzMjM1MmQzNjM4MzMzMDJkMzk2MzMzMzYyZDY1MzkzNDM2NjEzOTM4NjY2NjMxMzgzNDAwMDAwMDA3NGM2OTZkNjk3NDY1NjQwMDAwMDAwMDY1ZDVkZTQ4MDAwMDAwMGQ0NzQ1NGUyZDMwMzc2MjY2Mzc2NjJkMzMzODAwMDAwMDAwMDAwMDAwMTMwMDAwMDAyNDMxNjU2NTY0MzA2MTM5MzYyZDM3MzgzMjM1MmQzNjM4MzMzMDJkMzk2MzMzMzYyZDY1MzkzNDM2NjEzOTM4NjY2NjMxMzgzNDAwMDAwMDA0NTI2MTcyNjUwMDAwMDAwMDY1ZDVkZTQ4MDAwMDAwMGQ0NzQ1NGUyZDMwMzc2MjY2Mzc2NjJkMzMzOTAwMDAwMDAwMDAwMDAwYWEwMDAwMDAyNDMxNjU2NTY0MzA2MTM5MzYyZDM3MzgzMjM1MmQzNjM4MzMzMDJkMzk2MzMzMzYyZDY1MzkzNDM2NjEzOTM4NjY2NjMxMzgzNDAwMDAwMDA0NTI2MTcyNjUwMDAwMDAwMDY1ZDVkZTQ4MDAwMDAwOGM2ODc0NzQ3MDczM2EyZjJmNjM2OTM0MzMzMTMwMzAyZDY0NmY2Mzc1NmQ2NTZlNzQyZDczNjU3Mjc2Njk2MzY1MmQ2NzY1NmU2NTdhNzk3MzJkNjQ2ZjYzNzU2ZDY1NmU3NDczMmQ2Mjc1NjM2YjY1NzQyZTczMzMyZTY1NzUyZDc3NjU3Mzc0MmQzMzJlNjE2ZDYxN2E2ZjZlNjE3NzczMmU2MzZmNmQyZjMxNjU2NTY0MzA2MTM5MzcyZDMwMzIzNDM5MmQzNjM0MzgzMDJkMzQzNjMzMzgyZDMwMzYzNDM1NjQzMzM2MzQ2MjM5MzkzMDJmNDM2MTcyNjQ0ZDY1NzQ2MTY0NjE3NDYxNDk2ZDYxNjc2NTJmMDAwMDAwOGI2ODc0NzQ3MDczM2EyZjJmNjM2OTM0MzMzMTMwMzAyZDY0NmY2Mzc1NmQ2NTZlNzQyZDczNjU3Mjc2Njk2MzY1MmQ2NzY1NmU2NTdhNzk3MzJkNjQ2ZjYzNzU2ZDY1NmU3NDczMmQ2Mjc1NjM2YjY1NzQyZTczMzMyZTY1NzUyZDc3NjU3Mzc0MmQzMzJlNjE2ZDYxN2E2ZjZlNjE3NzczMmU2MzZmNmQyZjMxNjU2NTY0MzA2MTM5MzcyZDMwMzIzNDM5MmQzNjM0MzgzMDJkMzQzNjMzMzgyZDMwMzYzNDM1NjQzMzM2MzQ2MjM5MzkzMDJmNDM2MTcyNjQ0ZDY1NzQ2MTY0NjE3NDYxNGE3MzZmNmUyZg==', + prevTxHash: + 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + originalTxHash: + 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + callType: '0', + miniBlockHash: + 'f02e34e2a2e90990141ddf8c3e9fc478f37368d90248931a7e7213f3000a1807', + function: 'transfer' + }, + { + hash: 'b7abcad8ab36164730affb48bdacf6c737ce5232494ea8b8ca96aaf1d9fa9fb9', + timestamp: 1708514888, + nonce: 1, + gasLimit: 0, + gasPrice: 1000000000, + value: '0', + sender: testAddress, + receiver: testReceiver, + data: 'TXVsdGlFU0RUTkZUVHJhbnNmZXJANTQwYTc3ODE4ODcxMDQyZDU1MDE5YzE3NTMwMjZkM2U3MTQzNDJhNDE1YjA2ZjYwMTFmMGU4MDgzNDI3ZjA1N0AwNUA0NzQ1NGUyZDMwMzc2MjY2Mzc2NkAzNUAwMUA0NzQ1NGUyZDMwMzc2MjY2Mzc2NkAzNkAwMUA0NzQ1NGUyZDMwMzc2MjY2Mzc2NkAzN0AwMUA0NzQ1NGUyZDMwMzc2MjY2Mzc2NkAzOEAwMUA0NzQ1NGUyZDMwMzc2MjY2Mzc2NkAzOUAwMQ==', + prevTxHash: + 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + originalTxHash: + 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + callType: '0', + miniBlockHash: + 'f02e34e2a2e90990141ddf8c3e9fc478f37368d90248931a7e7213f3000a1807', + function: 'MultiESDTNFTTransfer' + }, + { + hash: '62688728fd2f3bcb6ae6b96e63edf2789ab7dbbbfe46bb226d280c31899c538d', + timestamp: 1708514888, + nonce: 0, + gasLimit: 0, + gasPrice: 1000000000, + value: '0', + sender: testAddress, + receiver: testReceiver, + data: 'RVNEVE5GVFRyYW5zZmVyQDQ3NDU0ZTJkMzAzNzYyNjYzNzY2QDM0QDAxQDU0MGE3NzgxODg3MTA0MmQ1NTAxOWMxNzUzMDI2ZDNlNzE0MzQyYTQxNWIwNmY2MDExZjBlODA4MzQyN2YwNTc=', + prevTxHash: + 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + originalTxHash: + 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + callType: '0', + miniBlockHash: + 'f02e34e2a2e90990141ddf8c3e9fc478f37368d90248931a7e7213f3000a1807', + function: 'ESDTNFTTransfer' + } + ], + price: 57.31, + logs: { + id: 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + address: testAddress, + events: [] + }, + operations: [ + { + id: 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + action: TransactionOperationActionTypeEnum.create, + type: VisibleTransactionOperationType.nft, + esdtType: NftEnumType.NonFungibleESDT, + collection: 'GEN-07bf7f', + identifier: 'GEN-07bf7f-34', + ticker: 'GEN-07bf7f', + name: 'col 1 #52', + sender: testAddress, + receiver: testAddress, + value: '1' + }, + { + id: 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + action: TransactionOperationActionTypeEnum.transfer, + type: VisibleTransactionOperationType.nft, + esdtType: NftEnumType.NonFungibleESDT, + collection: 'GEN-07bf7f', + identifier: 'GEN-07bf7f-34', + ticker: 'GEN-07bf7f', + name: 'col 1 #52', + sender: testAddress, + receiver: testReceiver, + value: '1' + }, + { + id: 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + action: TransactionOperationActionTypeEnum.create, + type: VisibleTransactionOperationType.nft, + esdtType: NftEnumType.NonFungibleESDT, + collection: 'GEN-07bf7f', + identifier: 'GEN-07bf7f-35', + ticker: 'GEN-07bf7f', + name: 'col 1 #53', + sender: testAddress, + receiver: testAddress, + value: '1' + }, + { + id: 'f6e4f89f693ba8da573b448ea89a295aa4f2db8f3160019a7b3e25b852bdfcf0', + action: TransactionOperationActionTypeEnum.create, + type: VisibleTransactionOperationType.nft, + esdtType: NftEnumType.NonFungibleESDT, + collection: 'GEN-07bf7f', + identifier: 'GEN-07bf7f-36', + ticker: 'GEN-07bf7f', + name: 'col 1 #54', + sender: testAddress, + receiver: testAddress, + value: '1' + }, + { + id: '6b9cd262f250c0197c679582daca50a86a552df717dabc1733fc203cd62b204f', + action: TransactionOperationActionTypeEnum.writeLog, + type: HiddenTransactionOperationType.log, + sender: testAddress, + receiver: testReceiver, + senderAssets: { + description: 'test transaction', + name: 'test.elrond', + tags: ['dns', 'username'] + }, + value: '1', + data: '@ff2b' + } + ], + transactionDetails: { + direction: undefined, + method: '', + transactionTokens: [], + isContract: undefined + }, + links: { + senderLink: undefined, + receiverLink: undefined, + senderShardLink: undefined, + receiverShardLink: undefined, + transactionLink: undefined + } +}; + +describe('getOperationsDetails tests', () => { + test('returns operations without hidden operation type', () => { + expect(getOperationsDetails({ transaction: TRANSACTION }).length).toBe(4); + }); + + test('returns operations without hidden operation type and with create action type', () => { + expect( + getOperationsDetails({ + transaction: TRANSACTION, + filterBy: { + action: TransactionOperationActionTypeEnum.create + } + }).length + ).toBe(3); + }); + + test('returns operations without hidden operation type and with transfer action type', () => { + expect( + getOperationsDetails({ + transaction: TRANSACTION, + filterBy: { + action: TransactionOperationActionTypeEnum.transfer + } + }).length + ).toBe(1); + }); + + test('returns operations without hidden operation type and with transfer action type', () => { + expect( + getOperationsDetails({ + transaction: TRANSACTION, + filterBy: { + action: TransactionOperationActionTypeEnum.transfer, + receiver: testReceiver + } + }).length + ).toBe(1); + }); + + test('returns operations without hidden operation type and with create action type', () => { + expect( + getOperationsDetails({ + transaction: TRANSACTION, + filterBy: { + action: TransactionOperationActionTypeEnum.create, + receiver: testReceiver + } + }).length + ).toBe(0); + }); + + test('returns operations without hidden operation type and with create action type, sender and receiver', () => { + expect( + getOperationsDetails({ + transaction: TRANSACTION, + filterBy: { + action: TransactionOperationActionTypeEnum.create, + sender: testAddress, + receiver: testAddress + } + }).length + ).toBe(3); + }); +}); diff --git a/src/utils/waitForChainID.ts b/src/utils/waitForChainID.ts new file mode 100644 index 000000000..29d919883 --- /dev/null +++ b/src/utils/waitForChainID.ts @@ -0,0 +1,33 @@ +import { getEnvironmentForChainId } from 'apiCalls'; +import { chainIDSelector } from 'reduxStore/selectors'; +import { store } from 'reduxStore/store'; + +export const waitForChainID = ({ + maxRetries +}: { + maxRetries: number; +}): Promise => + new Promise((resolve, reject) => { + let retries = 0; + + // Function to periodically check the value of chainID + const checkChainID = () => { + const chainID = chainIDSelector(store.getState()); + const isValidEnvironment = getEnvironmentForChainId(chainID); + + if (Boolean(isValidEnvironment)) { + resolve(chainID); + return; + } + + if (retries < maxRetries) { + retries++; + setTimeout(checkChainID, 1000); + return; + } + + reject(null); + }; + + checkChainID(); + }); diff --git a/src/wrappers/DappProvider/DappProvider.tsx b/src/wrappers/DappProvider/DappProvider.tsx index 0ed55f4ac..27a0bc9b5 100644 --- a/src/wrappers/DappProvider/DappProvider.tsx +++ b/src/wrappers/DappProvider/DappProvider.tsx @@ -3,7 +3,7 @@ import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import { ProviderInitializer } from 'components/ProviderInitializer/ProviderInitializer'; import { setExternalProvider } from 'providers/accountProvider'; -import { ExperimentalWebviewProvider } from 'providers/experimentalWebViewProvider'; +import { ExperimentalWebviewProvider } from 'providers/experimentalWebViewProvider/ExperimentalWebviewProvider'; import { webviewProvider } from 'providers/webviewProvider'; import { DappCoreContext } from 'reduxStore/DappProviderContext'; import { persistor, store } from 'reduxStore/store'; @@ -22,7 +22,9 @@ export { DappConfigType }; const setWebviewProvider = () => { if (getWebviewPlatform() === PlatformsEnum.webWallet) { - setExternalProvider(ExperimentalWebviewProvider.getInstance()); + const providerInstance = ExperimentalWebviewProvider.getInstance(); + providerInstance.init?.(); + setExternalProvider(providerInstance); } else { setExternalProvider(webviewProvider); } diff --git a/yarn.lock b/yarn.lock index afc53d074..2f06b7f3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8,9 +8,9 @@ integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== "@adobe/css-tools@^4.0.1": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" - integrity sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ== + version "4.3.2" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.2.tgz#a6abc715fb6884851fca9dad37fc34739a04fd11" + integrity sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw== "@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": version "2.2.1" @@ -181,17 +181,6 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-define-polyfill-provider@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz#465805b7361f461e86c680f1de21eaf88c25901b" - integrity sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q== - dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - "@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" @@ -1387,12 +1376,12 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": +"@hapi/hoek@^9.0.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== -"@hapi/topo@^5.1.0": +"@hapi/topo@^5.0.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== @@ -1761,9 +1750,9 @@ integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.22" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" - integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== + version "0.3.21" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz#5dc1df7b3dc4a6209e503a924e1ca56097a2bb15" + integrity sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -1847,9 +1836,9 @@ events "^3.3.0" "@ledgerhq/hw-transport@^6.28.4", "@ledgerhq/hw-transport@^6.28.6": - version "6.30.2" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.2.tgz#de988b3ba8df4ca0ae97f7b7f99327bd61a535ee" - integrity sha512-iTB0cwQaISvUXwrnPOLAmPoAOMvW14XmtKsuQce0qYJZC/1/eUPNFu6sOD8X0qUBbMfWhre8X9AmMuWTPQH3lA== + version "6.30.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.30.1.tgz#fd3c825f41197aeaf705e3c066f82843eaf48cae" + integrity sha512-Xeeo4nt33g5Fsp3CdsPvcc2Uk7dwYeKRSlSFLWcYAAKprf/PmxgNekhke1eaNU/wLoeLOWhY2Cki8F8w9nLMdQ== dependencies: "@ledgerhq/devices" "^8.2.0" "@ledgerhq/errors" "^6.16.1" @@ -1861,6 +1850,13 @@ resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.12.0.tgz#ad903528bf3687a44da435d7b2479d724d374f5d" integrity sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA== +"@lifeomic/axios-fetch@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@lifeomic/axios-fetch/-/axios-fetch-3.0.1.tgz#a0f135470d7bb54d0ce82b0a2f3b19daa7bf77e2" + integrity sha512-bwEgYXtGrn/F+yYqoUIAWBRzyqQ7yB1VL84gCq0uAk36GRoyoWyOzbE35VWeXlmkkoby91FnAwh4UhgayMM/og== + dependencies: + "@types/node-fetch" "^2.5.10" + "@mdx-js/mdx@^1.6.22": version "1.6.22" resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" @@ -1945,6 +1941,20 @@ keccak "3.0.2" protobufjs "7.2.4" +"@multiversx/sdk-core@13.0.0-beta.4": + version "13.0.0-beta.4" + resolved "https://registry.yarnpkg.com/@multiversx/sdk-core/-/sdk-core-13.0.0-beta.4.tgz#a6a8121cd53f7ac05882647cb756f9276f106c8c" + integrity sha512-JCpuBgWqB1aMxYz9se+YYNXq2Z7IGLbZjV63Bq6Ba68AbSpgAZtSe4GBQcRIzNeHTWUCbKZb8NCRtyAXcveqFA== + dependencies: + "@multiversx/sdk-transaction-decoder" "1.0.2" + bech32 "1.1.4" + blake2b "2.1.3" + buffer "6.0.3" + json-bigint "1.0.0" + json-duplicate-key-handle "1.0.0" + keccak "3.0.2" + protobufjs "7.2.4" + "@multiversx/sdk-extension-provider@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@multiversx/sdk-extension-provider/-/sdk-extension-provider-3.0.0.tgz#e0e178ee5555f9440457547759621f5c3152c5fa" @@ -1983,13 +1993,13 @@ dependencies: bech32 "^2.0.0" -"@multiversx/sdk-wallet-connect-provider@4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@multiversx/sdk-wallet-connect-provider/-/sdk-wallet-connect-provider-4.1.0.tgz#802f7ffbd69a774014ce6a26dcd52b1ab0492633" - integrity sha512-gvgZ2q+boliQHRiYDkJ+OOYCbsbrhoavqx01wwECs41EPn7TCIFKVcA5ljtK9OSiX8Htis7ckpTaZFWSr5wkCg== +"@multiversx/sdk-wallet-connect-provider@4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@multiversx/sdk-wallet-connect-provider/-/sdk-wallet-connect-provider-4.1.1.tgz#4c5fc34eeac818fd545d0bf94f1eff79cb7b07db" + integrity sha512-Fh+MD0RUXbn+Cnxep7TV4tH5XI5T5pSerrU5usbNRBe/DFGlWYcaGLbxVaat4VnMDHu9WnEjT5r2qO530YQi4g== dependencies: - "@walletconnect/sign-client" "2.11.0" - "@walletconnect/utils" "2.11.0" + "@walletconnect/sign-client" "2.11.2" + "@walletconnect/utils" "2.11.2" bech32 "1.1.4" "@multiversx/sdk-wallet@4.2.0": @@ -2010,10 +2020,10 @@ tweetnacl "1.0.3" uuid "8.3.2" -"@multiversx/sdk-web-wallet-cross-window-provider@0.0.14": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@multiversx/sdk-web-wallet-cross-window-provider/-/sdk-web-wallet-cross-window-provider-0.0.14.tgz#95c62d57f4a695b9e22496de56a679ddf53799b5" - integrity sha512-bIma2d5Bd2THO9r4icbVkJd+o6FfmUsmRkERQ7vVM9bEjBZyxFdoWn4p0gh6Nu8EdGVSj7BE0BagYpag+zJdow== +"@multiversx/sdk-web-wallet-cross-window-provider@0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@multiversx/sdk-web-wallet-cross-window-provider/-/sdk-web-wallet-cross-window-provider-0.0.26.tgz#957ec9153154243e8f3e936ebc8e4c43e4a8f84b" + integrity sha512-ihmarUFc3ujjnAEqbThDM+Obft9u8tUEVuoua2LXiUqkkV7eTceY2nnSbEWrJaUQklfR6e2ZjGYIO0pm7EqV9Q== dependencies: "@multiversx/sdk-core" "12.18.0" "@types/jest" "^29.5.11" @@ -2195,50 +2205,50 @@ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== -"@parcel/watcher-android-arm64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.0.tgz#9c93763794153e4f76920994a423b6ea3257059d" - integrity sha512-+fPtO/GsbYX1LJnCYCaDVT3EOBjvSFdQN9Mrzh9zWAOOfvidPWyScTrHIZHHfJBvlHzNA0Gy0U3NXFA/M7PHUA== +"@parcel/watcher-android-arm64@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.3.0.tgz#d82e74bb564ebd4d8a88791d273a3d2bd61e27ab" + integrity sha512-f4o9eA3dgk0XRT3XhB0UWpWpLnKgrh1IwNJKJ7UJek7eTYccQ8LR7XUWFKqw6aEq5KUNlCcGvSzKqSX/vtWVVA== -"@parcel/watcher-darwin-arm64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.0.tgz#2c79c2abde16aa24cac67e555b60802fd13fe210" - integrity sha512-T/At5pansFuQ8VJLRx0C6C87cgfqIYhW2N/kBfLCUvDhCah0EnLLwaD/6MW3ux+rpgkpQAnMELOCTKlbwncwiA== +"@parcel/watcher-darwin-arm64@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.3.0.tgz#c9cd03f8f233d512fcfc873d5b4e23f1569a82ad" + integrity sha512-mKY+oijI4ahBMc/GygVGvEdOq0L4DxhYgwQqYAz/7yPzuGi79oXrZG52WdpGA1wLBPrYb0T8uBaGFo7I6rvSKw== -"@parcel/watcher-darwin-x64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.0.tgz#23d82f198c5d033f047467c68d7c335f3df49b46" - integrity sha512-vZMv9jl+szz5YLsSqEGCMSllBl1gU1snfbRL5ysJU03MEa6gkVy9OMcvXV1j4g0++jHEcvzhs3Z3LpeEbVmY6Q== +"@parcel/watcher-darwin-x64@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.3.0.tgz#83c902994a2a49b9e1ab5050dba24876fdc2c219" + integrity sha512-20oBj8LcEOnLE3mgpy6zuOq8AplPu9NcSSSfyVKgfOhNAc4eF4ob3ldj0xWjGGbOF7Dcy1Tvm6ytvgdjlfUeow== -"@parcel/watcher-freebsd-x64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.0.tgz#7310cc86abc27dacd57624bcdba1f0ba092e76df" - integrity sha512-dHTRMIplPDT1M0+BkXjtMN+qLtqq24sLDUhmU+UxxLP2TEY2k8GIoqIJiVrGWGomdWsy5IO27aDV1vWyQ6gfHA== +"@parcel/watcher-freebsd-x64@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.3.0.tgz#7a0f4593a887e2752b706aff2dae509aef430cf6" + integrity sha512-7LftKlaHunueAEiojhCn+Ef2CTXWsLgTl4hq0pkhkTBFI3ssj2bJXmH2L67mKpiAD5dz66JYk4zS66qzdnIOgw== -"@parcel/watcher-linux-arm-glibc@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.0.tgz#c31b76e695027eeb1078d3d6f1d641d0b900c335" - integrity sha512-9NQXD+qk46RwATNC3/UB7HWurscY18CnAPMTFcI9Y8CTbtm63/eex1SNt+BHFinEQuLBjaZwR2Lp+n7pmEJPpQ== +"@parcel/watcher-linux-arm-glibc@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.3.0.tgz#3fc90c3ebe67de3648ed2f138068722f9b1d47da" + integrity sha512-1apPw5cD2xBv1XIHPUlq0cO6iAaEUQ3BcY0ysSyD9Kuyw4MoWm1DV+W9mneWI+1g6OeP6dhikiFE6BlU+AToTQ== -"@parcel/watcher-linux-arm64-glibc@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.0.tgz#56e09b86e9d8a4096f606be118b588da6e965080" - integrity sha512-QuJTAQdsd7PFW9jNGaV9Pw+ZMWV9wKThEzzlY3Lhnnwy7iW23qtQFPql8iEaSFMCVI5StNNmONUopk+MFKpiKg== +"@parcel/watcher-linux-arm64-glibc@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.3.0.tgz#f7bbbf2497d85fd11e4c9e9c26ace8f10ea9bcbc" + integrity sha512-mQ0gBSQEiq1k/MMkgcSB0Ic47UORZBmWoAWlMrTW6nbAGoLZP+h7AtUM7H3oDu34TBFFvjy4JCGP43JlylkTQA== -"@parcel/watcher-linux-arm64-musl@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.0.tgz#27ffd5ca5f510ecd638f9ad22e2e813049db54e7" - integrity sha512-oyN+uA9xcTDo/45bwsd6TFHa7Lc7hKujyMlvwrCLvSckvWogndCEoVYFNfZ6JJ2KNL/6fFiGPcbjp8jJmEh5Ng== +"@parcel/watcher-linux-arm64-musl@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.3.0.tgz#de131a9fcbe1fa0854e9cbf4c55bed3b35bcff43" + integrity sha512-LXZAExpepJew0Gp8ZkJ+xDZaTQjLHv48h0p0Vw2VMFQ8A+RKrAvpFuPVCVwKJCr5SE+zvaG+Etg56qXvTDIedw== -"@parcel/watcher-linux-x64-glibc@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.0.tgz#44cbbb1e5884a1ca900655f47a0775218318f934" - integrity sha512-KphV8awJmxU3q52JQvJot0QMu07CIyEjV+2Tb2ZtbucEgqyRcxOBDMsqp1JNq5nuDXtcCC0uHQICeiEz38dPBQ== +"@parcel/watcher-linux-x64-glibc@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.3.0.tgz#193dd1c798003cdb5a1e59470ff26300f418a943" + integrity sha512-P7Wo91lKSeSgMTtG7CnBS6WrA5otr1K7shhSjKHNePVmfBHDoAOHYRXgUmhiNfbcGk0uMCHVcdbfxtuiZCHVow== -"@parcel/watcher-linux-x64-musl@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.0.tgz#4c33993618c8d5113722852806239cb80360494b" - integrity sha512-7jzcOonpXNWcSijPpKD5IbC6xC7yTibjJw9jviVzZostYLGxbz8LDJLUnLzLzhASPlPGgpeKLtFUMjAAzM+gSA== +"@parcel/watcher-linux-x64-musl@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.3.0.tgz#6dbdb86d96e955ab0fe4a4b60734ec0025a689dd" + integrity sha512-+kiRE1JIq8QdxzwoYY+wzBs9YbJ34guBweTK8nlzLKimn5EQ2b2FSC+tAOpq302BuIMjyuUGvBiUhEcLIGMQ5g== "@parcel/watcher-wasm@2.3.0": version "2.3.0" @@ -2249,43 +2259,43 @@ micromatch "^4.0.5" napi-wasm "^1.1.0" -"@parcel/watcher-win32-arm64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.0.tgz#2a172fd2fda95fe5389298ca3e70b5a96316162a" - integrity sha512-NOej2lqlq8bQNYhUMnOD0nwvNql8ToQF+1Zhi9ULZoG+XTtJ9hNnCFfyICxoZLXor4bBPTOnzs/aVVoefYnjIg== +"@parcel/watcher-win32-arm64@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.3.0.tgz#59da26a431da946e6c74fa6b0f30b120ea6650b6" + integrity sha512-35gXCnaz1AqIXpG42evcoP2+sNL62gZTMZne3IackM+6QlfMcJLy3DrjuL6Iks7Czpd3j4xRBzez3ADCj1l7Aw== -"@parcel/watcher-win32-ia32@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.0.tgz#279225b2ebe1fadd3c5137c9b2365ad422656904" - integrity sha512-IO/nM+K2YD/iwjWAfHFMBPz4Zqn6qBDqZxY4j2n9s+4+OuTSRM/y/irksnuqcspom5DjkSeF9d0YbO+qpys+JA== +"@parcel/watcher-win32-ia32@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.3.0.tgz#3ee6a18b08929cd3b788e8cc9547fd9a540c013a" + integrity sha512-FJS/IBQHhRpZ6PiCjFt1UAcPr0YmCLHRbTc00IBTrelEjlmmgIVLeOx4MSXzx2HFEy5Jo5YdhGpxCuqCyDJ5ow== -"@parcel/watcher-win32-x64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.0.tgz#93e0bd0ad1bda2c9a688764b9b30b71dc5b72a71" - integrity sha512-pAUyUVjfFjWaf/pShmJpJmNxZhbMvJASUpdes9jL6bTEJ+gDxPRSpXTIemNyNsb9AtbiGXs9XduP1reThmd+dA== +"@parcel/watcher-win32-x64@2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.3.0.tgz#14e7246289861acc589fd608de39fe5d8b4bb0a7" + integrity sha512-dLx+0XRdMnVI62kU3wbXvbIRhLck4aE28bIGKbRGS7BJNt54IIj9+c/Dkqb+7DJEbHUZAX1bwaoM8PqVlHJmCA== "@parcel/watcher@^2.3.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.0.tgz#2d3c4ef8832a5cdfdbb76b914f022489933e664f" - integrity sha512-XJLGVL0DEclX5pcWa2N9SX1jCGTDd8l972biNooLFtjneuGqodupPQh6XseXIBBeVIMaaJ7bTcs3qGvXwsp4vg== + version "2.3.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.3.0.tgz#803517abbc3981a1a1221791d9f59dc0590d50f9" + integrity sha512-pW7QaFiL11O0BphO+bq3MgqeX/INAk9jgBldVDYjlQPO4VddoZnF22TcF9onMhnLVHuNqBJeRf+Fj7eezi/+rQ== dependencies: detect-libc "^1.0.3" is-glob "^4.0.3" micromatch "^4.0.5" node-addon-api "^7.0.0" optionalDependencies: - "@parcel/watcher-android-arm64" "2.4.0" - "@parcel/watcher-darwin-arm64" "2.4.0" - "@parcel/watcher-darwin-x64" "2.4.0" - "@parcel/watcher-freebsd-x64" "2.4.0" - "@parcel/watcher-linux-arm-glibc" "2.4.0" - "@parcel/watcher-linux-arm64-glibc" "2.4.0" - "@parcel/watcher-linux-arm64-musl" "2.4.0" - "@parcel/watcher-linux-x64-glibc" "2.4.0" - "@parcel/watcher-linux-x64-musl" "2.4.0" - "@parcel/watcher-win32-arm64" "2.4.0" - "@parcel/watcher-win32-ia32" "2.4.0" - "@parcel/watcher-win32-x64" "2.4.0" + "@parcel/watcher-android-arm64" "2.3.0" + "@parcel/watcher-darwin-arm64" "2.3.0" + "@parcel/watcher-darwin-x64" "2.3.0" + "@parcel/watcher-freebsd-x64" "2.3.0" + "@parcel/watcher-linux-arm-glibc" "2.3.0" + "@parcel/watcher-linux-arm64-glibc" "2.3.0" + "@parcel/watcher-linux-arm64-musl" "2.3.0" + "@parcel/watcher-linux-x64-glibc" "2.3.0" + "@parcel/watcher-linux-x64-musl" "2.3.0" + "@parcel/watcher-win32-arm64" "2.3.0" + "@parcel/watcher-win32-ia32" "2.3.0" + "@parcel/watcher-win32-x64" "2.3.0" "@pmmmwh/react-refresh-webpack-plugin@^0.5.3": version "0.5.11" @@ -2370,7 +2380,7 @@ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.0.4.tgz#cbfbec6735711e7c2fc93b9b40adf70ef5a39990" integrity sha512-gTL8H5USTAKOyVA4xczzDJnC3HMssdFa3tRlwBicXynx9XfiXwneHnYQogwSKpdCkjXISrEKSTtX62rLpNEVQg== -"@sideway/address@^4.1.4": +"@sideway/address@^4.1.3": version "4.1.4" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== @@ -3898,7 +3908,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^1.0.5": +"@types/estree@*", "@types/estree@^1.0.0": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -4077,7 +4087,7 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== -"@types/node-fetch@^2.5.7": +"@types/node-fetch@^2.5.10": version "2.6.11" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24" integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g== @@ -4085,10 +4095,18 @@ "@types/node" "*" form-data "^4.0.0" +"@types/node-fetch@^2.5.7": + version "2.6.10" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.10.tgz#ff5c1ceacab782f2b7ce69957d38c1c27b0dc469" + integrity sha512-PPpPK6F9ALFTn59Ka3BaL+qGuipRfxNE8qVgkp0bVixeiR2c2/L+IVOiBdu9JhhT22sWnQEp6YyHGI2b2+CMcA== + dependencies: + "@types/node" "*" + form-data "^4.0.0" + "@types/node@*", "@types/node@>=13.7.0": - version "20.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.6.tgz#6adf4241460e28be53836529c033a41985f85b6e" - integrity sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q== + version "20.11.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.0.tgz#8e0b99e70c0c1ade1a86c4a282f7b7ef87c9552f" + integrity sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ== dependencies: undici-types "~5.26.4" @@ -4098,9 +4116,9 @@ integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": - version "16.18.75" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.75.tgz#88460b2706e5be1788f5ed6ef51152283b7703a2" - integrity sha512-+FSfZd5mpMDTcIK7bp2GueIcAespzR4FROOXnEst248c85vwthIEwtXYOLgVc/sI4ihE1K/7yO1lEiSgvwAOxA== + version "16.18.70" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.70.tgz#d4c819be1e9f8b69a794d6f2fd929d9ff76f6d4b" + integrity sha512-8eIk20G5VVVQNZNouHjLA2b8utE2NvGybLjMaF4lyhA9uhGwnmXF8o+icdXKGSQSNANJewXva/sFUoZLwAaYAg== "@types/normalize-package-data@^2.4.0": version "2.4.4" @@ -4406,10 +4424,10 @@ "@typescript-eslint/types" "5.14.0" eslint-visitor-keys "^3.0.0" -"@walletconnect/core@2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.11.0.tgz#3a4e301077b2f858fd916b7a20b5b984d1afce63" - integrity sha512-2Tjp5BCevI7dbmqo/OrCjX4tqgMqwJNQLlQAlphqPfvwlF9+tIu6pGcVbSN3U9zyXzWIZCeleqEaWUeSeET4Ew== +"@walletconnect/core@2.11.2": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.11.2.tgz#35286be92c645fa461fecc0dfe25de9f076fca8f" + integrity sha512-bB4SiXX8hX3/hyBfVPC5gwZCXCl+OPj+/EDVM71iAO3TDsh78KPbrVAbDnnsbHzZVHlsMohtXX3j5XVsheN3+g== dependencies: "@walletconnect/heartbeat" "1.2.1" "@walletconnect/jsonrpc-provider" "1.0.13" @@ -4422,8 +4440,8 @@ "@walletconnect/relay-auth" "^1.0.4" "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.11.0" - "@walletconnect/utils" "2.11.0" + "@walletconnect/types" "2.11.2" + "@walletconnect/utils" "2.11.2" events "^3.3.0" isomorphic-unfetch "3.1.0" lodash.isequal "4.5.0" @@ -4533,19 +4551,19 @@ dependencies: tslib "1.14.1" -"@walletconnect/sign-client@2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.11.0.tgz#de10f976cc1b8ab04b7f7c27f6a298e4e083ab25" - integrity sha512-H2ukscibBS+6WrzQWh+WyVBqO5z4F5et12JcwobdwgHnJSlqIoZxqnUYYWNCI5rUR5UKsKWaUyto4AE9N5dw4Q== +"@walletconnect/sign-client@2.11.2": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.11.2.tgz#855609653855f0d23b0502cdbdcf43402e34c459" + integrity sha512-MfBcuSz2GmMH+P7MrCP46mVE5qhP0ZyWA0FyIH6/WuxQ6G+MgKsGfaITqakpRPsykWOJq8tXMs3XvUPDU413OQ== dependencies: - "@walletconnect/core" "2.11.0" + "@walletconnect/core" "2.11.2" "@walletconnect/events" "^1.0.1" "@walletconnect/heartbeat" "1.2.1" "@walletconnect/jsonrpc-utils" "1.0.8" "@walletconnect/logger" "^2.0.1" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.11.0" - "@walletconnect/utils" "2.11.0" + "@walletconnect/types" "2.11.2" + "@walletconnect/utils" "2.11.2" events "^3.3.0" "@walletconnect/time@^1.0.2": @@ -4555,10 +4573,10 @@ dependencies: tslib "1.14.1" -"@walletconnect/types@2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.11.0.tgz#474a009c56faa9ef4063b76ed84415c801dc9f1e" - integrity sha512-AB5b1lrEbCGHxqS2vqfCkIoODieH+ZAUp9rA1O2ftrhnqDJiJK983Df87JhYhECsQUBHHfALphA8ydER0q+9sw== +"@walletconnect/types@2.11.2": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.11.2.tgz#d0359dd4106fcaa1634241a00428d3ea08d0d3c7" + integrity sha512-p632MFB+lJbip2cvtXPBQslpUdiw1sDtQ5y855bOlAGquay+6fZ4h1DcDePeKQDQM3P77ax2a9aNPZxV6y/h1Q== dependencies: "@walletconnect/events" "^1.0.1" "@walletconnect/heartbeat" "1.2.1" @@ -4567,10 +4585,10 @@ "@walletconnect/logger" "^2.0.1" events "^3.3.0" -"@walletconnect/utils@2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.11.0.tgz#31c95151c823022077883dda61800cdea71879b7" - integrity sha512-hxkHPlTlDQILHfIKXlmzgNJau/YcSBC3XHUSuZuKZbNEw3duFT6h6pm3HT/1+j1a22IG05WDsNBuTCRkwss+BQ== +"@walletconnect/utils@2.11.2": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.11.2.tgz#dee0f19adf5e38543612cbe9fa4de7ed28eb7e85" + integrity sha512-LyfdmrnZY6dWqlF4eDrx5jpUwsB2bEPjoqR5Z6rXPiHJKUOdJt7az+mNOn5KTSOlRpd1DmozrBrWr+G9fFLYVw== dependencies: "@stablelib/chacha20poly1305" "1.0.1" "@stablelib/hkdf" "1.0.1" @@ -4580,7 +4598,7 @@ "@walletconnect/relay-api" "^1.0.9" "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.11.0" + "@walletconnect/types" "2.11.2" "@walletconnect/window-getters" "^1.0.1" "@walletconnect/window-metadata" "^1.0.1" detect-browser "5.3.0" @@ -5393,7 +5411,7 @@ axios-mock-adapter@1.21.2: fast-deep-equal "^3.1.3" is-buffer "^2.0.5" -axios@1.6.5: +axios@1.6.5, axios@^1.6.5: version "1.6.5" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== @@ -5409,15 +5427,6 @@ axios@^0.21.1: dependencies: follow-redirects "^1.14.0" -axios@^1.6.5: - version "1.6.6" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.6.tgz#878db45401d91fe9e53aed8ac962ed93bde8dd1c" - integrity sha512-XZLZDFfXKM9U/Y/B4nNynfCRUqNyVZ4sBC/n9GDRCkq9vd2mIvKjKKsbIh1WPmHmNbg6ND7cTBY3Y2+u1G3/2Q== - dependencies: - follow-redirects "^1.15.4" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - babel-jest@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" @@ -5492,12 +5501,12 @@ babel-plugin-macros@^3.0.1: resolve "^1.19.0" babel-plugin-polyfill-corejs2@^0.4.7: - version "0.4.8" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz#dbcc3c8ca758a290d47c3c6a490d59429b0d2269" - integrity sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg== + version "0.4.7" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz#679d1b94bf3360f7682e11f2cb2708828a24fe8c" + integrity sha512-LidDk/tEGDfuHW2DWh/Hgo4rmnw3cduK6ZkOI1NPFceSK3n/yAGeOsNT7FLnSGHkXj3RHGSEVkN3FsCTY6w2CQ== dependencies: "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.5.0" + "@babel/helper-define-polyfill-provider" "^0.4.4" semver "^6.3.1" babel-plugin-polyfill-corejs3@^0.1.0: @@ -5517,11 +5526,11 @@ babel-plugin-polyfill-corejs3@^0.8.7: core-js-compat "^3.33.1" babel-plugin-polyfill-regenerator@^0.5.4: - version "0.5.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz#8b0c8fc6434239e5d7b8a9d1f832bb2b0310f06a" - integrity sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg== + version "0.5.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.4.tgz#c6fc8eab610d3a11eb475391e52584bacfc020f4" + integrity sha512-S/x2iOCvDaCASLYsOOgWOq4bCfKYVqvO/uxjkaYyZ3rVsVE3CeAI/c84NpyuBBymEgNvHgjEot3a9/Z/kXvqsg== dependencies: - "@babel/helper-define-polyfill-provider" "^0.5.0" + "@babel/helper-define-polyfill-provider" "^0.4.4" babel-plugin-react-docgen@^4.2.1: version "4.2.1" @@ -5633,7 +5642,7 @@ bignumber.js@9.0.1: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== -bignumber.js@9.x: +bignumber.js@9.x, bignumber.js@^9.0.0: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== @@ -5864,7 +5873,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.12.0, browserslist@^4.21.10, browserslist@^4.22.2: +browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.22.2: version "4.22.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== @@ -6087,9 +6096,9 @@ can-bind-to-host@^1.1.1: integrity sha512-CqsgmaqiyFRNtP17Ihqa/uHbZxRirntNVNl/kJz31DLKuNRfzvzionkLoUSkElQ6Cz+cpXKA3mhHq4tjbieujA== caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001565: - version "1.0.30001580" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001580.tgz#e3c76bc6fe020d9007647044278954ff8cd17d1e" - integrity sha512-mtj5ur2FFPZcCEpXFy8ADXbDACuNFXg6mxVDqp7tqooX6l3zwm+d8EPoeOSIFRDvHs8qu7/SLFOGniULkcH2iA== + version "1.0.30001576" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz#893be772cf8ee6056d6c1e2d07df365b9ec0a5c4" + integrity sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg== capture-exit@^2.0.0: version "2.0.0" @@ -6594,21 +6603,21 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-compat@^3.31.0, core-js-compat@^3.33.1, core-js-compat@^3.8.1: - version "3.35.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.1.tgz#215247d7edb9e830efa4218ff719beb2803555e2" - integrity sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw== + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.35.0.tgz#c149a3d1ab51e743bc1da61e39cb51f461a41873" + integrity sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw== dependencies: browserslist "^4.22.2" core-js-pure@^3.23.3: - version "3.35.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.35.1.tgz#f33ad7fdf9dddae260339a30e5f8363f5c49a3bc" - integrity sha512-zcIdi/CL3MWbBJYo5YCeVAAx+Sy9yJE9I3/u9LkFABwbeaPhTMRWraM8mYFp9jW5Z50hOy7FVzCc8dCrpZqtIQ== + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.35.0.tgz#4660033304a050215ae82e476bd2513a419fbb34" + integrity sha512-f+eRYmkou59uh7BPcyJ8MC76DiGhspj1KMxVIcF24tzP8NA9HVa1uC7BTW2tgx7E1QVCzDzsgp7kArrzhlz8Ew== core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: - version "3.35.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.1.tgz#9c28f8b7ccee482796f8590cc8d15739eaaf980c" - integrity sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw== + version "3.35.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.0.tgz#58e651688484f83c34196ca13f099574ee53d6b4" + integrity sha512-ntakECeqg81KqMueeGJ79Q5ZgQNR+6eaE8sxGCx62zMbAIj65q+uYvatToew3m6eAGdU4gNZwpZ34NMe4GYswg== core-util-is@~1.0.0: version "1.0.3" @@ -7324,9 +7333,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.601: - version "1.4.645" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.645.tgz#117f964252eb2f0ff00fc7360cb3080e2cf66e3c" - integrity sha512-EeS1oQDCmnYsRDRy2zTeC336a/4LZ6WKqvSaM1jLocEk5ZuyszkQtCpsqvuvaIXGOUjwtvF6LTcS8WueibXvSw== + version "1.4.629" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.629.tgz#9cbffe1b08a5627b6a25118360f7fd3965416caf" + integrity sha512-5UUkr3k3CZ/k+9Sw7vaaIMyOzMC0XbPyprKI3n0tbKDqkzTDOjK4izm7DxlkueRMim6ZZQ1ja9F7hoFVplHihA== element-resize-detector@^1.2.2: version "1.2.4" @@ -9114,7 +9123,7 @@ has-glob@^1.0.0: dependencies: is-glob "^3.0.0" -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: +has-property-descriptors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== @@ -10156,6 +10165,14 @@ isobject@^4.0.0: resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== +isomorphic-fetch@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" + integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== + dependencies: + node-fetch "^2.6.1" + whatwg-fetch "^3.4.1" + isomorphic-timers-promises@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/isomorphic-timers-promises/-/isomorphic-timers-promises-1.0.1.tgz#e4137c24dbc54892de8abae3a4b5c1ffff381598" @@ -10856,13 +10873,13 @@ jiti@^1.21.0: integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== joi@^17.3.0: - version "17.12.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.12.0.tgz#a3fb5715f198beb0471cd551dd26792089c308d5" - integrity sha512-HSLsmSmXz+PV9PYoi3p7cgIbj06WnEBNT28n+bbBNcPZXZFqCzzvGqpTBPujx/Z0nh1+KNQPDrNgdmQ8dq0qYw== + version "17.11.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.11.0.tgz#aa9da753578ec7720e6f0ca2c7046996ed04fc1a" + integrity sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ== dependencies: - "@hapi/hoek" "^9.3.0" - "@hapi/topo" "^5.1.0" - "@sideway/address" "^4.1.4" + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" "@sideway/formula" "^3.0.1" "@sideway/pinpoint" "^2.0.0" @@ -10944,6 +10961,13 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== +json-bigint@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -10994,9 +11018,9 @@ json5@^2.1.2, json5@^2.1.3, json5@^2.2.1, json5@^2.2.3: integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== jsonc-parser@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" - integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== jsonfile@^6.0.1: version "6.1.0" @@ -11878,9 +11902,9 @@ node-addon-api@^2.0.0: integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== node-addon-api@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" - integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== + version "7.0.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.0.0.tgz#8136add2f510997b3b94814f4af1cce0b0e3962e" + integrity sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA== node-dir@^0.1.10: version "0.1.17" @@ -12731,17 +12755,17 @@ platform@1.3.6: resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== -playwright-core@1.41.1, playwright-core@>=1.2.0: - version "1.41.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.41.1.tgz#9c152670010d9d6f970f34b68e3e935d3c487431" - integrity sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg== +playwright-core@1.40.1, playwright-core@>=1.2.0: + version "1.40.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.40.1.tgz#442d15e86866a87d90d07af528e0afabe4c75c05" + integrity sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ== playwright@^1.14.0: - version "1.41.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.41.1.tgz#83325f34165840d019355c2a78a50f21ed9b9c85" - integrity sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ== + version "1.40.1" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.40.1.tgz#a11bf8dca15be5a194851dbbf3df235b9f53d7ae" + integrity sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw== dependencies: - playwright-core "1.41.1" + playwright-core "1.40.1" optionalDependencies: fsevents "2.3.2" @@ -12810,9 +12834,9 @@ postcss-modules-local-by-default@^3.0.2: postcss-value-parser "^4.1.0" postcss-modules-local-by-default@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz#7cbed92abd312b94aaea85b68226d3dec39a14e6" - integrity sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q== + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz#b08eb4f083050708998ba2c6061b50c2870ca524" + integrity sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA== dependencies: icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" @@ -12827,9 +12851,9 @@ postcss-modules-scope@^2.2.0: postcss-selector-parser "^6.0.0" postcss-modules-scope@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz#32cfab55e84887c079a19bbb215e721d683ef134" - integrity sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.1.0.tgz#fbfddfda93a31f310f1d152c2bb4d3f3c5592ee0" + integrity sha512-SaIbK8XW+MZbd0xHPf7kdfA/3eOt7vxJ72IRecn3EzuZVLr1r0orzf0MX/pN8m+NMDoo6X/SQd8oeKqGZd8PXg== dependencies: postcss-selector-parser "^6.0.4" @@ -13827,12 +13851,12 @@ rxjs@^7.5.5, rxjs@^7.8.1: tslib "^2.1.0" safe-array-concat@^1.0.0, safe-array-concat@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" - integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== dependencies: - call-bind "^1.0.5" - get-intrinsic "^1.2.2" + call-bind "^1.0.2" + get-intrinsic "^1.2.1" has-symbols "^1.0.3" isarray "^2.0.5" @@ -13913,9 +13937,9 @@ sass@1.56.1: source-map-js ">=0.6.2 <2.0.0" sass@^1.49.0: - version "1.70.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.70.0.tgz#761197419d97b5358cb25f9dd38c176a8a270a75" - integrity sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ== + version "1.69.7" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.69.7.tgz#6e7e1c8f51e8162faec3e9619babc7da780af3b7" + integrity sha512-rzj2soDeZ8wtE2egyLXgOOHQvaC2iosZrkF6v3EUG+tBwEvhqUCzm0VP3k9gHF9LXbSrRhT5SksoI56Iw8NPnQ== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -14075,15 +14099,14 @@ set-cookie-parser@^2.4.6: integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== set-function-length@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" - integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== dependencies: define-data-property "^1.1.1" - function-bind "^1.1.2" - get-intrinsic "^1.2.2" + get-intrinsic "^1.2.1" gopd "^1.0.1" - has-property-descriptors "^1.0.1" + has-property-descriptors "^1.0.0" set-function-name@^2.0.0, set-function-name@^2.0.1: version "2.0.1" @@ -14375,9 +14398,9 @@ spdx-correct@^3.0.0: spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz#c07a4ede25b16e4f78e6707bbd84b15a45c19c1b" - integrity sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw== + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== spdx-expression-parse@^3.0.0: version "3.0.1" @@ -14536,9 +14559,9 @@ stream-http@^3.2.0: xtend "^4.0.2" stream-shift@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.3.tgz#85b8fab4d71010fc3ba8772e8046cc49b8a3864b" - integrity sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ== + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.2.tgz#548bff71c92322e1ade886979f7f67c0723eb9e4" + integrity sha512-rV4Bovi9xx0BFzOb/X0B2GqoIjvqPCttZdu0Wgtx2Dxkj7ETyWl9gmqJ4EutWRLvtZWm8dxE+InQZX1IryZn/w== strict-event-emitter@^0.2.4: version "0.2.8" @@ -14917,7 +14940,7 @@ terser-webpack-plugin@^4.2.3: terser "^5.3.4" webpack-sources "^1.4.3" -terser-webpack-plugin@^5.3.10: +terser-webpack-plugin@^5.3.7: version "5.3.10" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== @@ -14938,9 +14961,9 @@ terser@^4.1.2, terser@^4.6.3: source-map-support "~0.5.12" terser@^5.26.0, terser@^5.3.4: - version "5.27.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" - integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== + version "5.26.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.26.0.tgz#ee9f05d929f4189a9c28a0feb889d96d50126fe1" + integrity sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -15886,18 +15909,18 @@ webpack@4: webpack-sources "^1.4.1" "webpack@>=4.43.0 <6.0.0": - version "5.90.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.0.tgz#313bfe16080d8b2fee6e29b6c986c0714ad4290e" - integrity sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w== + version "5.89.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" + integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.5" + "@types/estree" "^1.0.0" "@webassemblyjs/ast" "^1.11.5" "@webassemblyjs/wasm-edit" "^1.11.5" "@webassemblyjs/wasm-parser" "^1.11.5" acorn "^8.7.1" acorn-import-assertions "^1.9.0" - browserslist "^4.21.10" + browserslist "^4.14.5" chrome-trace-event "^1.0.2" enhanced-resolve "^5.15.0" es-module-lexer "^1.2.1" @@ -15911,7 +15934,7 @@ webpack@4: neo-async "^2.6.2" schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.10" + terser-webpack-plugin "^5.3.7" watchpack "^2.4.0" webpack-sources "^3.2.3" @@ -15922,6 +15945,11 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" +whatwg-fetch@^3.4.1: + version "3.6.20" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" + integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== + whatwg-mimetype@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"