Compare commits

...
Author SHA1 Message Date
Matthew Russell a14ed6a997 chore: add revised first bullet 2023-05-19 13:55:30 -07:00
Matthew Russell aa1a875095 chore: remove first discalimer bullet point 2023-05-19 13:32:52 -07:00
Matthew Russell 61b10e0580 chore: align risk message design and welcome dialog 2023-05-19 12:46:26 -07:00
Matthew Russell 0ecd0828cf chore: adjust design of disclaimer dialog 2023-05-19 12:32:01 -07:00
Matthew Russell 1678e17ac3 chore: update layout of disclaimer page to match other pages on governance 2023-05-19 12:05:37 -07:00
maciek 6f3c1cbef7 feat: improve flow of risk disclaimer presentation - remove unnecessary store values 2023-05-19 17:29:20 +02:00
maciek 8da85a2126 Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-19 17:25:51 +02:00
maciek d051eb5612 feat: improve flow of risk disclaimer presentation - adjust mainnet telemetry for 2023-05-19 17:25:24 +02:00
maciek ba73f7f7a6 Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-19 15:33:27 +02:00
maciek 775b67b478 feat: improve flow of risk disclaimer presentation - add unit test 2023-05-19 15:33:07 +02:00
maciek 99267dd099 feat: improve flow of risk disclaimer presentation - add unit test 2023-05-19 15:14:30 +02:00
maciek 5c436b3bf3 feat: improve flow of risk disclaimer presentation - fix failing unit tests 2023-05-19 14:57:41 +02:00
maciek 904fee83b5 Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-19 14:36:29 +02:00
maciek 96078c6cc8 feat: improve flow of risk disclaimer presentation - fix lint problems 2023-05-19 14:36:08 +02:00
maciek 275d91e7a5 Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-19 13:04:12 +02:00
maciek 11bf1b7e48 feat: improve flow of risk disclaimer presentation - refactor for make it more compact 2023-05-19 13:03:44 +02:00
maciek 13ded71724 Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-19 08:59:07 +02:00
maciek 46435ef743 feat: improve flow of risk disclaimer presentation - make draft for parametrization vega wallet 2023-05-19 08:58:54 +02:00
maciek 16d1e127ee Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-18 18:29:35 +02:00
maciek 2423cfb4cc feat: improve flow of risk disclaimer presentation - make draft for parametrization vega wallet 2023-05-18 18:28:47 +02:00
maciek 5b8754365e Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-18 12:02:05 +02:00
maciek d35fb7a26d feat: improve flow of risk disclaimer presentation - make int test more durable 2023-05-17 13:17:23 +02:00
maciek 33be090dd6 Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-17 12:11:54 +02:00
maciek b5df733a4e chore: refactor createDocsUrl for fully static - add an int test 2023-05-17 12:11:37 +02:00
maciek 91e58c391f Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-17 11:10:39 +02:00
maciek f1422667dd chore: refactor createDocsUrl for fully static - fix failing int tests 2023-05-17 11:10:14 +02:00
maciek e7851186a7 Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-17 08:53:36 +02:00
maciek 3fc4a71edd Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-16 17:09:32 +02:00
maciek e8f0bc7380 feat: improve flow of risk disclaimer presentation - fix typo 2023-05-16 17:09:19 +02:00
maciek c08dca33f4 feat: improve flow of risk disclaimer presentation - fix failling unit tests 2023-05-15 18:00:24 +02:00
maciek 71852faa36 Merge remote-tracking branch 'origin/develop' into feat/3737-console-in-context-messaging 2023-05-15 17:52:29 +02:00
maciek 51a8d99150 feat: improve flow of risk disclaimer presentation - prevent eager connect when np risk accepted 2023-05-15 17:52:02 +02:00
maciek 264b9a59dd feat: improve flow of risk disclaimer presentation 2023-05-15 16:44:21 +02:00
29 changed files with 404 additions and 136 deletions
@@ -0,0 +1,40 @@
import { t } from '@vegaprotocol/i18n';
import {
ExternalLink,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import Routes from '../../routes/routes';
export const RiskMessage = () => {
return (
<>
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
<ul className="list-[square] ml-4">
<li>
{t(
'You may encounter bugs, loss of functionality or loss of assets using the App.'
)}
</li>
<li>
{t(
'No party accepts any liability for any losses whatsoever related to its use.'
)}
</li>
</ul>
</div>
<p className="mb-8">
{t(
'By using the Vega Governance App, you acknowledge that you have read and understood the'
)}{' '}
<ExternalLink href={Routes.DISCLAIMER} className="underline">
<span className="flex items-center gap-1">
<span>{t('Vega Governance Disclaimer')}</span>
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</span>
</ExternalLink>
.
</p>
</>
);
};
@@ -4,9 +4,11 @@ import {
useAppState,
} from '../../contexts/app-state/app-state-context';
import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
export const VegaWalletDialogs = () => {
const { appState, appDispatch } = useAppState();
return (
<>
<VegaConnectDialog
@@ -17,6 +19,7 @@ export const VegaWalletDialogs = () => {
isOpen: open,
})
}
riskMessage={<RiskMessage />}
/>
<VegaManageDialog
@@ -815,5 +815,11 @@
"NoThanks": "No thanks",
"ShareData": "Share data",
"ContinueSharingData": "Continue sharing data",
"NodeUnsuitable": "Node: {{url}} is unsuitable"
"NodeUnsuitable": "Node: {{url}} is unsuitable",
"Disclaimer": "Disclaimer",
"disclaimer1": "The Vega Governance App allows the Vega network to arrive at on-chain decisions, where tokenholders can create proposals that other tokenholders can vote to approve or reject. Vega supports on-chain proposals for creating markets and assets, and changing network parameters, markets and assets. Vega also supports freeform proposals for community suggestions that will not be enacted on-chain.",
"disclaimer2": "The Vega Governance App is free, public and open source software. Software upgrades may contain bugs or security vulnerabilities that might result in loss of functionality.",
"disclaimer3": "The Vega Governance App uses data obtained from nodes on the Vega Blockchain. The developers of the Vega Governance App do not operate or run the Vega Blockchain or any other blockchain.",
"disclaimer4": "The Vega Governance App is provided “as is”. The developers of the Vega Governance App make no representations or warranties of any kind, whether express or implied, statutory or otherwise regarding the Vega Governance App. They disclaim all warranties of merchantability, quality, fitness for purpose. They disclaim all warranties that the Vega Governance App is free of harmful components or errors.",
"disclaimer5": "No developer of the Vega Governance App accepts any responsibility for, or liability to users in connection with their use of the Vega Governance App."
}
@@ -0,0 +1,21 @@
import type { RouteChildProps } from '..';
import { useDocumentTitle } from '../../hooks/use-document-title';
import { Heading } from '../../components/heading';
import { useTranslation } from 'react-i18next';
const Disclaimer = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
const { t } = useTranslation();
return (
<>
<Heading title={t('Disclaimer')} />
<p className="mb-6 mt-10">{t('disclaimer1')}</p>
<p className="mb-6">{t('disclaimer2')}</p>
<p className="mb-6">{t('disclaimer3')}</p>
<p className="mb-8">{t('disclaimer4')}</p>
<p className="mb-8">{t('disclaimer5')}</p>
</>
);
};
export default Disclaimer;
@@ -207,6 +207,13 @@ const LazyWithdrawals = React.lazy(
)
);
const LazyDisclaimer = React.lazy(
() =>
import(
/* webpackChunkName: "route-disclaimer", webpackPrefetch: true */ './disclaimer'
)
);
const redirects = [
{
path: Routes.VALIDATORS,
@@ -349,6 +356,10 @@ const routerConfig = [
path: Routes.CONTRACTS,
element: <LazyContracts name="Contracts" />,
},
{
path: Routes.DISCLAIMER,
element: <LazyDisclaimer name="Disclaimer" />,
},
{
path: '*',
// Not lazy as loaded when a user first hits the site
+1
View File
@@ -15,6 +15,7 @@ const Routes = {
SUPPLY: '/token/tranches',
ASSOCIATE: '/token/associate',
DISASSOCIATE: '/token/disassociate',
DISCLAIMER: '/disclaimer',
};
export default Routes;
+13 -1
View File
@@ -1,7 +1,7 @@
import { mockConnectWallet } from '@vegaprotocol/cypress';
describe('Navbar', { tags: '@smoke' }, () => {
before(() => {
beforeEach(() => {
cy.clearAllLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
@@ -47,6 +47,18 @@ describe('Navbar', { tags: '@smoke' }, () => {
});
});
});
it('Disclaimer should be presented after choosing from menu', () => {
cy.get('nav')
.find('ul li:contains(Resources)')
.contains('Resources')
.click();
cy.getByTestId('Disclaimer').eq(0).click();
cy.location('hash').should('equal', '#/disclaimer');
cy.get('p').contains(
'Vega is a decentralised peer-to-peer protocol that can be used to trade derivatives with cryptoassets.'
);
});
});
describe('mobile view', () => {
@@ -0,0 +1,48 @@
import { t } from '@vegaprotocol/i18n';
export const Disclaimer = () => {
return (
<div className="py-16 px-8 flex w-full justify-center">
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
<h1 className="text-4xl xl:text-5xl uppercase font-alpha calt">
{t('Disclaimer')}
</h1>
<p className="mb-6 mt-10">
{t(
'Vega is a decentralised peer-to-peer protocol that can be used to trade derivatives with cryptoassets. The Vega Protocol is an implementation layer (layer one) protocol made of free, public, open-source or source-available software. Use of the Vega Protocol involves various risks, including but not limited to, losses while digital assets are supplied to the Vega Protocol and losses due to the fluctuation of prices of assets.'
)}
</p>
<p className="mb-6">
{t(
'Before using the Vega Protocol, review the relevant documentation at docs.vega.xyz to make sure that you understand how it works. Conduct your own due diligence and consult your financial advisor before making any investment decisions.'
)}
</p>
<p className="mb-6">
{t(
'As described in the Vega Protocol core license, the Vega Protocol is provided “as is”, at your own risk, and without warranties of any kind. Although Gobalsky Labs Limited developed much of the initial code for the Vega Protocol, it does not provide or control the Vega Protocol, which is run by third parties deploying it on a bespoke blockchain. Upgrades and modifications to the Vega Protocol are managed in a community-driven way by holders of the VEGA governance token.'
)}
</p>
<p className="mb-8">
{t(
'No developer or entity involved in creating the Vega Protocol will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the Vega Protocol, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or legal costs, or loss of profits, cryptoassets, tokens or anything else of value.'
)}
</p>
<p className="mb-8">
{t(
'This website is hosted on a decentralised network, the Interplanetary File System (“IPFS”). The IPFS decentralised web is made up of all the computers (nodes) connected to it. Data is therefore stored on many different computers.'
)}
</p>
<p className="mb-8">
{t(
"The information provided on this website does not constitute investment advice, financial advice, trading advice, or any other sort of advice and you should not treat any of the website's content as such. No party recommends that any cryptoasset should be bought, sold, or held by you via this website. No party ensures the accuracy of information listed on this website or holds any responsibility for any missing or wrong information. You understand that you are using any and all information available here at your own risk."
)}
</p>
<p className="mb-8">
{t(
'Additionally, just as you can access email protocols such as SMTP through multiple email clients, you can potentially access the Vega Protocol through many web or mobile interfaces. You are responsible for doing your own diligence on those interfaces to understand the associated risks and any fees.'
)}
</p>
</div>
</div>
);
};
@@ -0,0 +1,3 @@
import { Disclaimer } from './disclaimer';
export default Disclaimer;
+10 -3
View File
@@ -36,9 +36,8 @@ export const Navbar = ({
}) => {
const { GITHUB_FEEDBACK_URL } = useEnvironment();
const tokenLink = useLinks(DApp.Token);
const { marketId } = useGlobalStore((store) => ({
marketId: store.marketId,
}));
const marketId = useGlobalStore((store) => store.marketId);
const tradingPath = marketId
? Links[Routes.MARKET](marketId)
: Links[Routes.MARKET]();
@@ -107,6 +106,14 @@ export const Navbar = ({
{t('Give Feedback')}
</NavExternalLink>
</NavigationItem>
<NavigationItem>
<NavigationLink
data-testid="Disclaimer"
to={Links[Routes.DISCLAIMER]()}
>
{t('Disclaimer')}
</NavigationLink>
</NavigationItem>
</NavigationList>
</NavigationContent>
</NavigationItem>
@@ -1 +1,2 @@
export * from './welcome-dialog';
export * from './risk-message';
@@ -0,0 +1,45 @@
import { t } from '@vegaprotocol/i18n';
import {
ExternalLink,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { Links, Routes } from '../../pages/client-router';
export const RiskMessage = () => {
return (
<>
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
<ul className="list-[square] ml-4">
<li className="mb-1">
{t(
'Conduct your own due diligence and consult your financial advisor before making any investment decisions.'
)}
</li>
<li className="mb-1">
{t(
'You may encounter bugs, loss of functionality or loss of assets.'
)}
</li>
<li>
{t('No party accepts any liability for any losses whatsoever.')}
</li>
</ul>
</div>
<p className="mb-8">
{t(
'By using the Vega Console, you acknowledge that you have read and understood the'
)}{' '}
<ExternalLink
href={`/#/${Links[Routes.DISCLAIMER]()}`}
className="underline"
>
<span className="flex items-center gap-1">
<span>{t('Vega Console Disclaimer')}</span>
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</span>
</ExternalLink>
</p>
</>
);
};
@@ -47,25 +47,4 @@ describe('Risk notice dialog', () => {
expect(mockOnClose).toHaveBeenCalled();
}
);
it('displays a risk message for mainnet', () => {
const introText = 'Regulation may apply to use of this app';
const network = Networks.MAINNET;
// @ts-ignore ignore mock implementation
useEnvironment.mockImplementation(() => ({
...mockEnvDefinitions,
VEGA_ENV: network,
}));
render(<RiskNoticeDialog onClose={mockOnClose} network={network} />);
expect(screen.getByText(introText)).toBeInTheDocument();
const button = screen.getByRole('button', {
name: 'I understand, Continue',
});
fireEvent.click(button);
expect(mockOnClose).toHaveBeenCalled();
});
});
@@ -7,8 +7,8 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { RISK_ACCEPTED_KEY } from '../constants';
import { TelemetryApproval } from './telemetry-approval';
import type { Networks } from '@vegaprotocol/environment';
import {
Networks,
useEnvironment,
DocsLinks,
ExternalLinks,
@@ -28,29 +28,17 @@ export const RiskNoticeDialog = ({ onClose, network }: Props) => {
};
return (
<>
{network === Networks.MAINNET ? (
<MainnetContent />
) : (
<TestnetContent network={network} />
)}
<div className="my-4">
<TelemetryApproval
helpText={t(
'Help identify bugs and improve the service by sharing anonymous usage data. You can change this in your settings at any time.'
)}
/>
</div>
<Button onClick={handleAcceptRisk}>
{network === Networks.MAINNET
? t('I understand, Continue')
: t('Continue')}
</Button>
</>
<TestnetContent network={network} handleAcceptRisk={handleAcceptRisk} />
);
};
const TestnetContent = ({ network }: { network: Networks }) => {
const TestnetContent = ({
network,
handleAcceptRisk,
}: {
network: Networks;
handleAcceptRisk: () => void;
}) => {
const { GITHUB_FEEDBACK_URL } = useEnvironment();
return (
<>
@@ -88,29 +76,14 @@ const TestnetContent = ({ network }: { network: Networks }) => {
</li>
</ul>
)}
</>
);
};
const MainnetContent = () => {
return (
<>
<h4 className="text-xl mb-2 mt-4">
{t('Regulation may apply to use of this app')}
</h4>
<p className="mb-6">
{t(
'This decentralised application allows you to connect to and use publicly available blockchain services operated by third parties that may include trading, financial products, or other services that may be subject to legal and regulatory restrictions in your jurisdiction. This application is a front end only and does not hold any funds or provide any products or services. It is available to anyone with an internet connection via IPFS and other methods, and the ability to access it does not imply any right to use any services or that it is legal for you to do so. By using this application you accept that it is your responsibility to ensure that your use of the application and any blockchain services accessed through it is compliant with applicable laws and regulations in your jusrisdiction.'
)}
</p>
<h4 className="text-xl mb-2">
{t('Technical and financial risk of loss')}
</h4>
<p className="mb-8">
{t(
'The public blockchain services accessible via this decentralised application are operated by third parties and may carry significant risks including the potential loss of all funds that you deposit or hold with these services. Technical risks include the risk of loss in the event of the failure or compromise of the public blockchain infrastructure or smart contracts that provide any services you use. Financial risks include but are not limited to losses due to volatility, excessive leverage, low liquidity, and your own lack of understanding of the services you use. By using this decentralised application you accept that it is your responsibility to ensure that you understand any services you use and the technical and financial risks inherent in your use. Do not risk what you cannot afford to lose.'
)}
</p>
<div className="my-4">
<TelemetryApproval
helpText={t(
'Help identify bugs and improve the service by sharing anonymous usage data. You can change this in your settings at any time.'
)}
/>
</div>
<Button onClick={handleAcceptRisk}>{t('Continue')}</Button>
</>
);
};
@@ -5,7 +5,7 @@ import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
export const TelemetryApproval = ({ helpText }: { helpText: string }) => {
const [isApproved, setIsApproved] = useTelemetryApproval();
return (
<div className="flex flex-col px-2 py-3">
<div className="flex flex-col py-3">
<div className="mr-4" role="form">
<Checkbox
label={<span className="text-lg pl-1">{t('Share usage data')}</span>}
@@ -30,17 +30,24 @@ export const WelcomeDialog = () => {
const shouldDisplayWelcomeDialog = useGlobalStore(
(store) => store.shouldDisplayWelcomeDialog
);
const isRiskDialogNeeded = riskAccepted !== 'true' && !('Cypress' in window);
const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog;
const onCloseDialog = useCallback(() => {
update({ shouldDisplayWelcomeDialog: isRiskDialogNeeded });
}, [update, isRiskDialogNeeded]);
const isRiskDialogNeeded =
riskAccepted !== 'true' &&
VEGA_ENV !== Networks.MAINNET &&
!('Cypress' in window);
const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog;
const onCloseDialog = useCallback(() => {
update({
shouldDisplayWelcomeDialog: isRiskDialogNeeded,
});
}, [update, isRiskDialogNeeded]);
if (isRiskDialogNeeded) {
dialogContent = (
<RiskNoticeDialog onClose={onCloseDialog} network={VEGA_ENV} />
);
title = VEGA_ENV === Networks.MAINNET ? t('WARNING') : t('Vega Console');
title = t('Vega Console');
size = 'medium';
} else if (isWelcomeDialogNeeded && data?.length === 0) {
dialogContent = <WelcomeNoticeDialog />;
@@ -16,6 +16,8 @@ import { Link } from 'react-router-dom';
import { ProposedMarkets } from './proposed-markets';
import { Links, Routes } from '../../pages/client-router';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { TelemetryApproval } from './telemetry-approval';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
export const SelectMarketLandingTable = ({
markets,
@@ -94,6 +96,8 @@ export const WelcomeLandingDialog = ({
onClose,
}: LandingDialogContainerProps) => {
const { data, loading, error } = useMarketList();
const { VEGA_ENV } = useEnvironment();
const isMainnet = VEGA_ENV === Networks.MAINNET;
if (error) {
return (
<div className="flex justify-center items-center">
@@ -114,6 +118,13 @@ export const WelcomeLandingDialog = ({
<>
<WelcomeDialogHeader />
<SelectMarketLandingTable markets={data} onClose={onClose} />
{isMainnet && (
<TelemetryApproval
helpText={t(
'Help identify bugs and improve the service by sharing anonymous usage data. You can change this in your settings at any time.'
)}
/>
)}
</>
);
};
@@ -10,6 +10,7 @@ import {
ExternalLinks,
} from '@vegaprotocol/environment';
import { ProposedMarkets } from './proposed-markets';
import { TelemetryApproval } from './telemetry-approval';
export const WelcomeNoticeDialog = () => {
const { VEGA_ENV } = useEnvironment();
@@ -20,30 +21,30 @@ export const WelcomeNoticeDialog = () => {
<>
<h1
data-testid="welcome-notice-title"
className="mb-6 p-4 text-center text-2xl"
className="text-2xl uppercase mb-6 text-center font-alpha calt"
>
{t('Welcome to Console')}
</h1>
<p className="leading-6 mb-7">
<p className="leading-6 mb-4">
{t(
'There are no markets to trade on right now. Trading on Vega is now live, but markets need to pass a governance vote before they can be traded on. In the meantime:'
)}
</p>
<ul className="list-[square] pl-7">
<ul className="list-[square] pl-4 mb-4">
{isMainnet && (
<li>
<li className="mb-1">
<ExternalLink target="_blank" href={consoleFairgroundLink()}>
{t('Try out Console')}
</ExternalLink>
{t(' on Fairground, our Testnet')}
</li>
)}
<li>
<li className="mb-1">
<ExternalLink target="_blank" href={tokenLink(TOKEN_PROPOSALS)}>
{t('View and vote for proposed markets')}
</ExternalLink>
</li>
<li>
<li className="mb-1">
<ExternalLink
target="_blank"
href={tokenLink(TOKEN_NEW_MARKET_PROPOSAL)}
@@ -51,12 +52,19 @@ export const WelcomeNoticeDialog = () => {
{t('Propose a market')}
</ExternalLink>
</li>
<li>
<li className="mb-1">
<ExternalLink target="_blank" href={ExternalLinks.BLOG}>
{t('Read about the mainnet launch')}
</ExternalLink>
</li>
</ul>
{isMainnet && (
<TelemetryApproval
helpText={t(
'Help identify bugs and improve the service by sharing anonymous usage data. You can change this in your settings at any time.'
)}
/>
)}
<ProposedMarkets />
</>
);
+10
View File
@@ -30,6 +30,10 @@ const LazySettings = dynamic(() => import('../client-pages/settings'), {
ssr: false,
});
const LazyDisclaimer = dynamic(() => import('../client-pages/disclaimer'), {
ssr: false,
});
export enum Routes {
HOME = '/',
MARKET = '/markets',
@@ -37,6 +41,7 @@ export enum Routes {
PORTFOLIO = '/portfolio',
LIQUIDITY = 'liquidity/:marketId',
SETTINGS = 'settings',
DISCLAIMER = 'disclaimer',
}
type ConsoleLinks = { [r in Routes]: (...args: string[]) => string };
@@ -51,6 +56,7 @@ export const Links: ConsoleLinks = {
? trimEnd(`${Routes.LIQUIDITY}/${marketId}`, '/')
: Routes.LIQUIDITY,
[Routes.SETTINGS]: () => Routes.SETTINGS,
[Routes.DISCLAIMER]: () => Routes.DISCLAIMER,
};
const routerConfig: RouteObject[] = [
@@ -97,6 +103,10 @@ const routerConfig: RouteObject[] = [
path: Routes.SETTINGS,
element: <LazySettings />,
},
{
path: Routes.DISCLAIMER,
element: <LazyDisclaimer />,
},
{
path: '*',
element: (
+5 -2
View File
@@ -12,13 +12,16 @@ import {
} from '@vegaprotocol/web3';
import { WelcomeDialog } from '../components/welcome-dialog';
import { TransferDialog } from '@vegaprotocol/accounts';
import { RiskMessage } from '../components/welcome-dialog';
const DialogsContainer = () => {
const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore();
return (
<>
<VegaConnectDialog connectors={Connectors} />
<VegaConnectDialog
connectors={Connectors}
riskMessage={<RiskMessage />}
/>
<AssetDetailsDialog
assetId={id}
trigger={trigger || null}
@@ -60,6 +60,7 @@ export function addVegaWalletConnect() {
export function addSetVegaWallet() {
Cypress.Commands.add('setVegaWallet', () => {
cy.window().then((win) => {
win.localStorage.setItem('vega_risk_accepted', 'true');
win.localStorage.setItem(
'vega_wallet_config',
JSON.stringify({
@@ -30,6 +30,7 @@ import type { Status } from '../use-json-rpc-connect';
import { useJsonRpcConnect } from '../use-json-rpc-connect';
import { ViewConnectorForm } from './view-connector-form';
import { useChainIdQuery } from './__generated__/ChainId';
import { useVegaWallet } from '../use-vega-wallet';
export const CLOSE_DELAY = 1700;
type Connectors = { [key: string]: VegaConnector };
@@ -38,6 +39,7 @@ type WalletType = 'jsonRpc' | 'hosted' | 'view';
export interface VegaConnectDialogProps {
connectors: Connectors;
onChangeOpen?: (open: boolean) => void;
riskMessage?: React.ReactNode;
}
export const useVegaWalletDialogStore = create<VegaWalletDialogStore>()(
@@ -60,25 +62,30 @@ export interface VegaWalletDialogStore {
export const VegaConnectDialog = ({
connectors,
onChangeOpen,
riskMessage,
}: VegaConnectDialogProps) => {
const vegaWalletDialogOpen = useVegaWalletDialogStore(
(store) => store.vegaWalletDialogOpen
);
const updateVegaWalletDialog = useVegaWalletDialogStore((store) =>
onChangeOpen
? (open: boolean) => {
store.updateVegaWalletDialog(open);
onChangeOpen(open);
}
: store.updateVegaWalletDialog
const updateVegaWalletDialog = useVegaWalletDialogStore(
(store) => (open: boolean) => {
store.updateVegaWalletDialog(open);
onChangeOpen?.(open);
}
);
const closeVegaWalletDialog = useVegaWalletDialogStore((store) =>
onChangeOpen
? () => {
store.closeVegaWalletDialog();
onChangeOpen(false);
}
: store.closeVegaWalletDialog
const closeVegaWalletDialog = useVegaWalletDialogStore((store) => () => {
store.closeVegaWalletDialog();
onChangeOpen?.(false);
});
const { disconnect, acknowledgeNeeded } = useVegaWallet();
const onVegaWalletDialogChange = useCallback(
(open: boolean) => {
updateVegaWalletDialog(open);
if (!open && acknowledgeNeeded) {
disconnect();
}
},
[updateVegaWalletDialog, acknowledgeNeeded, disconnect]
);
const { data, error, loading } = useChainIdQuery();
@@ -112,6 +119,7 @@ export const VegaConnectDialog = ({
connectors={connectors}
closeDialog={closeVegaWalletDialog}
appChainId={data.statistics.chainId}
riskMessage={riskMessage}
/>
);
};
@@ -120,7 +128,7 @@ export const VegaConnectDialog = ({
<Dialog
open={vegaWalletDialogOpen}
size="small"
onChange={updateVegaWalletDialog}
onChange={onVegaWalletDialogChange}
>
{renderContent()}
</Dialog>
@@ -131,16 +139,17 @@ const ConnectDialogContainer = ({
connectors,
closeDialog,
appChainId,
riskMessage,
}: {
connectors: Connectors;
closeDialog: () => void;
appChainId: string;
riskMessage?: React.ReactNode;
}) => {
const { VEGA_WALLET_URL, VEGA_ENV, HOSTED_WALLET_URL } = useEnvironment();
const [selectedConnector, setSelectedConnector] = useState<VegaConnector>();
const [walletUrl, setWalletUrl] = useState(VEGA_WALLET_URL || '');
const [walletType, setWalletType] = useState<WalletType>();
const reset = useCallback(() => {
setSelectedConnector(undefined);
setWalletType(undefined);
@@ -189,6 +198,7 @@ const ConnectDialogContainer = ({
onConnect={closeDialog}
appChainId={appChainId}
reset={reset}
riskMessage={riskMessage}
/>
) : (
<ConnectorList
@@ -255,6 +265,7 @@ const SelectedForm = ({
jsonRpcState,
reset,
onConnect,
riskMessage,
}: {
type: WalletType;
connector: VegaConnector;
@@ -265,6 +276,7 @@ const SelectedForm = ({
};
reset: () => void;
onConnect: () => void;
riskMessage?: React.ReactNode;
}) => {
if (connector instanceof RestConnector) {
return (
@@ -317,6 +329,7 @@ const SelectedForm = ({
onConnect={onConnect}
appChainId={appChainId}
reset={reset}
riskMessage={riskMessage}
/>
</ConnectDialogContent>
<ConnectDialogFooter />
@@ -1,6 +1,7 @@
import capitalize from 'lodash/capitalize';
import { t } from '@vegaprotocol/i18n';
import {
Button,
ButtonLink,
Diamond,
Link,
@@ -14,6 +15,8 @@ import { ClientErrors } from '../connectors';
import { ConnectDialogTitle } from './connect-dialog-elements';
import { Status } from '../use-json-rpc-connect';
import { DocsLinks } from '@vegaprotocol/environment';
import { useVegaWallet } from '../use-vega-wallet';
import { setAcknowledged } from '../storage';
export const ServiceErrors = {
NO_HEALTHY_NODE: 1000,
@@ -26,6 +29,8 @@ export const JsonRpcConnectorForm = ({
status,
error,
reset,
onConnect,
riskMessage,
}: {
connector: JsonRpcConnector;
appChainId: string;
@@ -33,35 +38,13 @@ export const JsonRpcConnectorForm = ({
error: WalletClientError | null;
onConnect: () => void;
reset: () => void;
riskMessage?: React.ReactNode;
}) => {
const { disconnect } = useVegaWallet();
if (status === Status.Idle) {
return null;
}
return (
<Connecting
status={status}
error={error}
connector={connector}
appChainId={appChainId}
reset={reset}
/>
);
};
const Connecting = ({
status,
error,
connector,
appChainId,
reset,
}: {
status: Status;
error: WalletClientError | null;
connector: JsonRpcConnector;
appChainId: string;
reset: () => void;
}) => {
if (status === Status.Error) {
return (
<Error
@@ -125,6 +108,34 @@ const Connecting = ({
);
}
if (status === Status.AcknowledgeNeeded) {
const setConnection = () => {
setAcknowledged();
onConnect();
};
const handleDisagree = () => {
disconnect();
onConnect(); // this is dialog closing
};
return (
<>
<ConnectDialogTitle>{t('Understand the risk')}</ConnectDialogTitle>
{riskMessage}
<div className="grid grid-cols-2 gap-5">
<div>
<Button onClick={handleDisagree} fill>
{t('Cancel')}
</Button>
</div>
<div>
<Button onClick={setConnection} variant="primary" fill>
{t('I agree')}
</Button>
</div>
</div>
</>
);
}
return null;
};
+3
View File
@@ -34,6 +34,9 @@ export interface VegaWalletContextShape {
/** Fetch public keys */
fetchPubKeys?: () => Promise<PubKey[] | null>;
/** Acknowledge disclaimer */
acknowledgeNeeded?: boolean;
}
export const VegaWalletContext = createContext<
+29
View File
@@ -7,6 +7,8 @@ import { VegaWalletProvider } from './provider';
import { LocalStorage } from '@vegaprotocol/utils';
import type { ReactNode } from 'react';
import { WALLET_KEY } from './storage';
import * as Environment from '@vegaprotocol/environment';
import * as ReactHelpers from '@vegaprotocol/react-helpers';
const restConnector = new RestConnector();
const viewConnector = new ViewConnector();
@@ -43,6 +45,7 @@ describe('VegaWalletProvider', () => {
// Default state
expect(result.current).toEqual({
acknowledgeNeeded: false,
pubKey: null,
pubKeys: null,
isReadOnly: false,
@@ -83,6 +86,7 @@ describe('VegaWalletProvider', () => {
// Default state
expect(result.current).toEqual({
acknowledgeNeeded: false,
pubKey: null,
pubKeys: null,
isReadOnly: false,
@@ -142,4 +146,29 @@ describe('VegaWalletProvider', () => {
});
expect(result.current.isReadOnly).toBe(true);
});
it('acknowledgeNeeded will set on', async () => {
jest
.spyOn(Environment, 'useEnvironment')
.mockReturnValue({ VEGA_ENV: 'MAINNET' });
jest.spyOn(ReactHelpers, 'useLocalStorage').mockImplementation(() => [
'',
() => {
/**/
},
() => {
/**/
},
]);
jest
.spyOn(viewConnector, 'connect')
.mockImplementation(() => Promise.resolve(mockPubKeys));
const { result } = setup();
expect(result.current.acknowledgeNeeded).toBe(true);
await act(async () => {
result.current.connect(viewConnector);
});
expect(result.current.isReadOnly).toBe(true);
});
});
+10 -1
View File
@@ -9,8 +9,10 @@ import type {
VegaConnector,
} from './connectors/vega-connector';
import { VegaWalletContext } from './context';
import { WALLET_KEY } from './storage';
import { WALLET_KEY, WALLET_RISK_ACCEPTED_KEY } from './storage';
import { ViewConnector } from './connectors';
import { useEnvironment, Networks } from '@vegaprotocol/environment';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
interface VegaWalletProviderProps {
children: ReactNode;
@@ -108,6 +110,11 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
return connector.current.sendTx(pubkey, transaction);
}, []);
const { VEGA_ENV } = useEnvironment();
const [riskAcceptedValue] = useLocalStorage(WALLET_RISK_ACCEPTED_KEY);
const acknowledgeNeeded =
VEGA_ENV === Networks.MAINNET && riskAcceptedValue !== 'true';
const contextValue = useMemo<VegaWalletContextShape>(() => {
return {
isReadOnly,
@@ -118,6 +125,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
disconnect,
sendTx,
fetchPubKeys,
acknowledgeNeeded,
};
}, [
isReadOnly,
@@ -128,6 +136,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
disconnect,
sendTx,
fetchPubKeys,
acknowledgeNeeded,
]);
return (
+9
View File
@@ -8,6 +8,7 @@ interface ConnectorConfig {
export const WALLET_CONFIG = 'vega_wallet_config';
export const WALLET_KEY = 'vega_wallet_key';
export const WALLET_RISK_ACCEPTED_KEY = 'vega_wallet_risk_accepted';
export function setConfig(cfg: ConnectorConfig) {
LocalStorage.setItem(WALLET_CONFIG, JSON.stringify(cfg));
@@ -29,3 +30,11 @@ export function getConfig(): ConnectorConfig | null {
export function clearConfig() {
LocalStorage.removeItem(WALLET_CONFIG);
}
export function getAcknowledged() {
return LocalStorage.getItem(WALLET_RISK_ACCEPTED_KEY) === 'true';
}
export function setAcknowledged() {
return LocalStorage.setItem(WALLET_RISK_ACCEPTED_KEY, 'true');
}
+4 -4
View File
@@ -7,13 +7,13 @@ export function useEagerConnect(Connectors: {
[connector: string]: VegaConnector;
}) {
const [connecting, setConnecting] = useState(true);
const { connect } = useVegaWallet();
const { connect, acknowledgeNeeded } = useVegaWallet();
useEffect(() => {
const attemptConnect = async () => {
const cfg = getConfig();
// No stored config, or config was malformed
if (!cfg || !cfg.connector) {
// No stored config, or config was malformed or no risk accepted
if (!cfg || !cfg.connector || acknowledgeNeeded) {
setConnecting(false);
return;
}
@@ -42,7 +42,7 @@ export function useEagerConnect(Connectors: {
if (typeof window !== 'undefined') {
attemptConnect();
}
}, [connect, Connectors]);
}, [connect, Connectors, acknowledgeNeeded]);
return connecting;
}
+9 -5
View File
@@ -13,10 +13,11 @@ export enum Status {
ListingKeys = 'ListingKeys',
Connected = 'Connected',
Error = 'Error',
AcknowledgeNeeded = 'AcknowledgeNeeded',
}
export const useJsonRpcConnect = (onConnect: () => void) => {
const { connect } = useVegaWallet();
const { connect, acknowledgeNeeded } = useVegaWallet();
const [status, setStatus] = useState(Status.Idle);
const [error, setError] = useState<WalletClientError | null>(null);
@@ -52,9 +53,12 @@ export const useJsonRpcConnect = (onConnect: () => void) => {
// Call connect in the wallet provider. The connector will be stored for
// future actions such as sending transactions
await connect(connector);
setStatus(Status.Connected);
onConnect();
if (acknowledgeNeeded) {
setStatus(Status.AcknowledgeNeeded);
} else {
setStatus(Status.Connected);
onConnect();
}
} catch (err) {
if (err instanceof WalletClientError) {
setError(err);
@@ -62,7 +66,7 @@ export const useJsonRpcConnect = (onConnect: () => void) => {
setStatus(Status.Error);
}
},
[onConnect, connect]
[onConnect, connect, acknowledgeNeeded]
);
return {