From 8f6f0b44a36e2aa65cffc4ed962a4b3ce5cbb8d8 Mon Sep 17 00:00:00 2001 From: envin Date: Wed, 14 Feb 2024 16:40:52 +0100 Subject: [PATCH 1/5] feat(redirect-popup): redirect popup to new url --- src/App.jsx | 27 +++++--- .../molecules/popup/RedirectBanner.jsx | 67 +++++++++++++++++++ src/components/molecules/popup/Warning.jsx | 37 ++++++---- src/components/pages/swap/Swap.jsx | 3 - 4 files changed, 108 insertions(+), 26 deletions(-) create mode 100644 src/components/molecules/popup/RedirectBanner.jsx diff --git a/src/App.jsx b/src/App.jsx index 2dcfe47f..44164409 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,26 +1,29 @@ -import React, { useEffect } from 'react' +import React, { useEffect, useState } from 'react' import PropTypes from 'prop-types' +import { Route, Switch, Redirect, useRouteMatch } from 'react-router-dom' +import queryString from 'query-string' +import { connect } from 'react-redux' + +import SwapController from './components/pages/swap/SwapController' +import { sendPageView, setPageLocation } from './ga4' +import SwapOldPntController from './components/pages/swapOldPnt/SwapOldPntController' import { loadSwapData } from './store/swap/swap.actions' import { loadMigrationData } from './store/migration/migration.actions' import { loadSwapOldPntData } from './store/swap-old-pnt/swap-old-pnt.actions' import { selectPage, setTheme } from './store/pages/pages.actions' -import { Route, Switch, Redirect, useRouteMatch } from 'react-router-dom' import history from './utils/history' -import queryString from 'query-string' -import { connect } from 'react-redux' import MigrationController from './components/pages/migration/MigrationController' import MigrationHomeController from './components/pages/migrationHome/MigrationHomeController' -import SwapController from './components/pages/swap/SwapController' -import SwapOldPntController from './components/pages/swapOldPnt/SwapOldPntController' -import HeaderController from './components/organisms/header/HeaderController' -import MainWrapper from './components/atoms/mainWrapper/MainWrapper' -import Notifications from './components/molecules/notifications/Notifications' import NftsController from './components/pages/nfts/NftsController' import Risks from './components/pages/risks/Risks' +import MainWrapper from './components/atoms/mainWrapper/MainWrapper' +import Notifications from './components/molecules/notifications/Notifications' import Popup from './components/molecules/popup/Popup' +import RedirectBanner from './components/molecules/popup/RedirectBanner' +import WarningPopup from './components/molecules/popup/Warning' import SocialLinks from './components/molecules/socials/Socials' import Version from './components/molecules/version/Version' -import { sendPageView, setPageLocation } from './ga4' +import HeaderController from './components/organisms/header/HeaderController' history.listen((location) => { setPageLocation(location.pathname) @@ -66,6 +69,8 @@ const RisksPage = () => { } const App = ({ loading, setTheme, loadSwapData, loadSwapOldPntData, loadMigrationData, selectPage }) => { + const [showWarningPopup, setShowWarningPopup] = useState(true) + useEffect(() => { /* window.location.search -> window.location.hash * window.location.search not available in HashRouter @@ -107,6 +112,8 @@ const App = ({ loading, setTheme, loadSwapData, loadSwapOldPntData, loadMigratio + + setShowWarningPopup(false)} /> } /> theme.bg1}; + font-size: 16px; + padding-top: 0px; + padding-bottom: 0px; + padding-left: 0.5rem; +` + +const WarningIcon = styled(Icon)` + height: 20px; + width: 20px; + margin: 5px; + vertical-align: top; + color: #475965; +` + +const Paragraph = styled.div` + text-align: center; + margin: 0rem; + margin-left: 2px; +` + +const getDomain = () => { + try { + const currentUrl = new URL(window.location.href) + return currentUrl.hostname + } catch (_err) { + // Handle error in case of use with ipns/ipfs enabled browsers + console.error('Error while retreiving url', _err.message) + return null + } +} + +const RedirectBanner = () => { + const domain = getDomain() + return ( + <> + {domain && domain.includes(DISMISSED_DOMAIN) ? ( + + + + + +
+ You are using an old domain that will be dismissed in the future. +
+ Please use pnetworkprotocol.eth +
+
+
+
+ ) : null} + + ) +} + +export default RedirectBanner diff --git a/src/components/molecules/popup/Warning.jsx b/src/components/molecules/popup/Warning.jsx index 330b8c71..a37548c5 100644 --- a/src/components/molecules/popup/Warning.jsx +++ b/src/components/molecules/popup/Warning.jsx @@ -1,8 +1,10 @@ -import React from 'react' import PropTypes from 'prop-types' +import React from 'react' +import { Alert, Row } from 'react-bootstrap' import styled from 'styled-components' -import { Alert } from 'react-bootstrap' + import Icon from '../../atoms/icon/Icon' +import { OuterContainerSwap } from '../../pages/swap/Swap' const StyledAlert = styled(Alert)` // background: ${({ theme }) => theme.bg1}; @@ -13,27 +15,36 @@ const StyledAlert = styled(Alert)` ` const WarningIcon = styled(Icon)` - padding-left: 0px; - padding-bottom: 2px; - padding-right: 5px; + height: 20px; + width: 20px; + margin: 5px; vertical-align: top; color: #475965; ` const Paragraph = styled.div` + text-align: center; margin: 0rem; - margin-left: 2px; + margin-left: 25px; ` const WarningPopup = ({ show, onClose }) => { return ( - - - - pNetwork is a new technology, and security audits don't eliminate risks completely. Please don't provide assets - you can't afford to lose. - - + + + + + +
+ pNetwork is a new technology, and security audits don't +
+ eliminate risks completely. Please don't provide assets you +
+ can't afford to lose. +
+
+
+
) } diff --git a/src/components/pages/swap/Swap.jsx b/src/components/pages/swap/Swap.jsx index 7e596bc9..e4013af0 100644 --- a/src/components/pages/swap/Swap.jsx +++ b/src/components/pages/swap/Swap.jsx @@ -15,7 +15,6 @@ import Button from '../../atoms/button/Button' import Icon from '../../atoms/icon/Icon' import Switch from '../../atoms/switch/Switch' import AddressWarning from '../../molecules/popup/AddressWarning' -import WarningPopup from '../../molecules/popup/Warning' import Progress from '../../molecules/progress/Progress' import AssetListModal from '../../organisms/assetListModal/AssetListModal' import DepositAddressModal from '../../organisms/depositAddressModal/DepositAddressModal' @@ -196,7 +195,6 @@ const Swap = ({ const [notifyMigration, setNotifyMigration] = useState() const [TosShow, setTosShow] = useState(false) const [AddressWarningShow, setAddressWarningShow] = useState(false) - const [showWarningPopup, setShowWarningPopup] = useState(true) const { from, @@ -281,7 +279,6 @@ const Swap = ({ return ( - setShowWarningPopup(false)} /> {assets.find(({ id }) => id === 'OLD_PBTC_ON_BSC_MAINNET') && From 68b5915a76e706df449b40001e67dcc27784b9e5 Mon Sep 17 00:00:00 2001 From: envin Date: Wed, 14 Feb 2024 16:44:43 +0100 Subject: [PATCH 2/5] test(redirect-popup): test redirect popup --- .../popup/__test__/RedirectBanner.test.jsx | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/components/molecules/popup/__test__/RedirectBanner.test.jsx diff --git a/src/components/molecules/popup/__test__/RedirectBanner.test.jsx b/src/components/molecules/popup/__test__/RedirectBanner.test.jsx new file mode 100644 index 00000000..d1e29526 --- /dev/null +++ b/src/components/molecules/popup/__test__/RedirectBanner.test.jsx @@ -0,0 +1,39 @@ +/* eslint-disable import/first */ +import { render, screen } from '@testing-library/react' +import { describe, expect, it, vi } from 'vitest' + +import RedirectBanner from '../RedirectBanner' + +const expectedText = 'You are using an old domain that will be dismissed in the future.' + +describe('RedirectBanner', async () => { + test.each([ + ['http://ptokens.io'], + ['http://dapp.ptokens.io'], + ['https://dapp.ptokens.io/#/swap?asset=btc&from=btc&to=eth'], + ])('Should show the url popup only on specific url', async (url) => { + Object.defineProperty(window, 'location', { + value: { + href: url, + }, + }) + render() + const textElement = screen.getByText((content) => content.includes(expectedText)) + expect(textElement).toBeInTheDocument() + }) + + test.each([ + ['http://p.network/'], + ['http://pnetworkprotocol.eth'], + ['https://dapp.pnetworkprotocol.eth.limo/'], + ['ipns://dapp.pnetworkprotocol.eth']['https://dapp.pnetworkprotocol.eth/#/swap?asset=btc&from=btc&to=eth'], + ])('Should show the url popup only on specific url', async (url) => { + Object.defineProperty(window, 'location', { + value: { + href: url, + }, + }) + const { container } = render() + expect(container.firstChild).toBeNull() + }) +}) From 298ea410dacca6e99e730cc7fd76603d11f4afa7 Mon Sep 17 00:00:00 2001 From: envin Date: Wed, 14 Feb 2024 16:46:36 +0100 Subject: [PATCH 3/5] feat(use-swap): block swap with balance 0 --- src/hooks/use-swap.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hooks/use-swap.js b/src/hooks/use-swap.js index 59a62362..4f20a82e 100644 --- a/src/hooks/use-swap.js +++ b/src/hooks/use-swap.js @@ -340,6 +340,11 @@ const useSwap = ({ return } + if (BigNumber.isBigNumber(from.balance) && from.balance.isZero()) { + updateSwapButton(`${from.symbol} balance is 0`, true) + return + } + if (BigNumber(toAmount).isLessThan(0)) { updateSwapButton('Amount too low', true) return From 2d3100e6ede4ee47d79389834d2b2637617a69e2 Mon Sep 17 00:00:00 2001 From: envin Date: Wed, 14 Feb 2024 16:47:31 +0100 Subject: [PATCH 4/5] test(swap): test swap with 0 balance --- .../pages/swap/__test__/Swap.test.jsx | 63 ++++++++++++++++--- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/src/components/pages/swap/__test__/Swap.test.jsx b/src/components/pages/swap/__test__/Swap.test.jsx index fd2e0533..adc910b7 100644 --- a/src/components/pages/swap/__test__/Swap.test.jsx +++ b/src/components/pages/swap/__test__/Swap.test.jsx @@ -1,15 +1,18 @@ /* eslint-disable import/first */ -import UserEvent from '@testing-library/user-event' -import { describe, expect, it, vi } from 'vitest' import { waitFor, render, screen, getByText } from '@testing-library/react' +import UserEvent from '@testing-library/user-event' +import BigNumber from 'bignumber.js' +import { useCallback, useState } from 'react' import { ThemeContext } from 'styled-components' -import * as SwapInfo from '../../../organisms/swapInfo/SwapInfo' -import * as AssetListModal from '../../../organisms/assetListModal/AssetListModal' -import * as feeUtils from '../../../../utils/fee' -import Swap from '../Swap' +import { describe, expect, it, vi } from 'vitest' + +import { PBTC_ON_ETH_MAINNET, PNT_ON_BSC_MAINNET, PNT_ON_ETH_MAINNET } from '../../../../constants' import swapAssets from '../../../../settings/swap-assets' import { getDefaultSelection } from '../../../../store/swap/utils/default-selection' -import { useCallback, useState } from 'react' +import * as feeUtils from '../../../../utils/fee' +import * as AssetListModal from '../../../organisms/assetListModal/AssetListModal' +import * as SwapInfo from '../../../organisms/swapInfo/SwapInfo' +import Swap from '../Swap' const Wrapper = ({ asset, originBlockchain, destBlockchain }) => { const ThemeContextMock = {} @@ -60,6 +63,48 @@ describe('Swap', async () => { expect(computeSwap).toBeCalledTimes(2) }) + it('Should prevent swap if balance is 0', async () => { + vi.spyOn(SwapInfo, 'default').mockImplementation(() =>
) + vi.spyOn(feeUtils, 'getSwapFees').mockResolvedValue({ basisPoints: 15, networkFee: 1e18, minProtocolFee: 2e18 }) + swapAssets.find((_el) => _el.id === PNT_ON_ETH_MAINNET).balance = BigNumber(0) + swapAssets.find((_el) => _el.id === PNT_ON_BSC_MAINNET).balance = BigNumber(0) + render() + await waitFor(() => expect(screen.getByText(/balance is 0/)).toBeInTheDocument()) + const [, , swapButton] = screen.getAllByRole('button') + const [fromInput, toInput, addressInput] = screen.getAllByRole('textbox') + await UserEvent.type(fromInput, '1') + expect(fromInput).toHaveAttribute('value', '1') + expect(toInput).toHaveAttribute('value', '-2') + expect(swapButton).toHaveTextContent( + `${swapAssets.find((_el) => _el.id === PNT_ON_ETH_MAINNET).symbol} balance is 0` + ) + expect(swapButton).toBeDisabled() + }) + + it('Should continue with balance null', async () => { + vi.spyOn(SwapInfo, 'default').mockImplementation(() =>
) + vi.spyOn(feeUtils, 'getSwapFees').mockResolvedValue({ basisPoints: 15, networkFee: 1e18, minProtocolFee: 2e18 }) + swapAssets.find((_el) => _el.id === 'BTC').balance = null + swapAssets.find((_el) => _el.id === PBTC_ON_ETH_MAINNET).balance = BigNumber(0) + render() + await waitFor(() => expect(screen.getByText(/Enter an address/)).toBeInTheDocument()) + const [, swapButton] = screen.getAllByRole('button') + const [fromInput, toInput, addressInput] = screen.getAllByRole('textbox') + await UserEvent.type(fromInput, '1') + expect(fromInput).toHaveAttribute('value', '1') + expect(toInput).toHaveAttribute('value', '-2') + expect(swapButton).toHaveTextContent('Amount too low') + await UserEvent.type(fromInput, '0') + expect(fromInput).toHaveAttribute('value', '10') + expect(toInput).toHaveAttribute('value', '7') + expect(swapButton).toHaveTextContent('Enter an address') + await UserEvent.type(addressInput, 'tttt') + expect(swapButton).toHaveTextContent('Invalid Address') + await UserEvent.clear(addressInput) + await UserEvent.type(addressInput, '0xA8Ae3c4cF1c92ADFf13e33b35280fc59b6600cA3') + expect(swapButton).toHaveTextContent('Get Deposit Address') + }) + it('Should update "to" amount correctly', async () => { vi.spyOn(SwapInfo, 'default').mockImplementation(() =>
) vi.spyOn(feeUtils, 'getSwapFees').mockResolvedValue({ basisPoints: 15, networkFee: 1e18, minProtocolFee: 2e18 }) @@ -88,13 +133,13 @@ describe('Swap', async () => { render() await waitFor(() => expect(screen.getByText(/Enter an address/)).toBeInTheDocument()) let img1, img2, img3 - ;[, img1, , img2, img3] = screen.getAllByRole('img') + ;[img1, , img2, img3] = screen.getAllByRole('img') expect(img1).toHaveAttribute('src', './assets/svg/BTC.svg') expect(img2).toHaveAttribute('src', './assets/svg/pBTC.svg') expect(img3).toHaveAttribute('src', './assets/svg/ETH.svg') const changeOrderButton = screen.getByTestId('icon-sort') await UserEvent.click(changeOrderButton) - ;[, img1, img2, , , img3] = screen.getAllByRole('img') + ;[img1, img2, , , img3] = screen.getAllByRole('img') expect(img1).toHaveAttribute('src', './assets/svg/pBTC.svg') expect(img2).toHaveAttribute('src', './assets/svg/ETH.svg') expect(img3).toHaveAttribute('src', './assets/svg/BTC.svg') From a9cb7df57fd39fababf00a01d3a0cf826668cd27 Mon Sep 17 00:00:00 2001 From: envin Date: Wed, 14 Feb 2024 17:27:28 +0100 Subject: [PATCH 5/5] chore(package): bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52628f0b..eed08212 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "plain-dapp-update", - "version": "1.19.0", + "version": "1.20.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "plain-dapp-update", - "version": "1.19.0", + "version": "1.20.0", "dependencies": { "@blockshake/defly-connect": "^1.1.5", "@curvefi/api": "^2.28.0", diff --git a/package.json b/package.json index c135589a..872f73bf 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "plain-dapp-update", "private": true, - "version": "1.19.0", + "version": "1.20.0", "type": "module", "scripts": { "dev": "npm run create-version-file && vite",