Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/redirect banner #65

Merged
merged 5 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
27 changes: 17 additions & 10 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -107,6 +112,8 @@ const App = ({ loading, setTheme, loadSwapData, loadSwapOldPntData, loadMigratio
<MainWrapper>
<Notifications />
<HeaderController />
<RedirectBanner/>
<WarningPopup show={showWarningPopup} onClose={() => setShowWarningPopup(false)} />
<Switch>
<Route exact path={'/swap'} render={() => <SwapController />} />
<Route
Expand Down
67 changes: 67 additions & 0 deletions src/components/molecules/popup/RedirectBanner.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import PropTypes from 'prop-types'
import React from 'react'
import { Alert, Row } from 'react-bootstrap'
import styled from 'styled-components'

import Icon from '../../atoms/icon/Icon'
import { OuterContainerSwap } from '../../pages/swap/Swap'

const DISMISSED_DOMAIN = 'ptokens.io'

const StyledAlert = styled(Alert)`
// background: ${({ theme }) => 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) ? (
github-advanced-security[bot] marked this conversation as resolved.
Dismissed
Show resolved Hide resolved
<Row>
<OuterContainerSwap className="mx-auto">
<StyledAlert variant="info">
<Paragraph>
<WarningIcon icon="info" />
<br />
You are using an old domain that will be dismissed in the future.
<br />
Please use <a href="https://dapp.pnetworkprotocol.eth.limo">pnetworkprotocol.eth</a>
</Paragraph>
</StyledAlert>
</OuterContainerSwap>
</Row>
) : null}
</>
)
}

export default RedirectBanner
37 changes: 24 additions & 13 deletions src/components/molecules/popup/Warning.jsx
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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 (
<StyledAlert show={show} onClose={onClose} variant="warning" dismissible>
<Paragraph>
<WarningIcon icon="warning" />
pNetwork is a new technology, and security audits don't eliminate risks completely. Please don't provide assets
you can't afford to lose.
</Paragraph>
</StyledAlert>
<Row>
<OuterContainerSwap className="mx-auto">
<StyledAlert show={show} onClose={onClose} variant="warning" dismissible>
<Paragraph>
<WarningIcon icon="warning" />
<br />
pNetwork is a new technology, and security audits don't
<br />
eliminate risks completely. Please don't provide assets you
<br />
can't afford to lose.
</Paragraph>
</StyledAlert>
</OuterContainerSwap>
</Row>
)
}

Expand Down
39 changes: 39 additions & 0 deletions src/components/molecules/popup/__test__/RedirectBanner.test.jsx
envin3 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -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(<RedirectBanner />)
const textElement = screen.getByText((content) => content.includes(expectedText))
expect(textElement).toBeInTheDocument()
envin3 marked this conversation as resolved.
Show resolved Hide resolved
})

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(<RedirectBanner />)
expect(container.firstChild).toBeNull()
envin3 marked this conversation as resolved.
Show resolved Hide resolved
})
})
3 changes: 0 additions & 3 deletions src/components/pages/swap/Swap.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -281,7 +279,6 @@ const Swap = ({
return (
<React.Fragment>
<Container>
<WarningPopup show={showWarningPopup} onClose={() => setShowWarningPopup(false)} />
<Row>
<Col className="d-flex justify-content-center">
{assets.find(({ id }) => id === 'OLD_PBTC_ON_BSC_MAINNET') &&
Expand Down
63 changes: 54 additions & 9 deletions src/components/pages/swap/__test__/Swap.test.jsx
Original file line number Diff line number Diff line change
@@ -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 = {}
Expand Down Expand Up @@ -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(() => <div data-testid="swap-info" />)
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(<Wrapper asset="pnt" originBlockchain="eth" destBlockchain="bsc" />)
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 () => {
oliviera9 marked this conversation as resolved.
Show resolved Hide resolved
vi.spyOn(SwapInfo, 'default').mockImplementation(() => <div data-testid="swap-info" />)
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(<Wrapper asset="btc" originBlockchain="btc" destBlockchain="eth" />)
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(() => <div data-testid="swap-info" />)
vi.spyOn(feeUtils, 'getSwapFees').mockResolvedValue({ basisPoints: 15, networkFee: 1e18, minProtocolFee: 2e18 })
Expand Down Expand Up @@ -88,13 +133,13 @@ describe('Swap', async () => {
render(<Wrapper />)
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')
Expand Down
5 changes: 5 additions & 0 deletions src/hooks/use-swap.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading