Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbe7dcc29f | ||
|
|
34b0d4f464 | ||
|
|
fc60cfcea8 | ||
|
|
3dffa0e882 | ||
|
|
dd924d98eb | ||
|
|
c9adde0f06 | ||
|
|
0672b20b37 | ||
|
|
f1426fcd4f | ||
|
|
10b2b36946 | ||
|
|
195424e1b8 | ||
|
|
4ef8d1ccbf | ||
|
|
570472b739 | ||
|
|
28f7bd36e7 |
@@ -22,8 +22,6 @@ NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
@@ -31,3 +29,4 @@ LC_ALL="en_US.UTF-8"
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
@@ -19,6 +19,9 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
@@ -26,4 +29,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
@@ -14,8 +14,12 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
@@ -14,9 +14,12 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -13,9 +13,12 @@ NX_DELEGATIONS_PAGINATION=50
|
||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -10,8 +10,12 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -15,8 +15,12 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -12,8 +12,12 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import { ENV } from '@vegaprotocol/environment';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
InjectedConnector,
|
||||
SnapConnector,
|
||||
DEFAULT_SNAP_ID,
|
||||
} from '@vegaprotocol/wallet';
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
export const injected = new InjectedConnector();
|
||||
export const jsonRpc = new JsonRpcConnector();
|
||||
export const injected = new InjectedConnector();
|
||||
export const view = new ViewConnector(urlParams.get('address'));
|
||||
|
||||
export const snap = new SnapConnector(
|
||||
ENV.VEGA_URL ? new URL(ENV.VEGA_URL).origin : undefined,
|
||||
DEFAULT_SNAP_ID
|
||||
);
|
||||
|
||||
export const Connectors = {
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap,
|
||||
};
|
||||
|
||||
@@ -36,4 +36,4 @@ CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
# Cosmic elevator flags (MUST be doubled with CYPRESS_ prefix)
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
// 0003-WTXN-001
|
||||
cy.getByTestId('connect-vega-wallet'); // Not connected
|
||||
cy.getByTestId(placeOrderBtn).should('exist');
|
||||
cy.getByTestId('get-started-button').should('exist');
|
||||
cy.getByTestId('order-connect-wallet').should('exist');
|
||||
});
|
||||
|
||||
it('must be able to select order direction - long/short', function () {
|
||||
@@ -44,7 +44,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
mockConnectWallet();
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
cy.getByTestId(orderPriceField).clear().type('101');
|
||||
cy.getByTestId('get-started-button').click();
|
||||
cy.getByTestId('order-connect-wallet').click();
|
||||
cy.getByTestId('dialog-content').should('be.visible');
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
|
||||
+2
-2
@@ -12,14 +12,14 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
@@ -12,6 +12,8 @@ NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
@@ -21,6 +23,7 @@ NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
@@ -13,12 +13,15 @@ NX_VEGA_DOCS_URL=#
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
@@ -13,6 +13,8 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet-mainnet
|
||||
# TAG name of the current app version - TODO: bump to the latest upon release
|
||||
NX_APP_VERSION=v0.20.21-core-0.71.6
|
||||
|
||||
@@ -21,6 +23,7 @@ NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
@@ -13,6 +13,8 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet-mainnet
|
||||
# TAG name of the current app version - TODO: bump to the latest upon release
|
||||
NX_APP_VERSION=v0.20.19-core-0.71.6
|
||||
|
||||
@@ -21,6 +23,7 @@ NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
@@ -13,9 +13,12 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
|
||||
@@ -14,12 +14,15 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
@@ -15,11 +15,15 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=false
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
|
||||
|
||||
@@ -1,10 +1,2 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export const THROTTLE_UPDATE_TIME = 500;
|
||||
export const ONBOARDING_VIEWED_KEY = 'vega_onboarding_viewed';
|
||||
export const MAINNET_WELCOME_HEADER = t(
|
||||
'Trade cash settled futures on the fully decentralised Vega network.'
|
||||
);
|
||||
export const TESTNET_WELCOME_HEADER = t(
|
||||
'Try out trading cash settled futures on the fully decentralised Vega network (Testnet).'
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
import type {
|
||||
SuccessorProposalListFieldsFragment,
|
||||
NewMarketSuccessorFieldsFragment,
|
||||
@@ -52,7 +52,7 @@ export const MarketSuccessorProposalBanner = ({
|
||||
TOKEN_PROPOSAL.replace(':id', item.id || '')
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Fragment key={i}>
|
||||
<ExternalLink href={externalLink} key={i}>
|
||||
{
|
||||
(item.terms?.change as NewMarketSuccessorFieldsFragment)
|
||||
@@ -60,7 +60,7 @@ export const MarketSuccessorProposalBanner = ({
|
||||
}
|
||||
</ExternalLink>
|
||||
{i < successors.length - 1 && ', '}
|
||||
</>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Header, HeaderTitle } from '../header';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { MarketSelector } from '../../components/market-selector/market-selector';
|
||||
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
|
||||
import { useMarket } from '@vegaprotocol/markets';
|
||||
import { useMarket, useMarketList } from '@vegaprotocol/markets';
|
||||
import { useState } from 'react';
|
||||
|
||||
export const MarketHeader = () => {
|
||||
@@ -11,6 +11,10 @@ export const MarketHeader = () => {
|
||||
const { data } = useMarket(marketId);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Ensure that markets are kept cached so opening the list
|
||||
// shows all markets instantly
|
||||
useMarketList();
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
return (
|
||||
|
||||
@@ -132,6 +132,7 @@ describe('MarketSelector', () => {
|
||||
data: markets,
|
||||
loading: false,
|
||||
error: undefined,
|
||||
reload: jest.fn(),
|
||||
});
|
||||
|
||||
it('Button "All" should be selected by default', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
|
||||
import { type MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
|
||||
import {
|
||||
TradingInput,
|
||||
TinyScroll,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useCallback, useState, useMemo, useRef } from 'react';
|
||||
import { useCallback, useState, useMemo, useRef, useEffect } from 'react';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import { useMarketSelectorList } from './use-market-selector-list';
|
||||
import type { ProductType } from './product-selector';
|
||||
@@ -44,7 +44,12 @@ export const MarketSelector = ({
|
||||
assets: [],
|
||||
});
|
||||
const allProducts = filter.product === Product.All;
|
||||
const { markets, data, loading, error } = useMarketSelectorList(filter);
|
||||
const { markets, data, loading, error, reload } =
|
||||
useMarketSelectorList(filter);
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
}, [reload]);
|
||||
|
||||
return (
|
||||
<div data-testid="market-selector">
|
||||
|
||||
@@ -15,6 +15,7 @@ export const Sort = {
|
||||
Gained: 'Gained',
|
||||
Lost: 'Lost',
|
||||
New: 'New',
|
||||
TopTraded: 'TopTraded',
|
||||
} as const;
|
||||
|
||||
export type SortType = keyof typeof Sort;
|
||||
@@ -26,6 +27,7 @@ export const SortTypeMapping: {
|
||||
[Sort.Gained]: 'Top gaining',
|
||||
[Sort.Lost]: 'Top losing',
|
||||
[Sort.New]: 'New markets',
|
||||
[Sort.TopTraded]: 'Top traded',
|
||||
};
|
||||
|
||||
const SortIconMapping: {
|
||||
@@ -35,6 +37,7 @@ const SortIconMapping: {
|
||||
[Sort.Gained]: VegaIconNames.TREND_UP,
|
||||
[Sort.Lost]: VegaIconNames.TREND_DOWN,
|
||||
[Sort.New]: VegaIconNames.STAR,
|
||||
[Sort.TopTraded]: VegaIconNames.ARROW_UP,
|
||||
};
|
||||
|
||||
export const SortDropdown = ({
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
|
||||
import {
|
||||
calcCandleVolume,
|
||||
calcTradedFactor,
|
||||
useMarketList,
|
||||
} from '@vegaprotocol/markets';
|
||||
import { priceChangePercentage } from '@vegaprotocol/utils';
|
||||
import type { Filter } from '../../components/market-selector/market-selector';
|
||||
import { Sort } from './sort-dropdown';
|
||||
@@ -20,7 +24,7 @@ export const useMarketSelectorList = ({
|
||||
sort,
|
||||
searchTerm,
|
||||
}: Filter) => {
|
||||
const { data, loading, error } = useMarketList();
|
||||
const { data, loading, error, reload } = useMarketList();
|
||||
|
||||
const markets = useMemo(() => {
|
||||
if (!data?.length) return [];
|
||||
@@ -94,10 +98,14 @@ export const useMarketSelectorList = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (sort === Sort.TopTraded) {
|
||||
return orderBy(markets, [(m) => calcTradedFactor(m)], ['desc']);
|
||||
}
|
||||
|
||||
return markets;
|
||||
}, [data, product, searchTerm, assets, sort]);
|
||||
|
||||
return { markets, data, loading, error };
|
||||
return { markets, data, loading, error, reload };
|
||||
};
|
||||
|
||||
export const isMarketActive = (state: MarketState) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketSelector } from '../market-selector';
|
||||
import { useMarket } from '@vegaprotocol/markets';
|
||||
import { useMarket, useMarketList } from '@vegaprotocol/markets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
||||
@@ -14,6 +14,10 @@ export const NavHeader = () => {
|
||||
const { data } = useMarket(marketId);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Ensure that markets are kept cached so opening the list
|
||||
// shows all markets instantly
|
||||
useMarketList();
|
||||
|
||||
if (!marketId) return null;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,16 +1,36 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { GetStarted } from './get-started';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
let mockStep = 1;
|
||||
jest.mock('./use-get-onboarding-step', () => ({
|
||||
...jest.requireActual('./use-get-onboarding-step'),
|
||||
useGetOnboardingStep: jest.fn(() => mockStep),
|
||||
}));
|
||||
|
||||
describe('GetStarted', () => {
|
||||
const renderComponent = (context: Partial<VegaWalletContextShape> = {}) => {
|
||||
return render(
|
||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
const checkTicks = (elements: Element[]) => {
|
||||
elements.forEach((item, i) => {
|
||||
if (i + 1 < mockStep) {
|
||||
expect(item.querySelector('[data-testid="icon-tick"]')).toBeTruthy();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders full get started content if not connected and no browser wallet detected', () => {
|
||||
renderComponent();
|
||||
@@ -20,12 +40,71 @@ describe('GetStarted', () => {
|
||||
it('renders connect prompt if no pubKey but wallet installed', () => {
|
||||
globalThis.window.vega = {} as Vega;
|
||||
renderComponent();
|
||||
expect(screen.getByTestId('order-connect-wallet')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('get-started-banner')).toBeInTheDocument();
|
||||
globalThis.window.vega = undefined as unknown as Vega;
|
||||
});
|
||||
|
||||
it('renders nothing if connected', () => {
|
||||
mockStep = 0;
|
||||
const { container } = renderComponent({ pubKey: 'my-pubkey' });
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('steps should be ticked', () => {
|
||||
const navigatorGetter: jest.SpyInstance = jest.spyOn(
|
||||
window.navigator,
|
||||
'userAgent',
|
||||
'get'
|
||||
);
|
||||
navigatorGetter.mockReturnValue('Chrome');
|
||||
mockStep = 1;
|
||||
const { rerender, container } = renderComponent();
|
||||
expect(screen.queryByTestId('icon-tick')).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('get-wallet-button')).toBeInTheDocument();
|
||||
|
||||
mockStep = 2;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Connect' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 3;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Deposit' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 4;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Dismiss' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 5;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey: 'my-pubkey' } as VegaWalletContextShape}
|
||||
>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +1,96 @@
|
||||
import classNames from 'classnames';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ExternalLink, Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
ExternalLink,
|
||||
Intent,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
GetWalletButton,
|
||||
useVegaWallet,
|
||||
useVegaWalletDialogStore,
|
||||
isBrowserWalletInstalled,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
OnboardingStep,
|
||||
useGetOnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useSidebar, ViewType } from '../sidebar';
|
||||
import * as constants from '../constants';
|
||||
|
||||
interface Props {
|
||||
lead?: string;
|
||||
}
|
||||
|
||||
export const GetStarted = ({ lead }: Props) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
||||
|
||||
const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
|
||||
const navigate = useNavigate();
|
||||
const [, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
const link = marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
let buttonText = t('Get started');
|
||||
let onClickHandle = () => {
|
||||
openVegaWalletDialog();
|
||||
};
|
||||
if (step === OnboardingStep.ONBOARDING_WALLET_STEP) {
|
||||
return <GetWalletButton className="justify-between" />;
|
||||
} else if (step === OnboardingStep.ONBOARDING_CONNECT_STEP) {
|
||||
buttonText = t('Connect');
|
||||
} else if (step === OnboardingStep.ONBOARDING_DEPOSIT_STEP) {
|
||||
buttonText = t('Deposit');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Deposit });
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
} else if (step === OnboardingStep.ONBOARDING_ORDER_STEP) {
|
||||
buttonText = t('Dismiss');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Order });
|
||||
setOnboardingViewed('true');
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
onClick={onClickHandle}
|
||||
size="small"
|
||||
data-testid="get-started-button"
|
||||
intent={Intent.Info}
|
||||
>
|
||||
{buttonText}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
|
||||
export const GetStarted = ({ lead }: Props) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
||||
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
||||
const currentStep = useGetOnboardingStep();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
|
||||
const onButtonClick = () => {
|
||||
openVegaWalletDialog();
|
||||
setOnboardingViewed('true');
|
||||
};
|
||||
const getStartedNeeded =
|
||||
onBoardingViewed !== 'true' &&
|
||||
currentStep &&
|
||||
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP;
|
||||
|
||||
const wrapperClasses = classNames(
|
||||
'flex flex-col py-4 px-6 gap-4 rounded',
|
||||
@@ -39,27 +99,48 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{ 'mt-8': !lead }
|
||||
);
|
||||
|
||||
if (!pubKey && !isBrowserWalletInstalled()) {
|
||||
if (getStartedNeeded) {
|
||||
return (
|
||||
<div className={wrapperClasses} data-testid="get-started-banner">
|
||||
{lead && <h2>{lead}</h2>}
|
||||
<h3 className="text-lg">{t('Get started')}</h3>
|
||||
<div>
|
||||
<ul className="list-decimal list-inside">
|
||||
<li>{t('Get a Vega wallet')}</li>
|
||||
<li>{t('Connect')}</li>
|
||||
<li>{t('Deposit funds')}</li>
|
||||
<li>{t('Open a position')}</li>
|
||||
<ul className="list-inside -ml-5" role="list">
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_WALLET_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">1. {t('Get a Vega wallet')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{(currentStep > OnboardingStep.ONBOARDING_CONNECT_STEP ||
|
||||
pubKey) && <VegaIcon name={VegaIconNames.TICK} size={20} />}
|
||||
</div>
|
||||
<div className="ml-1">2. {t('Connect')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_DEPOSIT_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">3. {t('Deposit funds')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_ORDER_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">4. {t('Open a position')}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<TradingButton
|
||||
intent={Intent.Info}
|
||||
onClick={onButtonClick}
|
||||
data-testid="get-started-button"
|
||||
>
|
||||
{t('Get started')}
|
||||
</TradingButton>
|
||||
<GetStartedButton step={currentStep} />
|
||||
</div>
|
||||
{VEGA_ENV === Networks.MAINNET && (
|
||||
<p className="text-sm">
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
OnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { ordersWithMarketProvider } from '@vegaprotocol/orders';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
|
||||
let mockData: object[] | null = [{ id: 'item-id' }];
|
||||
jest.mock('@vegaprotocol/data-provider', () => ({
|
||||
...jest.requireActual('@vegaprotocol/data-provider'),
|
||||
useDataProvider: jest.fn(() => ({ data: mockData })),
|
||||
}));
|
||||
|
||||
let mockContext: Partial<VegaWalletContextShape> = { pubKey: 'test-pubkey' };
|
||||
|
||||
describe('useGetOnboardingStep', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockData = [{ id: 'item-id' }];
|
||||
mockContext = { pubKey: 'test-pubkey' };
|
||||
globalThis.window.vega = {} as Vega;
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<VegaWalletContext.Provider
|
||||
value={mockContext as unknown as VegaWalletContextShape}
|
||||
>
|
||||
{children}
|
||||
</VegaWalletContext.Provider>
|
||||
);
|
||||
|
||||
it('should return properly ONBOARDING_UNKNOWN_STEP', () => {
|
||||
mockData = null;
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_UNKNOWN_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_WALLET_STEP', () => {
|
||||
// @ts-ignore test only purpose
|
||||
globalThis.window.vega = undefined;
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_WALLET_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_CONNECT_STEP', () => {
|
||||
mockContext = { pubKey: null };
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_CONNECT_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_DEPOSIT_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation((args) => {
|
||||
if (
|
||||
args.dataProvider === depositsProvider ||
|
||||
args.dataProvider === aggregatedAccountsDataProvider
|
||||
) {
|
||||
return { data: [] };
|
||||
}
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(
|
||||
OnboardingStep.ONBOARDING_DEPOSIT_STEP
|
||||
);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_ORDER_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation((args) => {
|
||||
if (
|
||||
args.dataProvider === ordersWithMarketProvider ||
|
||||
args.dataProvider === positionsDataProvider
|
||||
) {
|
||||
return { data: [] };
|
||||
}
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(OnboardingStep.ONBOARDING_ORDER_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_COMPLETE_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation(() => {
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(
|
||||
OnboardingStep.ONBOARDING_COMPLETE_STEP
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
import { isBrowserWalletInstalled, useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { ordersWithMarketProvider } from '@vegaprotocol/orders';
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export enum OnboardingStep {
|
||||
ONBOARDING_UNKNOWN_STEP,
|
||||
ONBOARDING_WALLET_STEP,
|
||||
ONBOARDING_CONNECT_STEP,
|
||||
ONBOARDING_DEPOSIT_STEP,
|
||||
ONBOARDING_ORDER_STEP,
|
||||
ONBOARDING_COMPLETE_STEP,
|
||||
}
|
||||
|
||||
export const useGetOnboardingStep = () => {
|
||||
const connecting = useGlobalStore((store) => store.eagerConnecting);
|
||||
const { pubKey = '', pubKeys } = useVegaWallet();
|
||||
const { data: depositsData } = useDataProvider({
|
||||
dataProvider: depositsProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const { data: collateralData } = useDataProvider({
|
||||
dataProvider: aggregatedAccountsDataProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const collaterals = Boolean(collateralData?.length);
|
||||
const deposits =
|
||||
depositsData?.some(
|
||||
(item) => item.status === Types.DepositStatus.STATUS_FINALIZED
|
||||
) || false;
|
||||
const { data: ordersData } = useDataProvider({
|
||||
dataProvider: ordersWithMarketProvider,
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
pagination: {
|
||||
first: 1,
|
||||
},
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
const orders = Boolean(ordersData?.length);
|
||||
|
||||
const partyIds = pubKeys?.map((item) => item.publicKey) || [];
|
||||
const { data: positionsData } = useDataProvider({
|
||||
dataProvider: positionsDataProvider,
|
||||
variables: {
|
||||
partyIds,
|
||||
},
|
||||
skip: !partyIds?.length,
|
||||
});
|
||||
const positions = Boolean(positionsData?.length);
|
||||
|
||||
const isLoading = Boolean(
|
||||
(connecting || pubKey) &&
|
||||
(depositsData === null ||
|
||||
ordersData === null ||
|
||||
collateralData === null ||
|
||||
positionsData === null)
|
||||
);
|
||||
if (isLoading) {
|
||||
return OnboardingStep.ONBOARDING_UNKNOWN_STEP;
|
||||
}
|
||||
if (!isBrowserWalletInstalled()) {
|
||||
return OnboardingStep.ONBOARDING_WALLET_STEP;
|
||||
}
|
||||
if (!pubKey) {
|
||||
return OnboardingStep.ONBOARDING_CONNECT_STEP;
|
||||
}
|
||||
if (!deposits && !collaterals) {
|
||||
return OnboardingStep.ONBOARDING_DEPOSIT_STEP;
|
||||
}
|
||||
if (!orders && !positions) {
|
||||
return OnboardingStep.ONBOARDING_ORDER_STEP;
|
||||
}
|
||||
return OnboardingStep.ONBOARDING_COMPLETE_STEP;
|
||||
};
|
||||
@@ -3,21 +3,19 @@ import { GetStarted } from './get-started';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import * as constants from '../constants';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export const WelcomeDialogContent = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const navigate = useNavigate();
|
||||
const browseMarkets = () => {
|
||||
const link = Links[Routes.MARKETS]();
|
||||
navigate(link);
|
||||
setOnboardingViewed('true');
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
const lead =
|
||||
VEGA_ENV === Networks.MAINNET
|
||||
@@ -57,7 +55,7 @@ export const WelcomeDialogContent = () => {
|
||||
{t('Browse the markets')}
|
||||
</TradingButton>
|
||||
</div>
|
||||
<div className="sm:w-1/2">
|
||||
<div className="sm:w-1/2 flex grow">
|
||||
<GetStarted lead={lead} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,31 +2,38 @@ import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { isBrowserWalletInstalled } from '@vegaprotocol/wallet';
|
||||
import * as constants from '../constants';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { WelcomeDialogContent } from './welcome-dialog-content';
|
||||
import { getConfig } from '@vegaprotocol/wallet';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
OnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import * as constants from '../constants';
|
||||
|
||||
export const WelcomeDialog = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [onBoardingViewed, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const dismissed = useGlobalStore((store) => store.onBoardingDismissed);
|
||||
const currentStep = useGetOnboardingStep();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const isOnboardingDialogNeeded =
|
||||
onBoardingViewed !== 'true' && !isBrowserWalletInstalled() && !getConfig();
|
||||
onBoardingViewed !== 'true' &&
|
||||
currentStep &&
|
||||
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP &&
|
||||
!dismissed;
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
const onClose = () => {
|
||||
setOnboardingViewed('true');
|
||||
const link = marketId
|
||||
? Links[Routes.MARKET](marketId)
|
||||
: Links[Routes.HOME]();
|
||||
navigate(link);
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
const title = (
|
||||
<span className="font-alpha calt" data-testid="welcome-title">
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { ENV } from '@vegaprotocol/environment';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
InjectedConnector,
|
||||
SnapConnector,
|
||||
DEFAULT_SNAP_ID,
|
||||
} from '@vegaprotocol/wallet';
|
||||
|
||||
export const jsonRpc = new JsonRpcConnector();
|
||||
@@ -15,8 +18,14 @@ if (typeof window !== 'undefined') {
|
||||
view = new ViewConnector();
|
||||
}
|
||||
|
||||
export const snap = new SnapConnector(
|
||||
ENV.VEGA_URL ? new URL(ENV.VEGA_URL).origin : undefined,
|
||||
DEFAULT_SNAP_ID
|
||||
);
|
||||
|
||||
export const Connectors = {
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
import './styles.css';
|
||||
import { usePageTitleStore } from '../stores';
|
||||
import { useGlobalStore, usePageTitleStore } from '../stores';
|
||||
import DialogsContainer from './dialogs-container';
|
||||
import ToastsManager from './toasts-manager';
|
||||
import {
|
||||
@@ -170,7 +170,8 @@ const PartyData = () => {
|
||||
|
||||
const MaybeConnectEagerly = () => {
|
||||
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
||||
useVegaEagerConnect(Connectors);
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const eagerConnecting = useVegaEagerConnect(Connectors);
|
||||
const [isTelemetryApproved] = useTelemetryApproval();
|
||||
useEthereumEagerConnect(
|
||||
isTelemetryApproved ? { dsn: SENTRY_DSN, env: VEGA_ENV } : {}
|
||||
@@ -182,5 +183,8 @@ const MaybeConnectEagerly = () => {
|
||||
if (query && !pubKey) {
|
||||
connect(Connectors['view']);
|
||||
}
|
||||
useEffect(() => {
|
||||
update({ eagerConnecting });
|
||||
}, [update, eagerConnecting]);
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,8 @@ import produce from 'immer';
|
||||
|
||||
interface GlobalStore {
|
||||
marketId: string | null;
|
||||
onBoardingDismissed: boolean;
|
||||
eagerConnecting: boolean;
|
||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||
}
|
||||
|
||||
@@ -14,6 +16,8 @@ interface PageTitleStore {
|
||||
|
||||
export const useGlobalStore = create<GlobalStore>()((set) => ({
|
||||
marketId: LocalStorage.getItem('marketId') || null,
|
||||
onBoardingDismissed: false,
|
||||
eagerConnecting: false,
|
||||
update: (newState) => {
|
||||
set(
|
||||
produce((state: GlobalStore) => {
|
||||
|
||||
@@ -408,6 +408,12 @@ function compileFeatureFlags(): FeatureFlags {
|
||||
process.env['NX_PRODUCT_PERPETUALS']
|
||||
) as string
|
||||
),
|
||||
METAMASK_SNAPS: TRUTHY.includes(
|
||||
windowOrDefault(
|
||||
'NX_METAMASK_SNAPS',
|
||||
process.env['NX_METAMASK_SNAPS']
|
||||
) as string
|
||||
),
|
||||
};
|
||||
const EXPLORER_FLAGS = {
|
||||
EXPLORER_ASSETS: TRUTHY.includes(
|
||||
|
||||
@@ -18,7 +18,11 @@ export type Environment = z.infer<typeof envSchema>;
|
||||
export type FeatureFlags = z.infer<typeof featureFlagsSchema>;
|
||||
export type CosmicElevatorFlags = Pick<
|
||||
FeatureFlags,
|
||||
'ICEBERG_ORDERS' | 'STOP_ORDERS' | 'SUCCESSOR_MARKETS' | 'PRODUCT_PERPETUALS'
|
||||
| 'ICEBERG_ORDERS'
|
||||
| 'STOP_ORDERS'
|
||||
| 'SUCCESSOR_MARKETS'
|
||||
| 'PRODUCT_PERPETUALS'
|
||||
| 'METAMASK_SNAPS'
|
||||
>;
|
||||
export type Configuration = z.infer<typeof tomlConfigSchema>;
|
||||
export const CUSTOM_NODE_KEY = 'custom' as const;
|
||||
|
||||
@@ -77,6 +77,7 @@ const COSMIC_ELEVATOR_FLAGS = {
|
||||
STOP_ORDERS: z.optional(z.boolean()),
|
||||
ICEBERG_ORDERS: z.optional(z.boolean()),
|
||||
PRODUCT_PERPETUALS: z.optional(z.boolean()),
|
||||
METAMASK_SNAPS: z.optional(z.boolean()),
|
||||
};
|
||||
|
||||
const EXPLORER_FLAGS = {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { Market } from './markets-provider';
|
||||
import { filterAndSortMarkets, totalFeesPercentage } from './market-utils';
|
||||
import type { Market, MarketMaybeWithDataAndCandles } from './markets-provider';
|
||||
import {
|
||||
calcTradedFactor,
|
||||
filterAndSortMarkets,
|
||||
totalFeesPercentage,
|
||||
} from './market-utils';
|
||||
const { MarketState, MarketTradingMode } = Schema;
|
||||
|
||||
const MARKET_A: Partial<Market> = {
|
||||
@@ -77,3 +81,52 @@ describe('totalFees', () => {
|
||||
expect(totalFeesPercentage(i)).toEqual(o);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calcTradedFactor', () => {
|
||||
const marketA = {
|
||||
data: {
|
||||
markPrice: '10',
|
||||
},
|
||||
candles: [
|
||||
{
|
||||
volume: '1000',
|
||||
},
|
||||
],
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
settlementAsset: {
|
||||
decimals: 18,
|
||||
quantum: '1000000000000000000', // 1
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const marketB = {
|
||||
data: {
|
||||
markPrice: '10',
|
||||
},
|
||||
candles: [
|
||||
{
|
||||
volume: '1000',
|
||||
},
|
||||
],
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
settlementAsset: {
|
||||
decimals: 18,
|
||||
quantum: '1', // 0.0000000000000000001
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
it('a is "traded" more than b', () => {
|
||||
const fa = calcTradedFactor(marketA as MarketMaybeWithDataAndCandles);
|
||||
const fb = calcTradedFactor(marketB as MarketMaybeWithDataAndCandles);
|
||||
// it should be true because market a's asset is "more valuable" than b's
|
||||
expect(fa > fb).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import { formatNumberPercentage, toBigNum } from '@vegaprotocol/utils';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import type { Market, Candle, MarketMaybeWithData } from '../';
|
||||
import type {
|
||||
Market,
|
||||
Candle,
|
||||
MarketMaybeWithData,
|
||||
MarketMaybeWithDataAndCandles,
|
||||
} from '../';
|
||||
const { MarketState, MarketTradingMode } = Schema;
|
||||
|
||||
export const totalFees = (fees: Market['fees']['factors']) => {
|
||||
@@ -86,3 +91,18 @@ export const calcCandleHigh = (candles: Candle[]): string | undefined => {
|
||||
export const calcCandleVolume = (candles: Candle[]): string | undefined =>
|
||||
candles &&
|
||||
candles.reduce((acc, c) => new BigNumber(acc).plus(c.volume).toString(), '0');
|
||||
|
||||
export const calcTradedFactor = (m: MarketMaybeWithDataAndCandles) => {
|
||||
const volume = Number(calcCandleVolume(m.candles || []) || 0);
|
||||
const price = m.data?.markPrice ? Number(m.data.markPrice) : 0;
|
||||
const quantum = Number(
|
||||
m.tradableInstrument.instrument.product.settlementAsset.quantum
|
||||
);
|
||||
const decimals = Number(
|
||||
m.tradableInstrument.instrument.product.settlementAsset.decimals
|
||||
);
|
||||
const fp = toBigNum(price, decimals);
|
||||
const fq = toBigNum(quantum, decimals);
|
||||
const factor = fq.multipliedBy(fp).multipliedBy(volume);
|
||||
return factor.toNumber();
|
||||
};
|
||||
|
||||
@@ -181,7 +181,7 @@ const getSubscriptionVariables = (
|
||||
): PositionsSubscriptionSubscriptionVariables[] =>
|
||||
([] as string[]).concat(variables.partyIds).map((partyId) => ({ partyId }));
|
||||
|
||||
const positionsDataProvider = makeDataProvider<
|
||||
export const positionsDataProvider = makeDataProvider<
|
||||
PositionsQuery,
|
||||
PositionFieldsFragment[],
|
||||
PositionsSubscriptionSubscription,
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
export const IconMetaMask = ({ size = 16 }: { size: number }) => (
|
||||
<svg viewBox="0 0 47 47" fill="none" height={size}>
|
||||
<g>
|
||||
<path
|
||||
d="m40.632 6.969-14.136 10.62 2.628-6.259L40.632 6.97Z"
|
||||
fill="#E17726"
|
||||
stroke="#E17726"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m8.024 6.969 14.01 10.72-2.502-6.359L8.024 6.97ZM35.542 31.594l-3.761 5.834 8.054 2.251 2.307-7.958-6.6-.127ZM6.528 31.721 8.82 39.68l8.04-2.251-3.747-5.834-6.586.127Z"
|
||||
fill="#E27625"
|
||||
stroke="#E27625"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m16.428 21.738-2.237 3.427 7.97.368-.266-8.709-5.467 4.914ZM32.229 21.738l-5.552-5.012-.181 8.807 7.97-.368-2.237-3.427ZM16.861 37.428l4.824-2.365-4.152-3.285-.672 5.65ZM26.971 35.063l4.81 2.365-.657-5.65-4.153 3.285Z"
|
||||
fill="#E27625"
|
||||
stroke="#E27625"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m31.78 37.428-4.81-2.365.392 3.172-.042 1.345 4.46-2.152ZM16.861 37.428l4.475 2.152-.028-1.345.377-3.172-4.824 2.365Z"
|
||||
fill="#D5BFB2"
|
||||
stroke="#D5BFB2"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m21.42 29.682-4-1.19 2.825-1.316 1.174 2.506ZM27.236 29.682l1.175-2.506 2.838 1.317-4.013 1.19Z"
|
||||
fill="#233447"
|
||||
stroke="#233447"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m16.861 37.427.7-5.834-4.447.128 3.747 5.706ZM31.096 31.593l.685 5.834 3.761-5.706-4.446-.128ZM34.465 25.165l-7.97.368.741 4.15 1.175-2.507 2.838 1.317 3.216-3.328ZM17.42 28.493l2.825-1.317 1.175 2.506.74-4.149-7.97-.368 3.23 3.328Z"
|
||||
fill="#CC6228"
|
||||
stroke="#CC6228"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m14.19 25.165 3.343 6.613-.112-3.285-3.23-3.328ZM31.25 28.493l-.126 3.285 3.342-6.613-3.216 3.328ZM22.161 25.533l-.741 4.149.937 4.9.21-6.458-.406-2.591ZM26.495 25.533l-.391 2.577.196 6.471.937-4.9-.741-4.148Z"
|
||||
fill="#E27525"
|
||||
stroke="#E27525"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m27.237 29.682-.937 4.9.671.481 4.153-3.285.126-3.285-4.013 1.19ZM17.42 28.493l.112 3.285 4.153 3.285.671-.481-.937-4.9-3.999-1.19Z"
|
||||
fill="#F5841F"
|
||||
stroke="#F5841F"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m27.32 39.58.042-1.345-.363-.312h-5.342l-.35.312.029 1.345-4.475-2.152 1.566 1.303 3.175 2.223h5.439l3.188-2.224 1.552-1.302-4.46 2.152Z"
|
||||
fill="#C0AC9D"
|
||||
stroke="#C0AC9D"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m26.97 35.063-.67-.482h-3.944l-.67.482-.378 3.172.35-.312h5.34l.364.312-.391-3.172Z"
|
||||
fill="#161616"
|
||||
stroke="#161616"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m41.234 18.283 1.188-5.863-1.79-5.451-13.66 10.266 5.257 4.503 7.425 2.195 1.636-1.94-.713-.524 1.132-1.048-.867-.68 1.133-.878-.741-.58ZM6.234 12.42l1.203 5.863-.77.58 1.147.878-.867.68L8.08 21.47l-.713.524 1.636 1.94 7.425-2.195 5.257-4.503L8.025 6.97l-1.79 5.452Z"
|
||||
fill="#763E1A"
|
||||
stroke="#763E1A"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m39.654 23.933-7.425-2.195 2.237 3.427-3.342 6.613 4.419-.057h6.6l-2.49-7.788ZM16.428 21.738l-7.425 2.195-2.475 7.788h6.586l4.418.056-3.342-6.612 2.238-3.427ZM26.495 25.533l.476-8.298 2.153-5.905h-9.592l2.153 5.905.476 8.298.181 2.605.014 6.443H26.3l.014-6.443.182-2.605Z"
|
||||
fill="#F5841F"
|
||||
stroke="#F5841F"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
@@ -1,8 +1,8 @@
|
||||
import { IconArrowDown } from './svg-icons/icon-arrow-down';
|
||||
import { IconArrowLeft } from './svg-icons/icon-arrow-left';
|
||||
import { IconArrowUp } from './svg-icons/icon-arrow-up';
|
||||
import { IconArrowRight } from './svg-icons/icon-arrow-right';
|
||||
import { IconArrowTopRight } from './svg-icons/icon-arrow-top-right';
|
||||
import { IconArrowUp } from './svg-icons/icon-arrow-up';
|
||||
import { IconBreakdown } from './svg-icons/icon-breakdown';
|
||||
import { IconBullet } from './svg-icons/icon-bullet';
|
||||
import { IconChevronDown } from './svg-icons/icon-chevron-down';
|
||||
@@ -20,28 +20,29 @@ import { IconGlobe } from './svg-icons/icon-globe';
|
||||
import { IconInfo } from './svg-icons/icon-info';
|
||||
import { IconKebab } from './svg-icons/icon-kebab';
|
||||
import { IconLinkedIn } from './svg-icons/icon-linkedin';
|
||||
import { IconMetaMask } from './svg-icons/icon-metamask';
|
||||
import { IconMinus } from './svg-icons/icon-minus';
|
||||
import { IconMoon } from './svg-icons/icon-moon';
|
||||
import { IconOpenExternal } from './svg-icons/icon-open-external';
|
||||
import { IconQuestionMark } from './svg-icons/icon-question-mark';
|
||||
import { IconPlus } from './svg-icons/icon-plus';
|
||||
import { IconQuestionMark } from './svg-icons/icon-question-mark';
|
||||
import { IconSearch } from './svg-icons/icon-search';
|
||||
import { IconStar } from './svg-icons/icon-star';
|
||||
import { IconTick } from './svg-icons/icon-tick';
|
||||
import { IconTicket } from './svg-icons/icon-ticket';
|
||||
import { IconTransfer } from './svg-icons/icon-transfer';
|
||||
import { IconTrendUp } from './svg-icons/icon-trend-up';
|
||||
import { IconTrendDown } from './svg-icons/icon-trend-down';
|
||||
import { IconTrendUp } from './svg-icons/icon-trend-up';
|
||||
import { IconTwitter } from './svg-icons/icon-twitter';
|
||||
import { IconVote } from './svg-icons/icon-vote';
|
||||
import { IconWithdraw } from './svg-icons/icon-withdraw';
|
||||
import { IconSearch } from './svg-icons/icon-search';
|
||||
|
||||
export enum VegaIconNames {
|
||||
ARROW_DOWN = 'arrow-down',
|
||||
ARROW_LEFT = 'arrow-left',
|
||||
ARROW_UP = 'arrow-up',
|
||||
ARROW_RIGHT = 'arrow-right',
|
||||
ARROW_TOP_RIGHT = 'arrow-top-right',
|
||||
ARROW_UP = 'arrow-up',
|
||||
BREAKDOWN = 'breakdown',
|
||||
BULLET = 'bullet',
|
||||
CHEVRON_DOWN = 'chevron-down',
|
||||
@@ -59,18 +60,19 @@ export enum VegaIconNames {
|
||||
INFO = 'info',
|
||||
KEBAB = 'kebab',
|
||||
LINKEDIN = 'linkedin',
|
||||
METAMASK = 'metamask',
|
||||
MINUS = 'minus',
|
||||
MOON = 'moon',
|
||||
OPEN_EXTERNAL = 'open-external',
|
||||
QUESTION_MARK = 'question-mark',
|
||||
PLUS = 'plus',
|
||||
QUESTION_MARK = 'question-mark',
|
||||
SEARCH = 'search',
|
||||
STAR = 'star',
|
||||
TICK = 'tick',
|
||||
TICKET = 'ticket',
|
||||
TRANSFER = 'transfer',
|
||||
TREND_UP = 'trend-up',
|
||||
TREND_DOWN = 'trend-down',
|
||||
TREND_UP = 'trend-up',
|
||||
TWITTER = 'twitter',
|
||||
VOTE = 'vote',
|
||||
WITHDRAW = 'withdraw',
|
||||
@@ -82,38 +84,39 @@ export const VegaIconNameMap: Record<
|
||||
> = {
|
||||
'arrow-down': IconArrowDown,
|
||||
'arrow-left': IconArrowLeft,
|
||||
'arrow-up': IconArrowUp,
|
||||
'arrow-right': IconArrowRight,
|
||||
'arrow-top-right': IconArrowTopRight,
|
||||
breakdown: IconBreakdown,
|
||||
bullet: IconBullet,
|
||||
'arrow-up': IconArrowUp,
|
||||
'chevron-down': IconChevronDown,
|
||||
'chevron-left': IconChevronLeft,
|
||||
'chevron-up': IconChevronUp,
|
||||
'exclaimation-mark': IconExclaimationMark,
|
||||
'open-external': IconOpenExternal,
|
||||
'question-mark': IconQuestionMark,
|
||||
'trend-down': IconTrendDown,
|
||||
'trend-up': IconTrendUp,
|
||||
breakdown: IconBreakdown,
|
||||
bullet: IconBullet,
|
||||
cog: IconCog,
|
||||
copy: IconCopy,
|
||||
cross: IconCross,
|
||||
deposit: IconDeposit,
|
||||
edit: IconEdit,
|
||||
'exclaimation-mark': IconExclaimationMark,
|
||||
eye: IconEye,
|
||||
forum: IconForum,
|
||||
globe: IconGlobe,
|
||||
info: IconInfo,
|
||||
kebab: IconKebab,
|
||||
linkedin: IconLinkedIn,
|
||||
metamask: IconMetaMask,
|
||||
minus: IconMinus,
|
||||
moon: IconMoon,
|
||||
'open-external': IconOpenExternal,
|
||||
plus: IconPlus,
|
||||
'question-mark': IconQuestionMark,
|
||||
search: IconSearch,
|
||||
star: IconStar,
|
||||
tick: IconTick,
|
||||
ticket: IconTicket,
|
||||
transfer: IconTransfer,
|
||||
'trend-up': IconTrendUp,
|
||||
'trend-down': IconTrendDown,
|
||||
twitter: IconTwitter,
|
||||
vote: IconVote,
|
||||
withdraw: IconWithdraw,
|
||||
|
||||
@@ -16,13 +16,16 @@ import type { WalletClientError } from '@vegaprotocol/wallet-client';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { VegaConnector } from '../connectors';
|
||||
import {
|
||||
DEFAULT_SNAP_ID,
|
||||
InjectedConnector,
|
||||
JsonRpcConnector,
|
||||
SnapConnector,
|
||||
ViewConnector,
|
||||
requestSnap,
|
||||
} from '../connectors';
|
||||
import { JsonRpcConnectorForm } from './json-rpc-connector-form';
|
||||
import { ViewConnectorForm } from './view-connector-form';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
BrowserIcon,
|
||||
ConnectDialogContent,
|
||||
@@ -38,10 +41,11 @@ import { useVegaWallet } from '../use-vega-wallet';
|
||||
import { InjectedConnectorForm } from './injected-connector-form';
|
||||
import { isBrowserWalletInstalled } from '../utils';
|
||||
import { useIsWalletServiceRunning } from '../use-is-wallet-service-running';
|
||||
import { useIsSnapRunning } from '../use-is-snap-running';
|
||||
|
||||
export const CLOSE_DELAY = 1700;
|
||||
type Connectors = { [key: string]: VegaConnector };
|
||||
export type WalletType = 'injected' | 'jsonRpc' | 'view';
|
||||
export type WalletType = 'injected' | 'jsonRpc' | 'view' | 'snap';
|
||||
|
||||
export interface VegaConnectDialogProps {
|
||||
connectors: Connectors;
|
||||
@@ -156,7 +160,10 @@ const ConnectDialogContainer = ({
|
||||
// for rest because we need to show an authentication form
|
||||
if (connector instanceof JsonRpcConnector) {
|
||||
jsonRpcConnect(connector, appChainId);
|
||||
} else if (connector instanceof InjectedConnector) {
|
||||
} else if (
|
||||
connector instanceof InjectedConnector ||
|
||||
connector instanceof SnapConnector
|
||||
) {
|
||||
injectedConnect(connector, appChainId);
|
||||
}
|
||||
};
|
||||
@@ -166,6 +173,8 @@ const ConnectDialogContainer = ({
|
||||
appChainId
|
||||
);
|
||||
|
||||
const isSnapRunning = useIsSnapRunning(DEFAULT_SNAP_ID);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConnectDialogContent>
|
||||
@@ -185,6 +194,7 @@ const ConnectDialogContainer = ({
|
||||
setWalletUrl={setWalletUrl}
|
||||
onSelect={handleSelect}
|
||||
isDesktopWalletRunning={isDesktopWalletRunning}
|
||||
isSnapRunning={isSnapRunning}
|
||||
/>
|
||||
)}
|
||||
</ConnectDialogContent>
|
||||
@@ -198,12 +208,15 @@ const ConnectorList = ({
|
||||
walletUrl,
|
||||
setWalletUrl,
|
||||
isDesktopWalletRunning,
|
||||
isSnapRunning,
|
||||
}: {
|
||||
onSelect: (type: WalletType) => void;
|
||||
walletUrl: string;
|
||||
setWalletUrl: (value: string) => void;
|
||||
isDesktopWalletRunning: boolean | null;
|
||||
isSnapRunning: boolean | null;
|
||||
}) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const title = isBrowserWalletInstalled()
|
||||
? t('Connect Vega wallet')
|
||||
: t('Get a Vega wallet');
|
||||
@@ -234,14 +247,54 @@ const ConnectorList = ({
|
||||
onClick={() => onSelect('injected')}
|
||||
/>
|
||||
) : (
|
||||
<GetWallet />
|
||||
<GetWalletButton />
|
||||
)}
|
||||
</div>
|
||||
{FLAGS.METAMASK_SNAPS ? (
|
||||
<div>
|
||||
{isSnapRunning ? (
|
||||
<ConnectionOption
|
||||
type="snap"
|
||||
text={
|
||||
<>
|
||||
<div className="w-full h-full flex justify-center items-center gap-1 text-base">
|
||||
{t('Connect via Vega MetaMask Snap')}
|
||||
</div>
|
||||
<div className="absolute right-1 top-0 h-8 flex items-center">
|
||||
<VegaIcon name={VegaIconNames.METAMASK} size={24} />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
onClick={() => {
|
||||
onSelect('snap');
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ConnectionOption
|
||||
type="snap"
|
||||
text={
|
||||
<>
|
||||
<div className="w-full h-full flex justify-center items-center gap-1 text-base">
|
||||
{t('Install Vega MetaMask Snap')}
|
||||
</div>
|
||||
<div className="absolute right-1 top-0 h-8 flex items-center">
|
||||
<VegaIcon name={VegaIconNames.METAMASK} size={24} />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
onClick={() => {
|
||||
requestSnap(DEFAULT_SNAP_ID);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
<div>
|
||||
<ConnectionOption
|
||||
type="view"
|
||||
text={t('View as party')}
|
||||
onClick={() => onSelect('view')}
|
||||
disabled={Boolean(pubKey)}
|
||||
/>
|
||||
</div>
|
||||
<div className="last:mb-0">
|
||||
@@ -280,7 +333,10 @@ const SelectedForm = ({
|
||||
onConnect: () => void;
|
||||
riskMessage?: ReactNode;
|
||||
}) => {
|
||||
if (connector instanceof InjectedConnector) {
|
||||
if (
|
||||
connector instanceof InjectedConnector ||
|
||||
connector instanceof SnapConnector
|
||||
) {
|
||||
return (
|
||||
<InjectedConnectorForm
|
||||
status={injectedState.status}
|
||||
@@ -318,7 +374,7 @@ const SelectedForm = ({
|
||||
throw new Error('No connector selected');
|
||||
};
|
||||
|
||||
const GetWallet = () => {
|
||||
export const GetWalletButton = ({ className }: { className?: string }) => {
|
||||
const { MOZILLA_EXTENSION_URL, CHROME_EXTENSION_URL } = useEnvironment();
|
||||
const isItChrome = window.navigator.userAgent.includes('Chrome');
|
||||
const isItMozilla =
|
||||
@@ -347,10 +403,13 @@ const GetWallet = () => {
|
||||
|
||||
return !isItChrome && !isItMozilla ? (
|
||||
<div
|
||||
className={classNames([
|
||||
'bg-vega-blue-350 hover:bg-vega-blue-400 dark:bg-vega-blue-650 dark:hover:bg-vega-blue-600',
|
||||
'flex gap-2 items-center justify-center rounded h-8 px-3 relative',
|
||||
])}
|
||||
className={classNames(
|
||||
[
|
||||
'bg-vega-blue-350 hover:bg-vega-blue-400 dark:bg-vega-blue-650 dark:hover:bg-vega-blue-600',
|
||||
'flex gap-2 items-center justify-center rounded h-8 px-3 relative',
|
||||
],
|
||||
className
|
||||
)}
|
||||
data-testid="get-wallet-button"
|
||||
>
|
||||
{buttonContent}
|
||||
@@ -360,7 +419,7 @@ const GetWallet = () => {
|
||||
onClick={onClick}
|
||||
intent={Intent.Info}
|
||||
data-testid="get-wallet-button"
|
||||
className="relative"
|
||||
className={classNames('relative', className)}
|
||||
size="small"
|
||||
fill
|
||||
>
|
||||
@@ -409,6 +468,7 @@ const CustomUrlInput = ({
|
||||
isDesktopWalletRunning: boolean | null;
|
||||
onSelect: (type: WalletType) => void;
|
||||
}) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const [urlInputExpanded, setUrlInputExpanded] = useState(false);
|
||||
return urlInputExpanded ? (
|
||||
<>
|
||||
@@ -433,7 +493,7 @@ const CustomUrlInput = ({
|
||||
/>
|
||||
</TradingFormGroup>
|
||||
<ConnectionOption
|
||||
disabled={!isDesktopWalletRunning}
|
||||
disabled={!isDesktopWalletRunning || Boolean(pubKey)}
|
||||
type="jsonRpc"
|
||||
text={t('Connect the App/CLI')}
|
||||
onClick={() => onSelect('jsonRpc')}
|
||||
@@ -442,7 +502,7 @@ const CustomUrlInput = ({
|
||||
) : (
|
||||
<>
|
||||
<ConnectionOption
|
||||
disabled={!isDesktopWalletRunning}
|
||||
disabled={!isDesktopWalletRunning || Boolean(pubKey)}
|
||||
type="jsonRpc"
|
||||
text={t('Use the Desktop App/CLI')}
|
||||
onClick={() => onSelect('jsonRpc')}
|
||||
@@ -453,6 +513,7 @@ const CustomUrlInput = ({
|
||||
<button
|
||||
className="underline text-default"
|
||||
onClick={() => setUrlInputExpanded(true)}
|
||||
disabled={Boolean(pubKey)}
|
||||
>
|
||||
{t('Enter a custom wallet location')}{' '}
|
||||
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
|
||||
@@ -467,6 +528,7 @@ const CustomUrlInput = ({
|
||||
<button
|
||||
className="underline"
|
||||
onClick={() => setUrlInputExpanded(true)}
|
||||
disabled={Boolean(pubKey)}
|
||||
>
|
||||
{t('custom wallet location')}
|
||||
</button>
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { setAcknowledged } from '../storage';
|
||||
import { useVegaWallet } from '../use-vega-wallet';
|
||||
import { InjectedConnectorErrors, SnapConnectorErrors } from '../connectors';
|
||||
|
||||
export const InjectedConnectorForm = ({
|
||||
status,
|
||||
@@ -20,7 +21,6 @@ export const InjectedConnectorForm = ({
|
||||
reset,
|
||||
error,
|
||||
}: {
|
||||
// connector: JsonRpcConnector;
|
||||
appChainId: string;
|
||||
status: Status;
|
||||
error: Error | null;
|
||||
@@ -109,7 +109,7 @@ export const InjectedConnectorForm = ({
|
||||
|
||||
const Center = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<div className="flex justify-center items-center my-6">{children}</div>
|
||||
<div className="flex items-center justify-center my-6">{children}</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -131,22 +131,30 @@ const Error = ({
|
||||
);
|
||||
|
||||
if (error) {
|
||||
if (error.message === 'Invalid chain') {
|
||||
if (error.message === InjectedConnectorErrors.INVALID_CHAIN.message) {
|
||||
title = t('Wrong network');
|
||||
text = t(
|
||||
'To complete your wallet connection, set your wallet network in your app to "%s".',
|
||||
appChainId
|
||||
);
|
||||
} else if (error.message === 'window.vega not found') {
|
||||
} else if (
|
||||
error.message === InjectedConnectorErrors.VEGA_UNDEFINED.message
|
||||
) {
|
||||
title = t('No wallet detected');
|
||||
text = t('Vega browser extension not installed');
|
||||
} else if (
|
||||
error.message === SnapConnectorErrors.ETHEREUM_UNDEFINED.message ||
|
||||
error.message === SnapConnectorErrors.NODE_ADDRESS_NOT_SET.message
|
||||
) {
|
||||
title = t('Snap failed');
|
||||
text = t('Could not connect to Vega MetaMask Snap');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConnectDialogTitle>{title}</ConnectDialogTitle>
|
||||
<p className="text-center mb-2 first-letter:uppercase">{text}</p>
|
||||
<p className="mb-2 text-center first-letter:uppercase">{text}</p>
|
||||
{tryAgain}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './vega-connector';
|
||||
export * from './injected-connector';
|
||||
export * from './json-rpc-connector';
|
||||
export * from './snap-connector';
|
||||
export * from './vega-connector';
|
||||
export * from './view-connector';
|
||||
|
||||
@@ -41,6 +41,11 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export const InjectedConnectorErrors = {
|
||||
VEGA_UNDEFINED: new Error('window.vega not found'),
|
||||
INVALID_CHAIN: new Error('Invalid chain'),
|
||||
};
|
||||
|
||||
export class InjectedConnector implements VegaConnector {
|
||||
description = 'Connects using the Vega wallet browser extension';
|
||||
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
import {
|
||||
WalletError,
|
||||
type PubKey,
|
||||
type Transaction,
|
||||
type VegaConnector,
|
||||
} from './vega-connector';
|
||||
import { clearConfig, setConfig } from '../storage';
|
||||
|
||||
type RequestArguments = {
|
||||
method: string;
|
||||
params?: unknown[] | object;
|
||||
};
|
||||
type WindowEthereumProvider = {
|
||||
isMetaMask: boolean;
|
||||
request<T = unknown>(args: RequestArguments): Promise<T>;
|
||||
};
|
||||
|
||||
export const SnapConnectorErrors = {
|
||||
ETHEREUM_UNDEFINED: new Error('MetaMask extension could not be found'),
|
||||
NODE_ADDRESS_NOT_SET: new Error('nodeAddress is not set'),
|
||||
SNAP_ID_NOT_SET: new Error('snapId is not set'),
|
||||
TRANSACTION_PARSE: new Error('could not parse transaction data'),
|
||||
};
|
||||
|
||||
const ethereumRequest = <T>(args: RequestArguments): Promise<T> => {
|
||||
// can't declare `EthereumProvider` here because of the conflict with
|
||||
// type definitions of `@web3-react`
|
||||
if (
|
||||
'ethereum' in window &&
|
||||
typeof window.ethereum === 'object' &&
|
||||
window.ethereum &&
|
||||
'request' in window.ethereum &&
|
||||
'isMetaMask' in window.ethereum &&
|
||||
window.ethereum.isMetaMask &&
|
||||
typeof window.ethereum.request === 'function'
|
||||
) {
|
||||
return (window.ethereum as WindowEthereumProvider).request<T>(args);
|
||||
}
|
||||
throw SnapConnectorErrors.ETHEREUM_UNDEFINED;
|
||||
};
|
||||
|
||||
export const LOCAL_SNAP_ID = 'local:http://localhost:8080';
|
||||
export const DEFAULT_SNAP_ID = 'npm:@vegaprotocol/snap';
|
||||
|
||||
type GetSnapsResponse = Record<string, Snap>;
|
||||
|
||||
type Snap = {
|
||||
id: string;
|
||||
initialPermissions?: Record<string, unknown>;
|
||||
version: string;
|
||||
enables: boolean;
|
||||
blocked: boolean;
|
||||
};
|
||||
|
||||
type InvokeSnapRequest = {
|
||||
method: string;
|
||||
params?: object;
|
||||
};
|
||||
|
||||
type SendTransactionResponse =
|
||||
| {
|
||||
transactionHash: string;
|
||||
receivedAt: string;
|
||||
sentAt: string;
|
||||
transaction?: {
|
||||
signature?: {
|
||||
value: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
| {
|
||||
error: Error & {
|
||||
code: number;
|
||||
data: unknown;
|
||||
};
|
||||
};
|
||||
type GetChainIdResponse = {
|
||||
chainID: string;
|
||||
};
|
||||
type ListKeysResponse = { keys: PubKey[] };
|
||||
|
||||
/**
|
||||
* Requests permission for a website to communicate with the specified snaps
|
||||
* and attempts to install them if they're not already installed.
|
||||
* If the installation of any snap fails, returns the error that caused the failure.
|
||||
* More informations here: https://docs.metamask.io/snaps/reference/rpc-api/#wallet_requestsnaps
|
||||
*/
|
||||
export const requestSnap = async (
|
||||
snapId: string,
|
||||
params: Record<'version' | string, unknown> = {}
|
||||
) => {
|
||||
try {
|
||||
await ethereumRequest({
|
||||
method: 'wallet_requestSnaps',
|
||||
params: {
|
||||
[snapId]: params,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
// NOOP - rejected by user
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the list of all installed snaps.
|
||||
* More information here: https://docs.metamask.io/snaps/reference/rpc-api/#wallet_getsnaps
|
||||
*/
|
||||
export const getSnaps = async (): Promise<GetSnapsResponse> => {
|
||||
return (await ethereumRequest({
|
||||
method: 'wallet_getSnaps',
|
||||
})) as GetSnapsResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the requested snap by `snapId` and an optional `version`
|
||||
*/
|
||||
export const getSnap = async (
|
||||
snapId: string,
|
||||
version?: string
|
||||
): Promise<Snap | undefined> => {
|
||||
try {
|
||||
const snaps = await getSnaps();
|
||||
return Object.values(snaps).find(
|
||||
(snap) => snap.id === snapId && (!version || snap.version === version)
|
||||
);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const invokeSnap = async <T>(
|
||||
snapId: string,
|
||||
request: InvokeSnapRequest
|
||||
) => {
|
||||
const req = {
|
||||
method: 'wallet_invokeSnap',
|
||||
params: {
|
||||
snapId,
|
||||
request,
|
||||
},
|
||||
};
|
||||
return await ethereumRequest<T>(req);
|
||||
};
|
||||
|
||||
export class SnapConnector implements VegaConnector {
|
||||
description = "Connects using Vega Protocol's MetaMask snap";
|
||||
snapId: string | undefined = undefined;
|
||||
nodeAddress: string | undefined = undefined;
|
||||
|
||||
constructor(nodeAddress?: string, snapId = DEFAULT_SNAP_ID) {
|
||||
this.nodeAddress = nodeAddress;
|
||||
this.snapId = snapId;
|
||||
}
|
||||
|
||||
async listKeys() {
|
||||
if (!this.snapId) throw SnapConnectorErrors.SNAP_ID_NOT_SET;
|
||||
return await invokeSnap<ListKeysResponse>(this.snapId, {
|
||||
method: 'client.list_keys',
|
||||
});
|
||||
}
|
||||
|
||||
async connect() {
|
||||
const res = await this.listKeys();
|
||||
setConfig({
|
||||
connector: 'snap',
|
||||
token: null, // no token required for snap
|
||||
url: null, // no url required for snap
|
||||
});
|
||||
return res?.keys;
|
||||
}
|
||||
|
||||
async sendTx(pubKey: string, transaction: Transaction) {
|
||||
if (!this.nodeAddress) throw SnapConnectorErrors.NODE_ADDRESS_NOT_SET;
|
||||
if (!this.snapId) throw SnapConnectorErrors.SNAP_ID_NOT_SET;
|
||||
|
||||
// This step is needed to strip the transaction object from any additional
|
||||
// properties, such as `__proto__`, etc.
|
||||
let txData = null;
|
||||
try {
|
||||
txData = JSON.parse(JSON.stringify(transaction));
|
||||
} catch (err) {
|
||||
throw SnapConnectorErrors.TRANSACTION_PARSE;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
method: 'client.send_transaction',
|
||||
params: {
|
||||
sendingMode: 'TYPE_SYNC',
|
||||
transaction: txData,
|
||||
publicKey: pubKey,
|
||||
networkEndpoints: [this.nodeAddress],
|
||||
},
|
||||
};
|
||||
|
||||
const result = await invokeSnap<SendTransactionResponse>(
|
||||
this.snapId,
|
||||
payload
|
||||
);
|
||||
|
||||
if ('error' in result) {
|
||||
const { message, code, data } = result.error;
|
||||
throw new WalletError(
|
||||
message,
|
||||
code,
|
||||
typeof data === 'string' ? data : ''
|
||||
);
|
||||
}
|
||||
|
||||
if (!result?.transaction?.signature) {
|
||||
throw new Error('could not retrieve transaction siganture');
|
||||
}
|
||||
|
||||
return {
|
||||
transactionHash: result.transactionHash,
|
||||
signature: result?.transaction?.signature?.value,
|
||||
receivedAt: result.receivedAt,
|
||||
sentAt: result.sentAt,
|
||||
};
|
||||
}
|
||||
|
||||
async getChainId(): Promise<GetChainIdResponse> {
|
||||
if (!this.nodeAddress) throw SnapConnectorErrors.NODE_ADDRESS_NOT_SET;
|
||||
if (!this.snapId) throw SnapConnectorErrors.SNAP_ID_NOT_SET;
|
||||
|
||||
const response = await invokeSnap<GetChainIdResponse>(this.snapId, {
|
||||
method: 'client.get_chain_id',
|
||||
params: { networkEndpoints: [this.nodeAddress] },
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
clearConfig();
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { LocalStorage } from '@vegaprotocol/utils';
|
||||
|
||||
interface ConnectorConfig {
|
||||
token: string | null;
|
||||
connector: 'injected' | 'jsonRpc' | 'view';
|
||||
connector: 'injected' | 'jsonRpc' | 'view' | 'snap';
|
||||
url: string | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import type { SnapConnector } from './';
|
||||
import { useVegaWallet } from './';
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { VegaConnector } from './connectors/vega-connector';
|
||||
import { getConfig } from './storage';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
export function useEagerConnect(Connectors: {
|
||||
[connector: string]: VegaConnector;
|
||||
}) {
|
||||
const { VEGA_URL } = useEnvironment();
|
||||
const [connecting, setConnecting] = useState(true);
|
||||
const { connect, acknowledgeNeeded } = useVegaWallet();
|
||||
|
||||
@@ -36,6 +39,12 @@ export function useEagerConnect(Connectors: {
|
||||
// @ts-ignore only injected wallet has connectWallet method
|
||||
await injectedInstance.connectWallet();
|
||||
await connect(injectedInstance);
|
||||
} else if (cfg.connector === 'snap') {
|
||||
const snapInstance = Connectors[cfg.connector] as SnapConnector;
|
||||
if (VEGA_URL) {
|
||||
snapInstance.nodeAddress = new URL(VEGA_URL).origin;
|
||||
await connect(snapInstance);
|
||||
}
|
||||
} else {
|
||||
await connect(Connectors[cfg.connector]);
|
||||
}
|
||||
@@ -49,7 +58,7 @@ export function useEagerConnect(Connectors: {
|
||||
if (typeof window !== 'undefined') {
|
||||
attemptConnect();
|
||||
}
|
||||
}, [connect, Connectors, acknowledgeNeeded]);
|
||||
}, [connect, Connectors, acknowledgeNeeded, VEGA_URL]);
|
||||
|
||||
return connecting;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import type { InjectedConnector } from './connectors';
|
||||
import {
|
||||
InjectedConnectorErrors,
|
||||
SnapConnector,
|
||||
SnapConnectorErrors,
|
||||
} from './connectors';
|
||||
import { InjectedConnector } from './connectors';
|
||||
import { useVegaWallet } from './use-vega-wallet';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
export enum Status {
|
||||
Idle = 'Idle',
|
||||
@@ -15,24 +21,39 @@ export const useInjectedConnector = (onConnect: () => void) => {
|
||||
const { connect, acknowledgeNeeded } = useVegaWallet();
|
||||
const [status, setStatus] = useState(Status.Idle);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const { VEGA_URL } = useEnvironment();
|
||||
|
||||
const attemptConnect = useCallback(
|
||||
async (connector: InjectedConnector, appChainId: string) => {
|
||||
async (
|
||||
connector: InjectedConnector | SnapConnector,
|
||||
appChainId: string
|
||||
) => {
|
||||
try {
|
||||
if (!('vega' in window)) {
|
||||
throw new Error('window.vega not found');
|
||||
if (connector instanceof InjectedConnector && !('vega' in window)) {
|
||||
throw InjectedConnectorErrors.VEGA_UNDEFINED;
|
||||
}
|
||||
if (connector instanceof SnapConnector) {
|
||||
if (!('ethereum' in window)) {
|
||||
throw SnapConnectorErrors.ETHEREUM_UNDEFINED;
|
||||
}
|
||||
if (!VEGA_URL) {
|
||||
throw SnapConnectorErrors.NODE_ADDRESS_NOT_SET;
|
||||
}
|
||||
connector.nodeAddress = new URL(VEGA_URL).origin;
|
||||
}
|
||||
|
||||
setStatus(Status.GettingChainId);
|
||||
|
||||
const { chainID } = await connector.getChainId();
|
||||
|
||||
if (chainID !== appChainId) {
|
||||
throw new Error('Invalid chain');
|
||||
throw InjectedConnectorErrors.INVALID_CHAIN;
|
||||
}
|
||||
|
||||
setStatus(Status.Connecting);
|
||||
await connector.connectWallet(); // authorize wallet
|
||||
if (connector instanceof InjectedConnector) {
|
||||
// extra step for injected connector - authorize wallet
|
||||
await connector.connectWallet();
|
||||
}
|
||||
await connect(connector); // connect with keys
|
||||
|
||||
if (acknowledgeNeeded) {
|
||||
@@ -50,7 +71,7 @@ export const useInjectedConnector = (onConnect: () => void) => {
|
||||
setStatus(Status.Error);
|
||||
}
|
||||
},
|
||||
[acknowledgeNeeded, connect, onConnect]
|
||||
[VEGA_URL, acknowledgeNeeded, connect, onConnect]
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getSnap } from './connectors';
|
||||
|
||||
const INTERVAL = 2_000;
|
||||
|
||||
export const useIsSnapRunning = (snapId: string) => {
|
||||
const [running, setRunning] = useState(false);
|
||||
useEffect(() => {
|
||||
const checkState = async () => {
|
||||
const snap = await getSnap(snapId);
|
||||
setRunning(!!snap);
|
||||
};
|
||||
const i = setInterval(() => {
|
||||
checkState();
|
||||
}, INTERVAL);
|
||||
checkState();
|
||||
return () => {
|
||||
clearInterval(i);
|
||||
};
|
||||
}, [snapId]);
|
||||
return running;
|
||||
};
|
||||
@@ -12,7 +12,9 @@ export const useIsWalletServiceRunning = (
|
||||
|
||||
const checkState = useCallback(async () => {
|
||||
const connector = connectors['jsonRpc'] as JsonRpcConnector;
|
||||
connector.url = url;
|
||||
if (url && url !== connector.url) {
|
||||
connector.url = url;
|
||||
}
|
||||
try {
|
||||
await connector.checkCompat();
|
||||
const chainIdResult = await connector.getChainId();
|
||||
|
||||
@@ -73,6 +73,7 @@ export const useVegaTransactionStore = create<VegaTransactionStore>()(
|
||||
order,
|
||||
};
|
||||
set({ transactions: transactions.concat(transaction) });
|
||||
|
||||
return transaction.id;
|
||||
},
|
||||
update: (index: number, update: Partial<VegaStoredTxState>) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { VegaTransactionContentMap } from './vega-transaction-dialog';
|
||||
import { VegaTransactionDialog } from './vega-transaction-dialog';
|
||||
import type { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import type { Transaction } from './connectors';
|
||||
import type { WalletError } from './connectors';
|
||||
import { WalletError } from './connectors';
|
||||
import { ClientErrors } from './connectors';
|
||||
|
||||
export interface DialogProps {
|
||||
@@ -44,7 +44,7 @@ export const initialState = {
|
||||
};
|
||||
|
||||
export const orderErrorResolve = (err: Error | unknown): Error => {
|
||||
if (err instanceof WalletClientError) {
|
||||
if (err instanceof WalletClientError || err instanceof WalletError) {
|
||||
return err;
|
||||
} else if (err instanceof WalletHttpError) {
|
||||
return ClientErrors.UNKNOWN;
|
||||
|
||||
@@ -725,11 +725,11 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
|
||||
const VegaTxErrorToastContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
let label = t('Error occurred');
|
||||
let errorMessage = `${tx.error?.message} ${
|
||||
tx.error instanceof WalletError && tx.error?.data
|
||||
? `: ${tx.error?.data}`
|
||||
: ''
|
||||
}`;
|
||||
let errorMessage =
|
||||
tx.error instanceof WalletError
|
||||
? `${tx.error.title}: ${tx.error.data}`
|
||||
: tx.error?.message;
|
||||
|
||||
const reconnectVegaWallet = useReconnectVegaWallet();
|
||||
|
||||
const orderRejection = tx.order && getRejectionReason(tx.order);
|
||||
@@ -740,6 +740,7 @@ const VegaTxErrorToastContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
const walletError =
|
||||
tx.error instanceof WalletError &&
|
||||
walletNoConnectionCodes.includes(tx.error.code);
|
||||
|
||||
if (orderRejection) {
|
||||
label = getOrderToastTitle(tx.order?.status) || t('Order rejected');
|
||||
errorMessage = t('Your order has been rejected because: %s', [
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
Dialog,
|
||||
Intent,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { MetaMask } from '@web3-react/metamask';
|
||||
import { WalletConnect } from '@web3-react/walletconnect-v2';
|
||||
import { WalletConnect as WalletConnectLegacy } from '@web3-react/walletconnect';
|
||||
@@ -118,7 +123,7 @@ export const Web3ConnectUncontrolledDialog = () => {
|
||||
function getConnectorInfo(connector: Connector) {
|
||||
if (connector instanceof MetaMask) {
|
||||
return {
|
||||
icon: <MetaMaskIcon width={40} />,
|
||||
icon: <VegaIcon name={VegaIconNames.METAMASK} size={32} />,
|
||||
name: 'MetaMask',
|
||||
text: t('MetaMask'),
|
||||
alt: t('MetaMask, Brave or other injected web wallet'),
|
||||
@@ -126,14 +131,14 @@ function getConnectorInfo(connector: Connector) {
|
||||
}
|
||||
if (connector instanceof CoinbaseWallet) {
|
||||
return {
|
||||
icon: <CoinbaseWalletIcon width={40} />,
|
||||
icon: <CoinbaseWalletIcon width={32} />,
|
||||
name: 'CoinbaseWallet',
|
||||
text: t('Coinbase'),
|
||||
};
|
||||
}
|
||||
if (connector instanceof WalletConnect) {
|
||||
return {
|
||||
icon: <WalletConnectIcon width={40} />,
|
||||
icon: <WalletConnectIcon width={32} />,
|
||||
name: 'WalletConnect',
|
||||
text: t('WalletConnect'),
|
||||
alt: t('WalletConnect v2'),
|
||||
@@ -143,7 +148,7 @@ function getConnectorInfo(connector: Connector) {
|
||||
return {
|
||||
icon: (
|
||||
<WalletConnectIcon
|
||||
width={40}
|
||||
width={32}
|
||||
fillColor={theme.colors.vega.light[200]}
|
||||
/>
|
||||
),
|
||||
@@ -160,115 +165,6 @@ type IconProps = {
|
||||
height?: number;
|
||||
};
|
||||
|
||||
const MetaMaskIcon = ({ width, height }: IconProps) => (
|
||||
<svg
|
||||
viewBox="0 0 47 47"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="m40.632 6.969-14.136 10.62 2.628-6.259L40.632 6.97Z"
|
||||
fill="#E17726"
|
||||
stroke="#E17726"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m8.024 6.969 14.01 10.72-2.502-6.359L8.024 6.97ZM35.542 31.594l-3.761 5.834 8.054 2.251 2.307-7.958-6.6-.127ZM6.528 31.721 8.82 39.68l8.04-2.251-3.747-5.834-6.586.127Z"
|
||||
fill="#E27625"
|
||||
stroke="#E27625"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m16.428 21.738-2.237 3.427 7.97.368-.266-8.709-5.467 4.914ZM32.229 21.738l-5.552-5.012-.181 8.807 7.97-.368-2.237-3.427ZM16.861 37.428l4.824-2.365-4.152-3.285-.672 5.65ZM26.971 35.063l4.81 2.365-.657-5.65-4.153 3.285Z"
|
||||
fill="#E27625"
|
||||
stroke="#E27625"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m31.78 37.428-4.81-2.365.392 3.172-.042 1.345 4.46-2.152ZM16.861 37.428l4.475 2.152-.028-1.345.377-3.172-4.824 2.365Z"
|
||||
fill="#D5BFB2"
|
||||
stroke="#D5BFB2"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m21.42 29.682-4-1.19 2.825-1.316 1.174 2.506ZM27.236 29.682l1.175-2.506 2.838 1.317-4.013 1.19Z"
|
||||
fill="#233447"
|
||||
stroke="#233447"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m16.861 37.427.7-5.834-4.447.128 3.747 5.706ZM31.096 31.593l.685 5.834 3.761-5.706-4.446-.128ZM34.465 25.165l-7.97.368.741 4.15 1.175-2.507 2.838 1.317 3.216-3.328ZM17.42 28.493l2.825-1.317 1.175 2.506.74-4.149-7.97-.368 3.23 3.328Z"
|
||||
fill="#CC6228"
|
||||
stroke="#CC6228"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m14.19 25.165 3.343 6.613-.112-3.285-3.23-3.328ZM31.25 28.493l-.126 3.285 3.342-6.613-3.216 3.328ZM22.161 25.533l-.741 4.149.937 4.9.21-6.458-.406-2.591ZM26.495 25.533l-.391 2.577.196 6.471.937-4.9-.741-4.148Z"
|
||||
fill="#E27525"
|
||||
stroke="#E27525"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m27.237 29.682-.937 4.9.671.481 4.153-3.285.126-3.285-4.013 1.19ZM17.42 28.493l.112 3.285 4.153 3.285.671-.481-.937-4.9-3.999-1.19Z"
|
||||
fill="#F5841F"
|
||||
stroke="#F5841F"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m27.32 39.58.042-1.345-.363-.312h-5.342l-.35.312.029 1.345-4.475-2.152 1.566 1.303 3.175 2.223h5.439l3.188-2.224 1.552-1.302-4.46 2.152Z"
|
||||
fill="#C0AC9D"
|
||||
stroke="#C0AC9D"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m26.97 35.063-.67-.482h-3.944l-.67.482-.378 3.172.35-.312h5.34l.364.312-.391-3.172Z"
|
||||
fill="#161616"
|
||||
stroke="#161616"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m41.234 18.283 1.188-5.863-1.79-5.451-13.66 10.266 5.257 4.503 7.425 2.195 1.636-1.94-.713-.524 1.132-1.048-.867-.68 1.133-.878-.741-.58ZM6.234 12.42l1.203 5.863-.77.58 1.147.878-.867.68L8.08 21.47l-.713.524 1.636 1.94 7.425-2.195 5.257-4.503L8.025 6.97l-1.79 5.452Z"
|
||||
fill="#763E1A"
|
||||
stroke="#763E1A"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
<path
|
||||
d="m39.654 23.933-7.425-2.195 2.237 3.427-3.342 6.613 4.419-.057h6.6l-2.49-7.788ZM16.428 21.738l-7.425 2.195-2.475 7.788h6.586l4.418.056-3.342-6.612 2.238-3.427ZM26.495 25.533l.476-8.298 2.153-5.905h-9.592l2.153 5.905.476 8.298.181 2.605.014 6.443H26.3l.014-6.443.182-2.605Z"
|
||||
fill="#F5841F"
|
||||
stroke="#F5841F"
|
||||
strokeWidth="0.223"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const CoinbaseWalletIcon = ({ width, height }: IconProps) => (
|
||||
<svg
|
||||
width={width}
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
"echo $NX_TENDERMINT_URL",
|
||||
"echo $NX_TENDERMINT_WEBSOCKET_URL",
|
||||
"echo $NX_ETHEREUM_PROVIDER_URL"
|
||||
],
|
||||
"url": "https://cloud.nx.app"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
# First use & get started steps
|
||||
|
||||
## When first enter the app
|
||||
## "Onboarding" state is gradable and has following steps: (<a name="0007-FUGS-002" href="#0007-FUGS-002">0007-FUGS-003</a>)
|
||||
|
||||
- New visitor - has no wallet nor any dapps running.
|
||||
- I **must** see CTA button "Get started", which clicking launches the get wallet flow (wallet connection window with links to the Chrome and FF stores)
|
||||
- Has wallet - Once wallet detected set to this.
|
||||
- I **must** see CTA button "Connect", which clicking launches the wallet connection
|
||||
- Has connected - Once user has connected set to this.
|
||||
- I **must** see CTA button "Deposit", which clicking launches the deposit ticket.
|
||||
- Has deposited - Once user has made AT LEAST one deposit of ANY settlement asset set to this.
|
||||
- I **must** see CTA button "Dismiss", which clicking updates the state to Ready to trade - Get started box should now disappear forever.
|
||||
- Ready to trade - Once user has made at least one deposit AND has dismissed the "Get Started" box in the ticket.
|
||||
- Onboarding window nor contextual "Get started" banner should be not displayed anymore.
|
||||
|
||||
## When first enter the app or next times, but I didn't accomplish all onboarding steps.
|
||||
|
||||
- **Must** When I open Console for the first time I can see what it is i.e. a short description and key features in auto opened dialog window (first use popup) (<a name="0007-FUGS-001" href="#0007-FUGS-001">0007-FUGS-001</a>)
|
||||
- **Must** If my wallet is already connected I don't see the first use popup (<a name="0007-FUGS-002" href="#0007-FUGS-002">0007-FUGS-002</a>)
|
||||
- - **Must** If window.vega is detected (browser wallet is installed), don't open first use popup (<a name="0007-FUGS-003" href="#0007-FUGS-003">0007-FUGS-003</a>)
|
||||
- - **Must** If we detect previous connection using localStorage for desktop/cli wallet, don't open first use popup (<a name="0007-FUGS-004" href="#0007-FUGS-004">0007-FUGS-004</a>)
|
||||
- If full "onboarding" hasn't been accomplished yet, I **must** see the popup with my progress marked. (<a name="0007-FUGS-002" href="#0007-FUGS-002">0007-FUGS-002</a>)
|
||||
- **Must** There is a call to action to browse markets, linking to the market view market/all (<a name="0007-FUGS-005" href="#0007-FUGS-005">0007-FUGS-005</a>)
|
||||
- **Must** I can see the steps I need to take to get started trading (<a name="0007-FUGS-007" href="#0007-FUGS-006">0007-FUGS-006</a>)
|
||||
- **Must** There is a call to action to get started, triggering the connect modal (<a name="0007-FUGS-007" href="#0007-FUGS-007">0007-FUGS-007</a>)
|
||||
- **Must** There is a link to try out trading on Fairground when I'm on Mainnet (<a name="0007-FUGS-008" href="#0007-FUGS-008">0007-FUGS-008</a>)
|
||||
- **Must** There is a link to trade with real funds on Mainnet when I am on Fairground (<a name="0007-FUGS-010" href="#0007-FUGS-010">0007-FUGS-010</a>)
|
||||
- **Must** When I am on the Fairground version, I can see a warning / call out that this is Fairground meaning I can try out with virtual assets at no risk (<a name="0007-FUGS-011" href="#0007-FUGS-011">0007-FUGS-011</a>)
|
||||
- If I dismiss the popup, I **must** not see it unless I NOT accomplish full "onboarding"
|
||||
- If I dismiss the popup, I land on the default market (<a name="0007-FUGS-012" href="#0007-FUGS-012">0007-FUGS-012</a>)
|
||||
|
||||
## When first use popup has been seen, but no browser wallet is installed
|
||||
## When the popup has been dismissed:
|
||||
|
||||
- **Must** I can see the steps to get started with a visible call to action to "get started" in the context of the deal ticket, deposit, withdraw, transfer components in the sidebar (<a name="0007-FUGS-013" href="#0007-FUGS-013">0007-FUGS-013</a>)
|
||||
- **Must** I can see the steps to get started with a visible call to action (according to my progress) in the context of the deal ticket, deposit, withdraw, transfer components in the sidebar (<a name="0007-FUGS-013" href="#0007-FUGS-013">0007-FUGS-013</a>)
|
||||
- **Must** Remove buttons from pane containers that prompt to connect wallet (<a name="0007-FUGS-014" href="#0007-FUGS-014">0007-FUGS-014</a>)
|
||||
- **Must** We've replaced "connect wallet" in the top right with "get started" (<a name="0007-FUGS-015" href="#0007-FUGS-015">0007-FUGS-015</a>)
|
||||
- **Must** When I press the get started CTA, I see the wallet connect popup (<a name="0007-FUGS-016" href="#0007-FUGS-016">0007-FUGS-016</a>)
|
||||
|
||||
Reference in New Issue
Block a user