diff --git a/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts b/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts index dd5e329c1..d3fd39507 100644 --- a/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts +++ b/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts @@ -25,6 +25,9 @@ import { vegaWalletSetSpecifiedApprovalAmount, vegaWalletTeardown, } from '../../support/wallet-functions'; +import { aliasGQLQuery } from '@vegaprotocol/cypress'; +import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock'; + const stakeValidatorListTotalStake = 'total-stake'; const stakeValidatorListTotalShare = 'total-stake-share'; const stakeValidatorListStakePercentage = 'stake-percentage'; @@ -55,6 +58,10 @@ context( function () { // 1002-STKE-002, 1002-STKE-032 before('visit staking tab and connect vega wallet', function () { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); cy.visit('/'); ethereumWalletConnect(); cy.connectVegaWallet(); @@ -65,6 +72,10 @@ context( beforeEach( 'teardown wallet & drill into a specific validator', function () { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); cy.clearLocalStorage(); turnTelemetryOff(); // Go to homepage to allow wallet teardown without epoch timer refreshing page diff --git a/apps/governance-e2e/src/integration/flow/withdrawal-flow.cy.ts b/apps/governance-e2e/src/integration/flow/withdrawal-flow.cy.ts index 1924f069e..946311c28 100644 --- a/apps/governance-e2e/src/integration/flow/withdrawal-flow.cy.ts +++ b/apps/governance-e2e/src/integration/flow/withdrawal-flow.cy.ts @@ -6,6 +6,8 @@ import { } from '../../support/common.functions'; import { ethereumWalletConnect } from '../../support/wallet-eth.functions'; import { depositAsset } from '../../support/wallet-functions'; +import { aliasGQLQuery } from '@vegaprotocol/cypress'; +import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock'; const withdraw = 'withdraw'; const withdrawalForm = 'withdraw-form'; @@ -42,12 +44,20 @@ context( { tags: '@slow' }, function () { before('visit withdrawals and connect vega wallet', function () { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); cy.visit('/'); ethereumWalletConnect(); depositAsset(usdcEthAddress, '1000', 5); }); beforeEach('Navigate to withdrawal page', function () { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); cy.clearLocalStorage(); turnTelemetryOff(); cy.reload(); diff --git a/apps/governance-e2e/src/integration/view/validators.cy.ts b/apps/governance-e2e/src/integration/view/validators.cy.ts index 0f32838eb..2ed63d8ba 100644 --- a/apps/governance-e2e/src/integration/view/validators.cy.ts +++ b/apps/governance-e2e/src/integration/view/validators.cy.ts @@ -11,6 +11,7 @@ import { waitForBeginningOfEpoch, } from '../../support/staking.functions'; import { previousEpochData } from '../../fixtures/mocks/previous-epoch'; +import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock'; const guideLink = 'staking-guide-link'; const validatorTitle = 'validator-node-title'; @@ -33,11 +34,22 @@ const overstakedPenaltyToolTip = 'overstaked-penalty-tooltip'; const multisigPenaltyToolTip = 'multisig-error-tooltip'; const epochCountDown = 'epoch-countdown'; const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/; +const txTimeout = Cypress.env('txTimeout'); context('Validators Page - verify elements on page', function () { - before('navigate to validators page', function () { + before('navigate to validators page', () => { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); cy.visit('/validators'); }); + beforeEach(() => { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); + }); describe('with wallets disconnected', { tags: '@smoke' }, function () { it('Should have validators tab highlighted', function () { @@ -177,6 +189,11 @@ context('Validators Page - verify elements on page', function () { { tags: '@smoke' }, function () { before('connect wallets and click on validator', function () { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); + cy.visit('/validators'); cy.connectVegaWallet(); clickOnValidatorFromList(0); }); @@ -263,7 +280,7 @@ context('Validators Page - verify elements on page', function () { cy.getByTestId(epochCountDown).within(() => { cy.get(epochTitle).should('not.be.empty'); - cy.get(nextEpochInfo).should('contain.text', 'Next epoch'); + cy.get(nextEpochInfo, txTimeout).should('contain.text', 'Next epoch'); }); }); } diff --git a/apps/governance-e2e/src/integration/view/wallet-vega.cy.ts b/apps/governance-e2e/src/integration/view/wallet-vega.cy.ts index 6725d01fb..df7d4682f 100644 --- a/apps/governance-e2e/src/integration/view/wallet-vega.cy.ts +++ b/apps/governance-e2e/src/integration/view/wallet-vega.cy.ts @@ -4,6 +4,8 @@ import { vegaWalletFaucetAssetsWithoutCheck, vegaWalletTeardown, } from '../../support/wallet-functions'; +import { aliasGQLQuery } from '@vegaprotocol/cypress'; +import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock'; const walletContainer = 'aside [data-testid="vega-wallet"]'; const walletHeader = '[data-testid="wallet-header"] h1'; @@ -14,10 +16,6 @@ const dialogHeader = 'dialog-title'; const walletDialogHeader = 'wallet-dialog-title'; const connectorsList = 'connectors-list'; const dialogCloseBtn = 'dialog-close'; -const restConnectorForm = 'rest-connector-form'; -const restWallet = '#wallet'; -const restPassphrase = '#passphrase'; -const restConnectBtn = '[type="submit"]'; const accountNo = 'vega-account-truncated'; const currencyTitle = 'currency-title'; const currencyValue = 'currency-value'; @@ -69,7 +67,7 @@ context( cy.get(dialog).within(() => { cy.getByTestId(walletDialogHeader) .should('be.visible') - .and('have.text', 'Connect'); + .and('have.text', 'Get a Vega wallet'); }); }); @@ -77,10 +75,7 @@ context( cy.getByTestId(connectorsList).within(() => { cy.getByTestId('connector-jsonRpc') .should('be.visible') - .and('have.text', 'Connect Vega wallet'); - cy.getByTestId('connector-rest') - .should('be.visible') - .and('have.text', 'Hosted Fairground wallet'); + .and('have.text', 'Use the Desktop App/CLI'); }); }); @@ -91,48 +86,14 @@ context( }); }); - describe('when rest connector form opened', function () { - before('click hosted wallet app button', function () { - cy.getByTestId(connectorsList).within(() => { - cy.getByTestId('connector-rest').click(); - }); - }); - - // 0002-WCON-002 - it('should have wallet field visible', function () { - cy.getByTestId(restConnectorForm).within(() => { - cy.get(restWallet).should('be.visible'); - }); - }); - - it('should have password field visible', function () { - cy.getByTestId(restConnectorForm).within(() => { - cy.get(restPassphrase).should('be.visible'); - }); - }); - - it('should have connect button visible', function () { - cy.getByTestId(restConnectorForm).within(() => { - cy.get(restConnectBtn) - .should('be.visible') - .and('have.text', 'Connect'); - }); - }); - - it('should have close button visible', function () { - cy.get(dialog).within(() => { - cy.getByTestId(dialogCloseBtn).should('be.visible'); - }); - }); - - after('close dialog', function () { - cy.getByTestId(dialogCloseBtn).click().should('not.exist'); - }); - }); - describe('when vega wallet connected', function () { before('connect vega wallet', function () { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); cy.visit('/'); + cy.wait('@ChainId'); cy.connectVegaWallet(); vegaWalletTeardown(); }); @@ -315,6 +276,10 @@ context( ]; before('faucet assets to connected vega wallet', function () { + cy.mockGQL((req) => { + aliasGQLQuery(req, 'ChainId', chainIdQuery()); + aliasGQLQuery(req, 'Statistics', statisticsQuery()); + }); for (const { id, amount } of assets) { vegaWalletFaucetAssetsWithoutCheck(id, amount, vegaWalletPublicKey); } diff --git a/apps/governance/.env b/apps/governance/.env index 91458552d..a9b48bb2e 100644 --- a/apps/governance/.env +++ b/apps/governance/.env @@ -21,9 +21,12 @@ 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 LC_ALL="en_US.UTF-8" # Cosmic elevator flags -NX_SUCCESSOR_MARKETS=true \ No newline at end of file +NX_SUCCESSOR_MARKETS=true diff --git a/apps/governance/src/components/vega-wallet/vega-wallet-prompt.tsx b/apps/governance/src/components/vega-wallet/vega-wallet-prompt.tsx index c0de8471e..340aa389e 100644 --- a/apps/governance/src/components/vega-wallet/vega-wallet-prompt.tsx +++ b/apps/governance/src/components/vega-wallet/vega-wallet-prompt.tsx @@ -15,6 +15,7 @@ export const VegaWalletPrompt = () => { setViewAsDialog(true)} > {t('viewAsParty')} diff --git a/apps/governance/src/lib/vega-connectors.ts b/apps/governance/src/lib/vega-connectors.ts index d66317fdb..32b305f55 100644 --- a/apps/governance/src/lib/vega-connectors.ts +++ b/apps/governance/src/lib/vega-connectors.ts @@ -1,5 +1,4 @@ import { - RestConnector, JsonRpcConnector, ViewConnector, InjectedConnector, @@ -8,13 +7,11 @@ import { const urlParams = new URLSearchParams(window.location.search); export const injected = new InjectedConnector(); -export const rest = new RestConnector(); export const jsonRpc = new JsonRpcConnector(); export const view = new ViewConnector(urlParams.get('address')); export const Connectors = { injected, - rest, jsonRpc, view, }; diff --git a/apps/trading-e2e/src/integration/home.cy.ts b/apps/trading-e2e/src/integration/home.cy.ts index d5f6d774d..88081b42c 100644 --- a/apps/trading-e2e/src/integration/home.cy.ts +++ b/apps/trading-e2e/src/integration/home.cy.ts @@ -169,14 +169,13 @@ describe('home', { tags: '@regression' }, () => { it('click get started button should open connect dialog', () => { cy.getByTestId('welcome-dialog').should('be.visible'); - cy.getByTestId('get-started-button').click(); cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`); cy.window().then((window) => { - expect(window.localStorage.getItem('vega_onboarding_viewed')).to.equal( - 'true' - ); + // @ts-ignore stub it out just for test case + window.vega = {}; + cy.getByTestId('get-started-button').click(); + cy.getByTestId('wallet-dialog-title').should('contain.text', 'Connect'); }); - cy.getByTestId('wallet-dialog-title').should('contain.text', 'Connect'); }); }); diff --git a/apps/trading-e2e/src/integration/wallet-vega.cy.ts b/apps/trading-e2e/src/integration/wallet-vega.cy.ts index 18de0c40e..850b1146d 100644 --- a/apps/trading-e2e/src/integration/wallet-vega.cy.ts +++ b/apps/trading-e2e/src/integration/wallet-vega.cy.ts @@ -5,118 +5,8 @@ import { const connectVegaBtn = 'connect-vega-wallet'; const manageVegaBtn = 'manage-vega-wallet'; -const form = 'rest-connector-form'; const dialogContent = 'dialog-content'; -describe( - 'connect hosted wallet', - { tags: '@smoke', testIsolation: true }, - () => { - beforeEach(() => { - // Using portfolio page as it requires vega wallet connection - cy.visit('/#/portfolio'); - cy.mockTradingPage(); - cy.mockSubscription(); - cy.setOnBoardingViewed(); - cy.get('[data-testid="pathname-/portfolio"]').should('exist'); - }); - - it('can connect', () => { - // 0002-WCON-002 - // 0002-WCON-003 - // 0002-WCON-039 - // 0002-WCON-017 - // 0002-WCON-018 - // 0002-WCON-019 - - // Mock authentication - cy.intercept( - 'POST', - 'https://wallet.testnet.vega.xyz/api/v1/auth/token', - { - body: { - token: 'test-token', - }, - } - ); - // Mock getting keys from wallet - cy.intercept('GET', 'https://wallet.testnet.vega.xyz/api/v1/keys', { - body: { - keys: [ - { - algorithm: { - name: 'algo', - version: 1, - }, - index: 0, - meta: [], - pub: 'HOSTED_PUBKEY', - tainted: false, - }, - ], - }, - }); - cy.getByTestId(connectVegaBtn).click(); - cy.contains( - 'Choose wallet app to connect, or to change port or server URL enter a custom wallet location first' - ); - cy.contains('Connect Vega wallet'); - cy.contains('Hosted Fairground wallet'); - - cy.getByTestId('connectors-list') - .find('[data-testid="connector-rest"]') - .click(); - cy.getByTestId(form).find('#wallet').click().type('user'); - cy.getByTestId(form).find('#passphrase').click().type('pass'); - cy.getByTestId('rest-connector-form').find('button[type=submit]').click(); - cy.getByTestId(manageVegaBtn).should('exist'); - cy.getByTestId('manage-vega-wallet').click(); - cy.getByTestId('keypair-list').should('exist'); - }); - - it('doesnt connect with invalid credentials', () => { - // 0002-WCON-020 - - // Mock incorrect username/password - cy.intercept( - 'POST', - 'https://wallet.testnet.vega.xyz/api/v1/auth/token', - { - body: { - error: 'No wallet', - }, - statusCode: 403, // 403 forbidden invalid crednetials - } - ); - cy.getByTestId(connectVegaBtn).click(); - cy.getByTestId('connectors-list') - .find('[data-testid="connector-rest"]') - .click(); - cy.getByTestId(form).find('#wallet').click().type('invalid name'); - cy.getByTestId(form).find('#passphrase').click().type('invalid password'); - cy.getByTestId('rest-connector-form').find('button[type=submit]').click(); - cy.getByTestId('form-error').should('have.text', 'Invalid credentials'); - }); - - it('doesnt connect with empty fields', () => { - cy.getByTestId(connectVegaBtn).click(); - cy.getByTestId('connectors-list') - .find('[data-testid="connector-rest"]') - .click(); - - cy.getByTestId('rest-connector-form').find('button[type=submit]').click(); - cy.getByTestId(form) - .find('#wallet') - .next('[data-testid="input-error-text"]') - .should('have.text', 'Required'); - cy.getByTestId(form) - .find('#passphrase') - .next('[data-testid="input-error-text"]') - .should('have.text', 'Required'); - }); - } -); - describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => { beforeEach(() => { // Using portfolio page as it requires vega wallet connection @@ -196,8 +86,6 @@ describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => { cy.getByTestId('connect-vega-wallet').should('exist'); cy.getByTestId('manage-vega-wallet').should('not.exist'); cy.getByTestId('connect-vega-wallet').click(); - cy.contains( - 'Choose wallet app to connect, or to change port or server URL enter a custom wallet location first' - ); + cy.contains('Enter a custom wallet location'); }); }); diff --git a/apps/trading/.env b/apps/trading/.env index ea12adf76..d4e8a9cab 100644 --- a/apps/trading/.env +++ b/apps/trading/.env @@ -13,9 +13,11 @@ 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 \ No newline at end of file +# NX_PRODUCT_PERPETUALS diff --git a/apps/trading/client-pages/markets/closed.spec.tsx b/apps/trading/client-pages/markets/closed.spec.tsx index 26c799b55..5084426e4 100644 --- a/apps/trading/client-pages/markets/closed.spec.tsx +++ b/apps/trading/client-pages/markets/closed.spec.tsx @@ -411,24 +411,26 @@ describe('Closed', () => { }, }, }; - render( - - - { + render( + + - - - - - ); + + + + + + ); + }); await waitFor(() => { expect( diff --git a/apps/trading/lib/vega-connectors.ts b/apps/trading/lib/vega-connectors.ts index 15f0dd0e5..b1dcf762b 100644 --- a/apps/trading/lib/vega-connectors.ts +++ b/apps/trading/lib/vega-connectors.ts @@ -1,11 +1,9 @@ import { - RestConnector, JsonRpcConnector, ViewConnector, InjectedConnector, } from '@vegaprotocol/wallet'; -export const rest = new RestConnector(); export const jsonRpc = new JsonRpcConnector(); export const injected = new InjectedConnector(); @@ -19,7 +17,6 @@ if (typeof window !== 'undefined') { export const Connectors = { injected, - rest, jsonRpc, view, }; diff --git a/libs/cypress/src/lib/commands/add-connect-public-key.ts b/libs/cypress/src/lib/commands/add-connect-public-key.ts index e5816aba0..02efc1d81 100644 --- a/libs/cypress/src/lib/commands/add-connect-public-key.ts +++ b/libs/cypress/src/lib/commands/add-connect-public-key.ts @@ -10,14 +10,11 @@ declare global { export const addConnectPublicKey = () => { Cypress.Commands.add('connectPublicKey', (publicKey) => { - const connectVegaWaletBtn = Cypress.$( - `[data-testid="connect-vega-wallet"]` - ); + const connectVegaWaletBtn = Cypress.$(`[data-testid="view-as-user"]`); if (connectVegaWaletBtn.length > 0) { - cy.get('aside [data-testid="connect-vega-wallet"]').click(); - cy.getByTestId('connector-view').should('be.visible').click(); - cy.getByTestId('address').click(); - cy.getByTestId('address').type(publicKey); + cy.get('aside button').contains('View as party').click(); + cy.getByTestId('address').should('be.visible').focus(); + cy.getByTestId('address').type(publicKey, { delay: 50 }); cy.getByTestId('connect').click(); } }); diff --git a/libs/cypress/src/lib/commands/vega-wallet-connect.ts b/libs/cypress/src/lib/commands/vega-wallet-connect.ts index 3335b7fae..dba138df2 100644 --- a/libs/cypress/src/lib/commands/vega-wallet-connect.ts +++ b/libs/cypress/src/lib/commands/vega-wallet-connect.ts @@ -29,7 +29,7 @@ export const mockConnectWallet = () => { export const mockConnectWalletWithUserError = () => { cy.mockWallet((req) => { - aliasWalletConnectWithUserError(req); + aliasWalletConnectWithUserError(req, Cypress.env('VEGA_WALLET_API_TOKEN')); }); }; diff --git a/libs/cypress/src/lib/mock-rest.ts b/libs/cypress/src/lib/mock-rest.ts index 5da0287c6..359a37a48 100644 --- a/libs/cypress/src/lib/mock-rest.ts +++ b/libs/cypress/src/lib/mock-rest.ts @@ -62,10 +62,27 @@ export const aliasWalletConnectQuery = ( }, }); } + if (hasMethod(req, 'client.get_chain_id')) { + req.reply({ + statusCode: 200, + headers: { + 'Access-Control-Expose-Headers': 'Authorization', + Authorization: `VWT ${token}`, + }, + body: { + jsonrpc: '2.0', + result: { + chainID: 'test-id', + }, + id: '1', + }, + }); + } }; export const aliasWalletConnectWithUserError = ( - req: CyHttpMessages.IncomingHttpRequest + req: CyHttpMessages.IncomingHttpRequest, + token: string ) => { if (hasMethod(req, 'client.connect_wallet')) { req.alias = 'client.connect_wallet'; @@ -82,4 +99,20 @@ export const aliasWalletConnectWithUserError = ( }, }); } + if (hasMethod(req, 'client.get_chain_id')) { + req.reply({ + statusCode: 200, + headers: { + 'Access-Control-Expose-Headers': 'Authorization', + Authorization: `VWT ${token}`, + }, + body: { + jsonrpc: '2.0', + result: { + chainID: 'test-id', + }, + id: '1', + }, + }); + } }; diff --git a/libs/environment/src/hooks/use-environment.ts b/libs/environment/src/hooks/use-environment.ts index 1f952bbc4..2a39c6625 100644 --- a/libs/environment/src/hooks/use-environment.ts +++ b/libs/environment/src/hooks/use-environment.ts @@ -371,6 +371,14 @@ function compileEnvVars() { 'NX_TENDERMINT_WEBSOCKET_URL', process.env['NX_TENDERMINT_WEBSOCKET_URL'] ), + CHROME_EXTENSION_URL: windowOrDefault( + 'NX_CHROME_EXTENSION_URL', + process.env['NX_CHROME_EXTENSION_URL'] + ), + MOZILLA_EXTENSION_URL: windowOrDefault( + 'NX_MOZILLA_EXTENSION_URL', + process.env['NX_MOZILLA_EXTENSION_URL'] + ), }; return env; diff --git a/libs/environment/src/hooks/use-links.ts b/libs/environment/src/hooks/use-links.ts index 9995e24f2..0c95820c8 100644 --- a/libs/environment/src/hooks/use-links.ts +++ b/libs/environment/src/hooks/use-links.ts @@ -160,7 +160,9 @@ export const ExternalLinks = { MARGIN_CREDIT_RISK: 'https://vega.xyz/papers/margins-and-credit-risk.pdf#page=7', VEGA_WALLET_URL: 'https://vega.xyz/wallet', + VEGA_WALLET_URL_ABOUT: 'https://vega.xyz/wallet/#overview', VEGA_WALLET_HOSTED_URL: 'https://vega-hosted-wallet.on.fleek.co/', + VEGA_WALLET_BROWSER_LIST: '', BLOG: 'https://blog.vega.xyz/', }; diff --git a/libs/environment/src/utils/validate-environment.ts b/libs/environment/src/utils/validate-environment.ts index f094dd34f..baf102b14 100644 --- a/libs/environment/src/utils/validate-environment.ts +++ b/libs/environment/src/utils/validate-environment.ts @@ -59,6 +59,8 @@ export const envSchema = z SENTRY_DSN: z.optional(z.string()), TENDERMINT_URL: z.optional(z.string()), TENDERMINT_WEBSOCKET_URL: z.optional(z.string()), + CHROME_EXTENSION_URL: z.optional(z.string()), + MOZILLA_EXTENSION_URL: z.optional(z.string()), }) .refine( (data) => { diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-arrow-left.tsx b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-arrow-left.tsx new file mode 100644 index 000000000..b40993b3f --- /dev/null +++ b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-arrow-left.tsx @@ -0,0 +1,19 @@ +export const IconArrowLeft = ({ size = 16 }: { size: number }) => { + return ( + + + + ); +}; diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-arrow-top-right.tsx b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-arrow-top-right.tsx new file mode 100644 index 000000000..6e2294914 --- /dev/null +++ b/libs/ui-toolkit/src/components/icon/vega-icons/svg-icons/icon-arrow-top-right.tsx @@ -0,0 +1,19 @@ +export const IconArrowTopRight = ({ size = 16 }: { size: number }) => { + return ( + + + + ); +}; diff --git a/libs/ui-toolkit/src/components/icon/vega-icons/vega-icon-record.ts b/libs/ui-toolkit/src/components/icon/vega-icons/vega-icon-record.ts index 3e907c4d3..60ee1f1ad 100644 --- a/libs/ui-toolkit/src/components/icon/vega-icons/vega-icon-record.ts +++ b/libs/ui-toolkit/src/components/icon/vega-icons/vega-icon-record.ts @@ -1,6 +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 { IconBreakdown } from './svg-icons/icon-breakdown'; import { IconBullet } from './svg-icons/icon-bullet'; import { IconChevronDown } from './svg-icons/icon-chevron-down'; @@ -36,8 +38,10 @@ 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', BREAKDOWN = 'breakdown', BULLET = 'bullet', CHEVRON_DOWN = 'chevron-down', @@ -77,8 +81,10 @@ export const VegaIconNameMap: Record< ({ size }: { size: number }) => JSX.Element > = { 'arrow-down': IconArrowDown, + 'arrow-left': IconArrowLeft, 'arrow-up': IconArrowUp, 'arrow-right': IconArrowRight, + 'arrow-top-right': IconArrowTopRight, breakdown: IconBreakdown, bullet: IconBullet, 'chevron-down': IconChevronDown, diff --git a/libs/ui-toolkit/src/components/index.ts b/libs/ui-toolkit/src/components/index.ts index 38e0c8b64..4c4ee984d 100644 --- a/libs/ui-toolkit/src/components/index.ts +++ b/libs/ui-toolkit/src/components/index.ts @@ -54,3 +54,4 @@ export * from './traffic-light'; export * from './vega-icons'; export * from './vega-logo'; export * from './viewing-as-user'; +export * from './pill'; diff --git a/libs/ui-toolkit/src/components/pill/index.ts b/libs/ui-toolkit/src/components/pill/index.ts new file mode 100644 index 000000000..ecc4c9e91 --- /dev/null +++ b/libs/ui-toolkit/src/components/pill/index.ts @@ -0,0 +1 @@ +export * from './pill'; diff --git a/libs/ui-toolkit/src/components/pill/pill.stories.tsx b/libs/ui-toolkit/src/components/pill/pill.stories.tsx new file mode 100644 index 000000000..5cd8b7d0a --- /dev/null +++ b/libs/ui-toolkit/src/components/pill/pill.stories.tsx @@ -0,0 +1,48 @@ +import type { Story, Meta } from '@storybook/react'; +import { Pill } from './pill'; +import { Intent } from '../../utils/intent'; + +export default { + component: Pill, + title: 'Pill', +} as Meta; + +const Template: Story = (args) => Pill; + +export const Default = Template.bind({ intent: Intent.Primary, size: 'md' }); + +export const None = Template.bind({}); +None.args = { + intent: Intent.None, + size: 'md', +}; + +export const Info = Template.bind({}); +Info.args = { + intent: Intent.Info, + size: 'md', +}; + +export const Primary = Template.bind({}); +Primary.args = { + intent: Intent.Primary, + size: 'md', +}; + +export const Success = Template.bind({}); +Success.args = { + intent: Intent.Success, + size: 'md', +}; + +export const Warning = Template.bind({}); +Warning.args = { + intent: Intent.Warning, + size: 'md', +}; + +export const Danger = Template.bind({}); +Danger.args = { + intent: Intent.Danger, + size: 'md', +}; diff --git a/libs/ui-toolkit/src/components/pill/pill.tsx b/libs/ui-toolkit/src/components/pill/pill.tsx new file mode 100644 index 000000000..c556bfb45 --- /dev/null +++ b/libs/ui-toolkit/src/components/pill/pill.tsx @@ -0,0 +1,44 @@ +import type { ReactNode } from 'react'; +import { Intent } from '../../utils/intent'; +import classNames from 'classnames'; + +type Size = 'lg' | 'md' | 'sm' | 'xs' | 'xxs'; +interface Props { + children: ReactNode; + intent?: Intent; + size?: Size; + className?: string; +} + +const getClasses = (size: Size, intent?: Intent, className?: string) => { + return classNames( + ['rounded-md', 'leading-none', 'font-alpha', 'py-1 px-2'], + { + 'bg-vega-yellow dark:bg-vega-yellow': intent === Intent.Primary, + 'bg-vega-clight-500 dark:bg-vega-cdark-500': intent === Intent.None, + 'bg-vega-blue-500 dark:bg-vega-blue-500': intent === Intent.Info, + 'bg-vega-orange-350 dark:bg-vega-orange-650': intent === Intent.Warning, + 'bg-vega-red-350 dark:bg-vega-red-650': intent === Intent.Danger, + 'bg-vega-green-350 dark:bg-vega-green-650': intent === Intent.Success, + 'text-vega-clight-50 dark:text-vega-cdark-50': intent !== Intent.Primary, + 'text-vega-clight-900 dark:text-vega-cdark-900': + intent === Intent.Primary, + }, + { + 'text-lg': size === 'lg', + 'text-base': size === 'md', + 'text-sma': size === 'sm', + 'text-xs': size === 'xs', + 'text-[10px]': size === 'xxs', + }, + className + ); +}; + +export const Pill = ({ intent, size, className, children }: Props) => { + return ( + + {children} + + ); +}; diff --git a/libs/ui-toolkit/src/components/trading-button/trading-button.tsx b/libs/ui-toolkit/src/components/trading-button/trading-button.tsx index 1c267c4c8..1dd16daf4 100644 --- a/libs/ui-toolkit/src/components/trading-button/trading-button.tsx +++ b/libs/ui-toolkit/src/components/trading-button/trading-button.tsx @@ -13,6 +13,7 @@ type TradingButtonProps = { children?: ReactNode; icon?: ReactNode; subLabel?: ReactNode; + fill?: boolean; }; const getClassName = ( @@ -20,7 +21,8 @@ const getClassName = ( size, subLabel, intent, - }: Pick, + fill, + }: Pick, className?: string ) => classNames( @@ -61,6 +63,7 @@ const getClassName = ( intent === Intent.Primary, '[&_[data-sub-label]]:text-vega-clight-100': intent === Intent.Primary, }, + { 'w-full': fill }, className ); @@ -99,6 +102,7 @@ export const TradingButton = forwardRef< children, className, subLabel, + fill, ...props }, ref @@ -107,7 +111,7 @@ export const TradingButton = forwardRef< ref={ref} type={type} data-trading-button - className={getClassName({ size, subLabel, intent }, className)} + className={getClassName({ size, subLabel, intent, fill }, className)} {...props} > diff --git a/libs/wallet/src/connect-dialog/connect-dialog-elements.tsx b/libs/wallet/src/connect-dialog/connect-dialog-elements.tsx index 6444051cd..7f3ebd3cf 100644 --- a/libs/wallet/src/connect-dialog/connect-dialog-elements.tsx +++ b/libs/wallet/src/connect-dialog/connect-dialog-elements.tsx @@ -1,16 +1,18 @@ -import { DocsLinks, ExternalLinks } from '@vegaprotocol/environment'; +import { ExternalLinks, useEnvironment } from '@vegaprotocol/environment'; import { t } from '@vegaprotocol/i18n'; -import { Link } from '@vegaprotocol/ui-toolkit'; +import { + ExternalLink, + VegaIcon, + VegaIconNames, +} from '@vegaprotocol/ui-toolkit'; import classNames from 'classnames'; import type { ReactNode } from 'react'; -import type { VegaConnector } from '../connectors'; -import { RestConnector } from '../connectors'; export const ConnectDialogTitle = ({ children }: { children: ReactNode }) => { return (

{children}

@@ -21,48 +23,353 @@ export const ConnectDialogContent = ({ children }: { children: ReactNode }) => { return
{children}
; }; -export const ConnectDialogFooter = ({ - connector, -}: { - connector: VegaConnector | undefined; -}) => { +export const ConnectDialogFooter = () => { const wrapperClasses = classNames( - 'flex justify-center gap-4', + 'flex justify-center gap-4 mt-4', 'px-4 md:px-8 pt-4 md:pt-6', 'border-t border-vega-light-200 dark:border-vega-dark-200', - 'text-vega-light-400 dark:text-vega-dark-400' + 'text-vega-light-400 dark:text-vega-dark-400 text-sm' ); - const isHostedWalletSelected = connector instanceof RestConnector; return (
- {isHostedWalletSelected ? ( -

- {t('For demo purposes get a ')} - - {t('hosted wallet')} - - {t(', or for the real experience create a wallet in the ')} - - {t('Vega wallet app')} - -

- ) : ( + + {t('About the Vega wallet')}{' '} + + + {ExternalLinks.VEGA_WALLET_BROWSER_LIST && ( <> - - {t('Get a Vega Wallet')} - {' | '} - {DocsLinks && ( - - {t('Having trouble?')} - - )} + + {t('Supported browsers')}{' '} + + )}
); }; + +export const ChromeIcon = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export const MozillaIcon = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export const BrowserIcon = () => { + const { MOZILLA_EXTENSION_URL, CHROME_EXTENSION_URL } = useEnvironment(); + const isItChrome = window.navigator.userAgent.includes('Chrome'); + const isItMozilla = + window.navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + return ( +
+ {!isItChrome && !isItMozilla ? ( + <> + + + {' '} + + + + + ) : ( + <> + {isItChrome && } + {isItMozilla && } + + )} +
+ ); +}; diff --git a/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx b/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx index 5503086e8..700d4a46b 100644 --- a/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx +++ b/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx @@ -1,10 +1,4 @@ -import { - act, - fireEvent, - render, - screen, - waitFor, -} from '@testing-library/react'; +import { act, fireEvent, render, screen } from '@testing-library/react'; import type { MockedResponse } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing'; import { VegaWalletProvider } from '../provider'; @@ -18,7 +12,6 @@ import { ClientErrors, InjectedConnector, JsonRpcConnector, - RestConnector, ViewConnector, WalletError, } from '../connectors'; @@ -36,6 +29,12 @@ const mockUpdateDialogOpen = jest.fn(); const mockCloseVegaDialog = jest.fn(); jest.mock('@vegaprotocol/environment'); +let mockIsDesktopRunning = true; +jest.mock('../use-is-wallet-service-running', () => ({ + useIsWalletServiceRunning: jest + .fn() + .mockImplementation(() => mockIsDesktopRunning), +})); // @ts-ignore ignore mock implementation useEnvironment.mockImplementation(() => ({ @@ -53,12 +52,10 @@ let defaultProps: VegaConnectDialogProps; const INITIAL_KEY = 'some-key'; -const rest = new RestConnector(); const jsonRpc = new JsonRpcConnector(); const view = new ViewConnector(INITIAL_KEY); const injected = new InjectedConnector(); const connectors = { - rest, jsonRpc, view, injected, @@ -104,6 +101,11 @@ function generateJSX(props?: Partial) { } describe('VegaConnectDialog', () => { + let navigatorGetter: jest.SpyInstance; + beforeEach(() => { + jest.clearAllMocks(); + navigatorGetter = jest.spyOn(window.navigator, 'userAgent', 'get'); + }); it('displays a list of connection options', async () => { const { container, rerender } = render(generateJSX()); expect(container).toBeEmptyDOMElement(); @@ -112,133 +114,23 @@ describe('VegaConnectDialog', () => { expect(list).toBeInTheDocument(); expect(list.children).toHaveLength(3); expect(screen.getByTestId('connector-jsonRpc')).toHaveTextContent( - 'Connect Vega wallet' - ); - expect(screen.getByTestId('connector-rest')).toHaveTextContent( - 'Hosted Fairground wallet' - ); - expect(screen.getByTestId('connector-view')).toHaveTextContent( - 'View as vega user' + 'Use the Desktop App/CLI' ); }); it('displays browser wallet option if detected on window object', async () => { + navigatorGetter.mockReturnValue('Chrome'); mockBrowserWallet(); render(generateJSX()); const list = await screen.findByTestId('connectors-list'); - expect(list.children).toHaveLength(4); + expect(list.children).toHaveLength(3); expect(screen.getByTestId('connector-injected')).toHaveTextContent( - 'Connect Web wallet' + 'Connect' ); + clearBrowserWallet(); }); - describe('RestConnector', () => { - it('connects', async () => { - const spy = jest - .spyOn(connectors.rest, 'authenticate') - .mockImplementation(() => - Promise.resolve({ success: true, error: null }) - ); - jest - .spyOn(connectors.rest, 'connect') - .mockImplementation(() => - Promise.resolve([{ publicKey: 'pubkey', name: 'test key 1' }]) - ); - render(generateJSX()); - // Switches to rest form - fireEvent.click(await screen.findByText('Hosted Fairground wallet')); - - // Client side validation - fireEvent.submit(screen.getByTestId('rest-connector-form')); - expect(spy).not.toHaveBeenCalled(); - await waitFor(() => { - expect(screen.getAllByText('Required')).toHaveLength(2); - }); - - const fields = fillInForm(); - - // Wait for auth method to be called - await act(async () => { - fireEvent.submit(screen.getByTestId('rest-connector-form')); - }); - await waitFor(() => { - expect(spy).toHaveBeenCalledWith(fields); - - expect(mockCloseVegaDialog).toHaveBeenCalled(); - }); - }); - - it('handles failed connection', async () => { - const errMessage = 'Error message'; - // Error from service - let spy = jest - .spyOn(connectors.rest, 'authenticate') - .mockImplementation(() => - Promise.resolve({ success: false, error: errMessage }) - ); - - render(generateJSX()); - // Switches to rest form - fireEvent.click(await screen.findByText('Hosted Fairground wallet')); - - const fields = fillInForm(); - fireEvent.submit(screen.getByTestId('rest-connector-form')); - - // Wait for auth method to be called - await act(async () => { - fireEvent.submit(screen.getByTestId('rest-connector-form')); - }); - - expect(spy).toHaveBeenCalledWith(fields); - - expect(screen.getByTestId('form-error')).toHaveTextContent(errMessage); - expect(mockUpdateDialogOpen).not.toHaveBeenCalled(); - - // Fetch failed due to wallet not running - spy = jest - .spyOn(connectors.rest, 'authenticate') - // @ts-ignore test fetch failed with typeerror - .mockImplementation(() => - Promise.reject(new TypeError('fetch failed')) - ); - - await act(async () => { - fireEvent.submit(screen.getByTestId('rest-connector-form')); - }); - - expect(screen.getByTestId('form-error')).toHaveTextContent( - `Wallet not running at ${mockHostedWalletUrl}` - ); - - // Reject eg non 200 results - spy = jest - .spyOn(connectors.rest, 'authenticate') - // @ts-ignore test fetch failed with typeerror - .mockImplementation(() => Promise.reject(new Error('Error!'))); - - await act(async () => { - fireEvent.submit(screen.getByTestId('rest-connector-form')); - }); - - expect(screen.getByTestId('form-error')).toHaveTextContent( - 'Authentication failed' - ); - }); - - const fillInForm = () => { - const walletValue = 'test-wallet'; - fireEvent.change(screen.getByTestId('rest-wallet'), { - target: { value: walletValue }, - }); - const passphraseValue = 'test-passphrase'; - fireEvent.change(screen.getByTestId('rest-passphrase'), { - target: { value: passphraseValue }, - }); - return { wallet: walletValue, passphrase: passphraseValue }; - }; - }); - describe('JsonRpcConnector', () => { const delay = 100; let spyOnCheckCompat: jest.SpyInstance; @@ -373,83 +265,36 @@ describe('VegaConnectDialog', () => { expect(screen.getByText('An unknown error occurred')).toBeInTheDocument(); }); + it('handles wallet running is not detected', async () => { + mockIsDesktopRunning = false; + render(generateJSX()); + expect(await screen.findByTestId('connector-jsonRpc')).toBeDisabled(); + }); + + it('Mozilla logo should be rendered', async () => { + navigatorGetter.mockReturnValue('Firefox'); + render(generateJSX()); + expect(await screen.findByTestId('mozilla-logo')).toBeInTheDocument(); + }); + + it('Chrome logo should be rendered', async () => { + navigatorGetter.mockReturnValue('Chrome'); + render(generateJSX()); + expect(await screen.findByTestId('chrome-logo')).toBeInTheDocument(); + }); + + it('Chrome and Firefox logo should be rendered', async () => { + navigatorGetter.mockReturnValue('Safari'); + render(generateJSX()); + expect(await screen.findByTestId('mozilla-logo')).toBeInTheDocument(); + expect(await screen.findByTestId('chrome-logo')).toBeInTheDocument(); + }); async function selectJsonRpc() { expect(await screen.findByRole('dialog')).toBeInTheDocument(); fireEvent.click(await screen.findByTestId('connector-jsonRpc')); } }); - describe('ViewOnlyConnector', () => { - const fillInForm = (address = '0'.repeat(64)) => { - fireEvent.change(screen.getByTestId('address'), { - target: { value: address }, - }); - return { address }; - }; - - it('connects', async () => { - const spy = jest.spyOn(connectors.view, 'connect'); - - render(generateJSX()); - // Switches to view form - fireEvent.click(await screen.findByText('View as vega user')); - - // Client side validation - fireEvent.submit(screen.getByTestId('view-connector-form')); - expect(spy).not.toHaveBeenCalled(); - await waitFor(() => { - expect(screen.getAllByText('Required')).toHaveLength(1); - }); - - fillInForm(); - - // Wait for auth method to be called - await act(async () => { - fireEvent.submit(screen.getByTestId('view-connector-form')); - }); - - expect(spy).toHaveBeenCalled(); - - expect(mockCloseVegaDialog).toHaveBeenCalled(); - }); - - it('ensures pubkey is of correct length', async () => { - render(generateJSX()); - // Switches to view form - fireEvent.click(await screen.findByText('View as vega user')); - - fillInForm('123'); - - // Wait for auth method to be called - await act(async () => { - fireEvent.submit(screen.getByTestId('view-connector-form')); - }); - await waitFor(() => { - expect( - screen.getAllByText('Pubkey must be 64 characters in length') - ).toHaveLength(1); - }); - }); - - it('ensures pubkey is of valid hex', async () => { - render(generateJSX()); - // Switches to view form - fireEvent.click(await screen.findByText('View as vega user')); - - fillInForm('q'.repeat(64)); - - // Wait for auth method to be called - await act(async () => { - fireEvent.submit(screen.getByTestId('view-connector-form')); - }); - await waitFor(() => { - expect(screen.getAllByText('Pubkey must be be valid hex')).toHaveLength( - 1 - ); - }); - }); - }); - describe('InjectedConnector', () => { beforeAll(() => { jest.useFakeTimers(); diff --git a/libs/wallet/src/connect-dialog/connect-dialog.tsx b/libs/wallet/src/connect-dialog/connect-dialog.tsx index 83f6707d3..95006ac49 100644 --- a/libs/wallet/src/connect-dialog/connect-dialog.tsx +++ b/libs/wallet/src/connect-dialog/connect-dialog.tsx @@ -1,43 +1,51 @@ +import classNames from 'classnames'; import { create } from 'zustand'; import { - Button, Dialog, FormGroup, Input, + Intent, + Pill, + TradingButton, VegaIcon, VegaIconNames, } from '@vegaprotocol/ui-toolkit'; +import type { ReactNode } from 'react'; import { useCallback, useState } from 'react'; import type { WalletClientError } from '@vegaprotocol/wallet-client'; import { t } from '@vegaprotocol/i18n'; import type { VegaConnector } from '../connectors'; -import { InjectedConnector } from '../connectors'; -import { ViewConnector } from '../connectors'; -import { JsonRpcConnector, RestConnector } from '../connectors'; -import { RestConnectorForm } from './rest-connector-form'; -import { JsonRpcConnectorForm } from './json-rpc-connector-form'; -import { Networks, useEnvironment } from '@vegaprotocol/environment'; import { + InjectedConnector, + JsonRpcConnector, + ViewConnector, +} from '../connectors'; +import { JsonRpcConnectorForm } from './json-rpc-connector-form'; +import { ViewConnectorForm } from './view-connector-form'; +import { useEnvironment } from '@vegaprotocol/environment'; +import { + BrowserIcon, ConnectDialogContent, ConnectDialogFooter, ConnectDialogTitle, } from './connect-dialog-elements'; import type { Status as JsonRpcStatus } from '../use-json-rpc-connect'; -import type { Status as InjectedStatus } from '../use-injected-connector'; import { useJsonRpcConnect } from '../use-json-rpc-connect'; -import { ViewConnectorForm } from './view-connector-form'; +import type { Status as InjectedStatus } from '../use-injected-connector'; +import { useInjectedConnector } from '../use-injected-connector'; import { useChainIdQuery } from './__generated__/ChainId'; import { useVegaWallet } from '../use-vega-wallet'; -import { useInjectedConnector } from '../use-injected-connector'; import { InjectedConnectorForm } from './injected-connector-form'; +import { isBrowserWalletInstalled } from '../utils'; +import { useIsWalletServiceRunning } from '../use-is-wallet-service-running'; export const CLOSE_DELAY = 1700; type Connectors = { [key: string]: VegaConnector }; -export type WalletType = 'injected' | 'jsonRpc' | 'rest' | 'view'; +export type WalletType = 'injected' | 'jsonRpc' | 'view'; export interface VegaConnectDialogProps { connectors: Connectors; - riskMessage?: React.ReactNode; + riskMessage?: ReactNode; } export interface VegaWalletDialogStore { @@ -109,9 +117,9 @@ const ConnectDialogContainer = ({ }: { connectors: Connectors; appChainId: string; - riskMessage?: React.ReactNode; + riskMessage?: ReactNode; }) => { - const { VEGA_WALLET_URL, VEGA_ENV, HOSTED_WALLET_URL } = useEnvironment(); + const { VEGA_WALLET_URL } = useEnvironment(); const closeDialog = useVegaWalletDialogStore( (store) => store.closeVegaWalletDialog ); @@ -135,11 +143,7 @@ const ConnectDialogContainer = ({ const handleSelect = (type: WalletType) => { const connector = connectors[type]; - - // If type is rest user has selected the hosted wallet option. So here - // we ensure that we are connecting to https://vega-hosted-wallet.on.fleek.co/ - // otherwise use walletUrl which defaults to the localhost:1789 - connector.url = type === 'rest' ? HOSTED_WALLET_URL : walletUrl; + connector.url = walletUrl; if (!connector) { // we should never get here unless connectors are not configured correctly @@ -156,6 +160,11 @@ const ConnectDialogContainer = ({ injectedConnect(connector, appChainId); } }; + const isDesktopWalletRunning = useIsWalletServiceRunning( + walletUrl, + connectors, + appChainId + ); return ( <> @@ -175,11 +184,11 @@ const ConnectDialogContainer = ({ walletUrl={walletUrl} setWalletUrl={setWalletUrl} onSelect={handleSelect} - isMainnet={VEGA_ENV === Networks.MAINNET} + isDesktopWalletRunning={isDesktopWalletRunning} /> )} - + ); }; @@ -188,52 +197,62 @@ const ConnectorList = ({ onSelect, walletUrl, setWalletUrl, - isMainnet, + isDesktopWalletRunning, }: { onSelect: (type: WalletType) => void; walletUrl: string; setWalletUrl: (value: string) => void; - isMainnet: boolean; + isDesktopWalletRunning: boolean | null; }) => { + const title = isBrowserWalletInstalled() + ? t('Connect Vega wallet') + : t('Get a Vega wallet'); + + const extendedText = ( + <> +
+ {t('Connect')} +
+ + + ); + return ( <> - {t('Connect')} - -
    -
  • - onSelect('jsonRpc')} - /> -
  • - {'vega' in window && ( -
  • + {title} +

    + {t( + 'Connect securely, deposit funds and approve or reject transactions with the Vega wallet' + )} +

    +
    +
    + {isBrowserWalletInstalled() ? ( onSelect('injected')} /> -
  • - )} - {!isMainnet && ( -
  • - onSelect('rest')} - /> -
  • - )} -
  • -
    {t('OR')}
    + ) : ( + + )} + +
    onSelect('view')} /> -
  • -
+ +
+ onSelect('jsonRpc')} + /> +
+ ); }; @@ -259,7 +278,7 @@ const SelectedForm = ({ }; reset: () => void; onConnect: () => void; - riskMessage?: React.ReactNode; + riskMessage?: ReactNode; }) => { if (connector instanceof InjectedConnector) { return ( @@ -274,24 +293,6 @@ const SelectedForm = ({ ); } - if (connector instanceof RestConnector) { - return ( - <> - - {t('Connect')} -
- -
- - ); - } - if (connector instanceof JsonRpcConnector) { return ( ); } - if (connector instanceof ViewConnector) { return ( ); } - throw new Error('No connector selected'); }; +const GetWallet = () => { + const { MOZILLA_EXTENSION_URL, CHROME_EXTENSION_URL } = useEnvironment(); + const isItChrome = window.navigator.userAgent.includes('Chrome'); + const isItMozilla = + window.navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + const onClick = () => { + if (isItMozilla) { + window.open(MOZILLA_EXTENSION_URL, '_blank'); + return; + } + if (isItChrome) { + window.open(CHROME_EXTENSION_URL, '_blank'); + } + }; + + const buttonContent = ( + <> +
+ {t('Get the Vega Wallet')} + + ALPHA + +
+ + + ); + + return !isItChrome && !isItMozilla ? ( +
+ {buttonContent} +
+ ) : ( + + {buttonContent} + + ); +}; + const ConnectionOption = ({ + disabled, type, text, onClick, + icon, }: { type: WalletType; - text: string; + text: string | ReactNode; onClick: () => void; + disabled?: boolean; + icon?: ReactNode; }) => { return ( - + {text} + ); }; const CustomUrlInput = ({ walletUrl, setWalletUrl, + isDesktopWalletRunning, + onSelect, }: { walletUrl: string; setWalletUrl: (url: string) => void; + isDesktopWalletRunning: boolean | null; + onSelect: (type: WalletType) => void; }) => { const [urlInputExpanded, setUrlInputExpanded] = useState(false); return urlInputExpanded ? ( <> -

{t('Custom wallet location')}

+
+

{t('Custom wallet location')}

+ +
-

{t('Choose wallet app to connect')}

+ onSelect('jsonRpc')} + /> ) : ( -

- {t( - 'Choose wallet app to connect, or to change port or server URL enter a ' + <> + onSelect('jsonRpc')} + /> + {isDesktopWalletRunning !== null && ( +

+ {isDesktopWalletRunning ? ( + + ) : ( + <> + + {t( + 'No running Desktop App/CLI detected. Open your app now to connect or enter a' + )} + {' '} + + + )} +

)} - {' '} - {t(' first')} -

+ ); }; diff --git a/libs/wallet/src/connect-dialog/rest-connector-form.tsx b/libs/wallet/src/connect-dialog/rest-connector-form.tsx deleted file mode 100644 index d2e90c97c..000000000 --- a/libs/wallet/src/connect-dialog/rest-connector-form.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { t } from '@vegaprotocol/i18n'; -import { Button, FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit'; -import { useState } from 'react'; -import { useForm } from 'react-hook-form'; -import type { RestConnector } from '../connectors'; -import { useVegaWallet } from '../use-vega-wallet'; - -interface FormFields { - wallet: string; - passphrase: string; -} - -interface RestConnectorFormProps { - connector: RestConnector; - onConnect: (connector: RestConnector) => void; -} - -export function RestConnectorForm({ - connector, - onConnect, -}: RestConnectorFormProps) { - const { connect } = useVegaWallet(); - const [error, setError] = useState(''); - const { - register, - handleSubmit, - formState: { errors }, - } = useForm(); - - async function onSubmit(fields: FormFields) { - const authFailedMessage = t('Authentication failed'); - try { - setError(''); - const res = await connector.authenticate({ - wallet: fields.wallet, - passphrase: fields.passphrase, - }); - - if (res.success) { - await connect(connector); - onConnect(connector); - } else { - setError(res.error || authFailedMessage); - } - } catch (err) { - if (err instanceof TypeError) { - setError(t(`Wallet not running at ${connector.url}`)); - } else if (err instanceof Error) { - setError(authFailedMessage); - } else { - setError(t('Something went wrong')); - } - } - } - - return ( -
- - - {errors.wallet?.message && ( - {errors.wallet.message} - )} - - - - {errors.passphrase?.message && ( - {errors.passphrase.message} - )} - {error && ( - - {error} - - )} - - -
- ); -} diff --git a/libs/wallet/src/connect-dialog/view-connector-form.tsx b/libs/wallet/src/connect-dialog/view-connector-form.tsx index 3c8d35ad4..661894906 100644 --- a/libs/wallet/src/connect-dialog/view-connector-form.tsx +++ b/libs/wallet/src/connect-dialog/view-connector-form.tsx @@ -1,21 +1,23 @@ import { t } from '@vegaprotocol/i18n'; import { - Button, FormGroup, Input, InputError, + Intent, + TradingButton, VegaIcon, VegaIconNames, } from '@vegaprotocol/ui-toolkit'; import { useForm } from 'react-hook-form'; import type { ViewConnector } from '../connectors'; import { useVegaWallet } from '../use-vega-wallet'; +import { ConnectDialogTitle } from './connect-dialog-elements'; interface FormFields { address: string; } -interface RestConnectorFormProps { +interface ViewConnectorFormProps { connector: ViewConnector; onConnect: (connector: ViewConnector) => void; reset?: () => void; @@ -25,7 +27,7 @@ export function ViewConnectorForm({ connector, onConnect, reset, -}: RestConnectorFormProps) { +}: ViewConnectorFormProps) { const { connect } = useVegaWallet(); const { register, @@ -51,23 +53,8 @@ export function ViewConnectorForm({ return ( <> - {reset && ( - - )} + {t('VIEW AS VEGA USER')}
-

- {t('VIEW AS VEGA USER')} -

{t( 'Browse from the perspective of another Vega user in read-only mode.' @@ -87,14 +74,25 @@ export function ViewConnectorForm({ {errors.address.message} )} - + + {reset && ( +

+ +
+ )}
); diff --git a/libs/wallet/src/connectors/index.ts b/libs/wallet/src/connectors/index.ts index a1d771177..37b96be0f 100644 --- a/libs/wallet/src/connectors/index.ts +++ b/libs/wallet/src/connectors/index.ts @@ -1,5 +1,4 @@ export * from './vega-connector'; -export * from './rest-connector'; export * from './injected-connector'; export * from './json-rpc-connector'; export * from './view-connector'; diff --git a/libs/wallet/src/connectors/json-rpc-connector.ts b/libs/wallet/src/connectors/json-rpc-connector.ts index 7a1186b48..58c698d94 100644 --- a/libs/wallet/src/connectors/json-rpc-connector.ts +++ b/libs/wallet/src/connectors/json-rpc-connector.ts @@ -40,6 +40,7 @@ export class JsonRpcConnector implements VegaConnector { address: cfg.url, token: cfg.token ?? undefined, onTokenChange: (token) => { + this.token = token; setConfig({ token, connector: 'jsonRpc', @@ -55,12 +56,14 @@ export class JsonRpcConnector implements VegaConnector { this.client = new WalletClient({ address: url, token: this.token ?? undefined, - onTokenChange: (token) => + onTokenChange: (token) => { + this.token = token; setConfig({ token, url, connector: 'jsonRpc', - }), + }); + }, }); } get url() { diff --git a/libs/wallet/src/connectors/rest-connector.ts b/libs/wallet/src/connectors/rest-connector.ts deleted file mode 100644 index f7dc1b3be..000000000 --- a/libs/wallet/src/connectors/rest-connector.ts +++ /dev/null @@ -1,284 +0,0 @@ -import * as Sentry from '@sentry/react'; -import { clearConfig, getConfig, setConfig } from '../storage'; -import type { Transaction, VegaConnector } from './vega-connector'; -import { WalletError } from './vega-connector'; -import { z } from 'zod'; -import { t } from '@vegaprotocol/i18n'; - -type TransactionError = - | { - errors: { - [key: string]: string[]; - }; - details?: string[]; - } - | { - error: string; - details?: string[]; - }; - -const VERSION = 'v1'; - -// Perhaps there should be a default ConnectorConfig that others can extend off. Do all connectors -// need to use local storage, I don't think so... - -enum Endpoints { - Auth = 'auth/token', - Command = 'command/sync', - Keys = 'keys', -} - -export const AuthTokenSchema = z.object({ - token: z.string(), -}); - -export const TransactionResponseSchema = z.object({ - txHash: z.string(), - tx: z.object({ - signature: z.object({ - value: z.string(), - }), - }), - sentAt: z.string(), - receivedAt: z.string(), -}); -export type V1TransactionResponse = z.infer; - -export const GetKeysSchema = z.object({ - keys: z.array( - z.object({ - algorithm: z.object({ - name: z.string(), - version: z.number(), - }), - index: z.number(), - meta: z.array( - z.object({ - key: z.string(), - value: z.string(), - }) - ), - pub: z.string(), - tainted: z.boolean(), - }) - ), -}); - -/** - * Connector for using the Vega Wallet Service rest api, requires authentication to get a session token - */ -export class RestConnector implements VegaConnector { - url: string | null = null; - token: string | null = null; - - constructor() { - const cfg = getConfig(); - if (cfg) { - this.token = cfg.token; - this.url = cfg.url; - } - } - - async sessionActive() { - return Boolean(this.token); - } - - async authenticate(params: { wallet: string; passphrase: string }) { - try { - const res = await this.request(Endpoints.Auth, { - method: 'post', - body: JSON.stringify(params), - }); - - if (res.status === 403) { - return { success: false, error: t('Invalid credentials') }; - } - - if (res.error) { - return { success: false, error: res.error }; - } - - const data = AuthTokenSchema.parse(res.data); - - // Store the token, and other things for later - setConfig({ - connector: 'rest', - token: data.token, - url: this.url, - }); - this.token = data.token; - - return { success: true, error: null }; - } catch (err) { - return { success: false, error: 'Authentication failed' }; - } - } - - async connect() { - try { - const res = await this.request(Endpoints.Keys, { - method: 'get', - headers: { - authorization: `Bearer ${this.token}`, - }, - }); - - if (res.error) { - return null; - } - - const data = GetKeysSchema.parse(res.data); - - return data.keys.map((k) => { - const nameMeta = k.meta.find((m) => m.key === 'name'); - return { - publicKey: k.pub, - name: nameMeta ? nameMeta.value : t('No name'), - }; - }); - } catch (err) { - // keysGet failed, its likely that the session has expired so remove the token from storage - clearConfig(); - return null; - } - } - - async disconnect() { - try { - await this.request(Endpoints.Auth, { - method: 'delete', - headers: { - authorization: `Bearer ${this.token}`, - }, - }); - } catch (err) { - Sentry.captureException(err); - } finally { - // Always clear config, if authTokenDelete fails the user still tried to - // connect so clear the config (and containing token) from storage - clearConfig(); - } - } - - async sendTx(pubKey: string, transaction: Transaction) { - const body = { - pubKey, - propagate: true, - ...transaction, - }; - const res = await this.request(Endpoints.Command, { - method: 'post', - body: JSON.stringify(body), - headers: { - authorization: `Bearer ${this.token}`, - }, - }); - - // User rejected - if (res.status === 401) { - return null; - } - - if (res.error) { - throw new WalletError(res.error, 1, res.details); - } - - const data = TransactionResponseSchema.parse(res.data); - - // Make return value match that of v2 service - return { - transactionHash: data.txHash, - signature: data.tx.signature.value, - receivedAt: data.receivedAt, - sentAt: data.sentAt, - }; - } - - /** Parse more complex error object into a single string */ - private parseError(err: TransactionError): string { - if ('error' in err) { - return err.error; - } - - if ('errors' in err) { - const result = Object.entries(err.errors) - .map((entry) => { - return `${entry[0]}: ${entry[1].join(' | ')}`; - }) - .join(', '); - return result; - } - - return t('Something went wrong'); - } - - /** Parse error details array into a single string */ - private parseErrorDetails(err: TransactionError): string | null { - if (err.details && err.details.length > 0) { - return err.details.join(', '); - } - - return null; - } - - private async request( - endpoint: Endpoints, - options: RequestInit - ): Promise<{ - status?: number; - data?: unknown; - error?: string; - details?: string; - }> { - try { - const fetchResult = await fetch( - `${this.url}/api/${VERSION}/${endpoint}`, - { - ...options, - headers: { - ...options.headers, - 'Content-Type': 'application/json', - }, - } - ); - - if (!fetchResult.ok) { - const errorData = await fetchResult.json(); - const error = this.parseError(errorData); - const errorDetails = this.parseErrorDetails(errorData); - - if (errorDetails) { - return { - status: fetchResult.status, - error, - details: errorDetails, - }; - } - - return { - status: fetchResult.status, - error, - }; - } - - // auth/token delete doesnt return json - if (endpoint === 'auth/token' && options.method === 'delete') { - const textResult = await fetchResult.text(); - return { - status: fetchResult.status, - data: textResult, - }; - } else { - const jsonResult = await fetchResult.json(); - return { - status: fetchResult.status, - data: jsonResult, - }; - } - } catch (err) { - return { - error: 'No wallet detected', - }; - } - } -} diff --git a/libs/wallet/src/index.ts b/libs/wallet/src/index.ts index dd309bcf9..3d43a0b18 100644 --- a/libs/wallet/src/index.ts +++ b/libs/wallet/src/index.ts @@ -13,6 +13,5 @@ export * from './provider'; export * from './connect-dialog'; export * from './utils'; export * from './storage'; -export * from './is-browser-wallet-installed'; export * from './__generated__/TransactionResult'; export * from './__generated__/WithdrawalApproval'; diff --git a/libs/wallet/src/is-browser-wallet-installed.ts b/libs/wallet/src/is-browser-wallet-installed.ts deleted file mode 100644 index b16e05d99..000000000 --- a/libs/wallet/src/is-browser-wallet-installed.ts +++ /dev/null @@ -1 +0,0 @@ -export const isBrowserWalletInstalled = () => Boolean(window.vega); diff --git a/libs/wallet/src/provider.spec.tsx b/libs/wallet/src/provider.spec.tsx index 5f7c35816..985f34f28 100644 --- a/libs/wallet/src/provider.spec.tsx +++ b/libs/wallet/src/provider.spec.tsx @@ -1,7 +1,6 @@ import { act, renderHook } from '@testing-library/react'; import type { Transaction } from './connectors'; -import { ViewConnector } from './connectors'; -import { RestConnector } from './connectors'; +import { ViewConnector, JsonRpcConnector } from './connectors'; import { useVegaWallet } from './use-vega-wallet'; import { VegaWalletProvider } from './provider'; import { LocalStorage } from '@vegaprotocol/utils'; @@ -10,7 +9,7 @@ import { WALLET_KEY } from './storage'; import * as Environment from '@vegaprotocol/environment'; import * as ReactHelpers from '@vegaprotocol/react-helpers'; -const restConnector = new RestConnector(); +const jsonRpcConnector = new JsonRpcConnector(); const viewConnector = new ViewConnector(); const setup = () => { @@ -30,14 +29,21 @@ describe('VegaWalletProvider', () => { { publicKey: '222', name: 'public key 2' }, ]; const spyOnConnect = jest - .spyOn(restConnector, 'connect') + .spyOn(jsonRpcConnector, 'connect') .mockImplementation(() => Promise.resolve(mockPubKeys)); const spyOnSend = jest - .spyOn(restConnector, 'sendTx') - .mockImplementation(() => Promise.resolve(null)); + .spyOn(jsonRpcConnector, 'sendTx') + .mockImplementation(() => + Promise.resolve({ + transactionHash: 'tsx', + sentAt: '', + receivedAt: '', + signature: '', + }) + ); const storageSpy = jest.spyOn(LocalStorage, 'setItem'); const spyOnDisconnect = jest - .spyOn(restConnector, 'disconnect') + .spyOn(jsonRpcConnector, 'disconnect') .mockImplementation(() => Promise.resolve()); it('connects, disconnects and retrieve keypairs', async () => { @@ -58,7 +64,7 @@ describe('VegaWalletProvider', () => { // Connect await act(async () => { - result.current.connect(restConnector); + result.current.connect(jsonRpcConnector); }); expect(spyOnConnect).toHaveBeenCalled(); expect(result.current.pubKeys).toHaveLength(mockPubKeys.length); @@ -99,7 +105,7 @@ describe('VegaWalletProvider', () => { // Connect await act(async () => { - result.current.connect(restConnector); + result.current.connect(jsonRpcConnector); result.current.selectPubKey(mockPubKeys[0].publicKey); }); expect(spyOnConnect).toHaveBeenCalled(); @@ -119,7 +125,7 @@ describe('VegaWalletProvider', () => { expect(result.current.pubKey).toBe(null); await act(async () => { - result.current.connect(restConnector); + result.current.connect(jsonRpcConnector); result.current.selectPubKey(mockPubKeys[0].publicKey); }); expect(result.current.pubKey).toBe(mockPubKeys[0].publicKey); diff --git a/libs/wallet/src/storage.ts b/libs/wallet/src/storage.ts index e8c3eedbb..50ee11494 100644 --- a/libs/wallet/src/storage.ts +++ b/libs/wallet/src/storage.ts @@ -2,7 +2,7 @@ import { LocalStorage } from '@vegaprotocol/utils'; interface ConnectorConfig { token: string | null; - connector: 'injected' | 'rest' | 'jsonRpc' | 'view'; + connector: 'injected' | 'jsonRpc' | 'view'; url: string | null; } diff --git a/libs/wallet/src/use-is-wallet-service-running.tsx b/libs/wallet/src/use-is-wallet-service-running.tsx new file mode 100644 index 000000000..4dd5ca50e --- /dev/null +++ b/libs/wallet/src/use-is-wallet-service-running.tsx @@ -0,0 +1,42 @@ +import type { VegaConnector } from './connectors'; +import { useCallback, useEffect, useState } from 'react'; +import type { JsonRpcConnector } from './connectors'; +import { ClientErrors } from './connectors'; + +export const useIsWalletServiceRunning = ( + url: string, + connectors: { [key: string]: VegaConnector }, + appChainId: string +) => { + const [run, setRun] = useState(null); + + const checkState = useCallback(async () => { + const connector = connectors['jsonRpc'] as JsonRpcConnector; + connector.url = url; + try { + await connector.checkCompat(); + const chainIdResult = await connector.getChainId(); + if (chainIdResult.chainID !== appChainId) { + throw ClientErrors.WRONG_NETWORK; + } + } catch (e) { + return false; + } + return true; + }, [connectors, url, appChainId]); + + useEffect(() => { + let interval: NodeJS.Timeout; + checkState().then((value) => { + setRun(value); + interval = setInterval(async () => { + setRun(await checkState()); + }, 1000 * 10); + }); + return () => { + clearInterval(interval); + }; + }, [checkState]); + + return run; +}; diff --git a/libs/wallet/src/utils.ts b/libs/wallet/src/utils.ts index 47444c9a8..1b0ce0a19 100644 --- a/libs/wallet/src/utils.ts +++ b/libs/wallet/src/utils.ts @@ -63,3 +63,5 @@ export const normalizeTransfer = >( oneOff: {}, }; }; + +export const isBrowserWalletInstalled = () => Boolean(window.vega); diff --git a/specs/0002-WCON-connect_vega_wallet.md b/specs/0002-WCON-connect_vega_wallet.md index f48fdce3f..d430e34bd 100644 --- a/specs/0002-WCON-connect_vega_wallet.md +++ b/specs/0002-WCON-connect_vega_wallet.md @@ -6,9 +6,24 @@ When looking to use Vega via a user interface e.g. Dapp (Decentralized web App), - If the app loads and already has a connection it can restore "eagerly" (without the user having to click connect) it **could** do so - **must** select a connection method / wallet type: (0002-WCON-002) -- if Rest: +- If I don't have the browser wallet installed, I see "get started" on the connect button, otherwise I see "Connect" there. (0002-WCON-002) +- If I don't have the browser wallet installed, when I press "Get started" I can see immediately a way to get the Vega wallet browser extension. (0002-WCON-0010) +- If I do have the browser wallet installed, I can easily choose to connect to it. (0002-WCON-0011) +- If the desktop wallet or CLI is detected as running, I can see that and choose to connect with my desktop / CLI wallet. (0002-WCON-0012) +- If there is not running desktop wallet or CLI detected, I can see that I need to open my wallet. (0002-WCON-0013) +- I can find out more about supported browsers i.e. there is a link (Issue "List compatible browsers" vegawallet-browser#360 has to be implemented). (0002-WCON-0013) +- I can find out more about the Vega Wallet and see what "other" versions there are i.e. there is a link to the page on the website (currently - https://vega.xyz/wallet#overview). (0002-WCON-0014) +- Browser wallet: + + - The browser extension you need is automatically detected if you are using Chrome or Firefox, presenting the specific call to action to install that browser extension in a visible way e.g. with a Chrome or Firefox icon. (0002-WCON-041) + - The browser extension store opens in a new tab on the Vega Wallet extension page (Chrome or Firefox). (0002-WCON-042) + - When the browser I am using is not Firefox or Chrome, there is a way to download the browser extension anyway but at my own risk i.e. I can see options for both the chrome and firefox extensions in the CTA. (0002-WCON-043) + - There is a way to understand the browser extension is an Alpha release e.g. there is a label / description. (0002-WCON-044) + +- if I choose Desktop/CLI App ("jsonRpc" type): - **must** have the option to input a non-default Wallet location (0002-WCON-003) + - If I select to enter a custom wallet location, there is a way to go back to the default view i.e. a back button or similar. - **must** submit attempt to connect to wallet (0002-WCON-005) - if the dapp DOES already have a permission with the wallet: **must** see that wallet is connected (0002-WCON-007) note: if the user want to connect to a different wallet to the one that they were previously connected with, they will have to hit logout. @@ -25,16 +40,6 @@ When looking to use Vega via a user interface e.g. Dapp (Decentralized web App), - if the dapp is unable to connect for technical reason (e.g. CORS): **must** see an explanation of the error, and a method of fixing the issue (0002-WCON-016) -- ~~Browser wallet~~ `not available yet` -- Fairground hosted wallet - - **must** only be be shown this option if the dapp is connected to fairground (0002-WCON-039) - - **must** input a wallet name (0002-WCON-017) - - **must** input a password (0002-WCON-018) - - if success: **must** see that the wallet is connected and details of connected key (0002-WCON-019) - - if failure: **must** see reason for failure (0002-WCON-020) - - _note: the fairground hosted wallet is configured to automatically approve connections from dapps so there is no need for key selection._ -- **must** have the option to select a different method / wallet type if I change my mind (0002-WCON-021) - ... so I can use the interface to read data about my key/party or request my wallet to broadcast transactions to a Vega network. ## Disconnect wallet diff --git a/tools/ci/define-dist-variables.py b/tools/ci/define-dist-variables.py index dbf6ac778..bacaeb577 100644 --- a/tools/ci/define-dist-variables.py +++ b/tools/ci/define-dist-variables.py @@ -14,8 +14,13 @@ domain = 'vega.rocks' bucket_name = '' if 'release/' in args.github_ref: - # remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading) - env_name = args.github_ref.replace('refs/heads/release/', '').split('-')[0] + if 'mainnet-mirror' in args.github_ref: + env_name = 'mainnet-mirror' + if 'validators-testnet' in args.github_ref: + env_name = 'validators-testnet' + else: + # remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading) + env_name = args.github_ref.replace('refs/heads/release/', '').split('-')[0] elif 'develop' in args.github_ref: env_name = 'stagnet1' apps_deployed_from_develop_to_mainnet = {