Compare commits
26
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4a2e7d604 | ||
|
|
6c7d4808e1 | ||
|
|
3c931e180e | ||
|
|
d760e4cc8e | ||
|
|
6f59083169 | ||
|
|
f0b953fb71 | ||
|
|
d46af0009a | ||
|
|
cda3a5050e | ||
|
|
76048fea66 | ||
|
|
5bbb592b01 | ||
|
|
722897fdc6 | ||
|
|
961c6e2ee6 | ||
|
|
9e413e66ae | ||
|
|
c46d701361 | ||
|
|
0d39c2354c | ||
|
|
ad6f0c5798 | ||
|
|
38d13085fb | ||
|
|
c0f4278b81 | ||
|
|
b2777043b4 | ||
|
|
4d19b55096 | ||
|
|
7ea7362a7d | ||
|
|
bbfe42ddb1 | ||
|
|
b163af3e8a | ||
|
|
e6b3ff456d | ||
|
|
00dbb7dd60 | ||
|
|
82df401611 |
@@ -1,36 +0,0 @@
|
|||||||
name: Cypress Console tests -- live environment
|
|
||||||
|
|
||||||
# This workflow runs using provided url
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
url:
|
|
||||||
description: 'Url'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
cypress-run:
|
|
||||||
name: Run Cypress Trading tests -- live environment
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Use Node.js 20
|
|
||||||
id: Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- name: Run Cypress tests
|
|
||||||
uses: cypress-io/github-action@v4
|
|
||||||
with:
|
|
||||||
browser: chrome
|
|
||||||
record: true
|
|
||||||
project: ./apps/trading-e2e
|
|
||||||
config: baseUrl=${{ github.event.inputs.url }}
|
|
||||||
env: grepTags=@live
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -12,7 +12,6 @@ on:
|
|||||||
options:
|
options:
|
||||||
- explorer-e2e
|
- explorer-e2e
|
||||||
- governance-e2e
|
- governance-e2e
|
||||||
- trading-e2e
|
|
||||||
tags:
|
tags:
|
||||||
description: 'Test tags to run'
|
description: 'Test tags to run'
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ jobs:
|
|||||||
uses: ./.github/workflows/cypress-run.yml
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
|
projects: '["explorer-e2e","governance-e2e"]'
|
||||||
tags: '@smoke @regression @slow'
|
tags: '@smoke @regression @slow'
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
turnTelemetryOff,
|
||||||
|
setRiskAccepted,
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
clickOnValidatorFromList,
|
clickOnValidatorFromList,
|
||||||
@@ -57,6 +58,7 @@ context(
|
|||||||
// 1002-STKE-002, 1002-STKE-032
|
// 1002-STKE-002, 1002-STKE-032
|
||||||
before('visit staking tab and connect vega wallet', function () {
|
before('visit staking tab and connect vega wallet', function () {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
|
setRiskAccepted();
|
||||||
ethereumWalletConnect();
|
ethereumWalletConnect();
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
turnTelemetryOff,
|
||||||
|
setRiskAccepted,
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
stakingPageAssociateTokens,
|
stakingPageAssociateTokens,
|
||||||
@@ -57,6 +58,7 @@ context(
|
|||||||
function () {
|
function () {
|
||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
turnTelemetryOff();
|
turnTelemetryOff();
|
||||||
|
setRiskAccepted();
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
waitForSpinner();
|
waitForSpinner();
|
||||||
|
|||||||
@@ -79,23 +79,23 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have information on active nodes', function () {
|
it.skip('should have information on active nodes', function () {
|
||||||
cy.getByTestId('node-information')
|
cy.getByTestId('node-information')
|
||||||
.first()
|
.first()
|
||||||
.should('contain.text', '1')
|
.should('contain.text', '2')
|
||||||
.and('contain.text', 'active nodes');
|
.and('contain.text', 'active nodes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have information on consensus nodes', function () {
|
it.skip('should have information on consensus nodes', function () {
|
||||||
cy.getByTestId('node-information')
|
cy.getByTestId('node-information')
|
||||||
.last()
|
.last()
|
||||||
.should('contain.text', '1')
|
.should('contain.text', '2')
|
||||||
.and('contain.text', 'consensus nodes');
|
.and('contain.text', 'consensus nodes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain link to specific validators', function () {
|
it('should contain link to specific validators', function () {
|
||||||
cy.getByTestId('validators')
|
cy.getByTestId('validators')
|
||||||
.should('have.length', '1')
|
.should('have.length', '2')
|
||||||
.each(($validator) => {
|
.each(($validator) => {
|
||||||
cy.wrap($validator).find('a').should('have.attr', 'href');
|
cy.wrap($validator).find('a').should('have.attr', 'href');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
import {
|
import {
|
||||||
navigation,
|
navigation,
|
||||||
|
setRiskAccepted,
|
||||||
verifyPageHeader,
|
verifyPageHeader,
|
||||||
verifyTabHighlighted,
|
verifyTabHighlighted,
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
@@ -187,6 +188,7 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
before('connect wallets and click on validator', function () {
|
before('connect wallets and click on validator', function () {
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.visit('/validators');
|
cy.visit('/validators');
|
||||||
|
setRiskAccepted();
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
clickOnValidatorFromList(0);
|
clickOnValidatorFromList(0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { truncateByChars } from '@vegaprotocol/utils';
|
import { truncateByChars } from '@vegaprotocol/utils';
|
||||||
import { waitForSpinner } from '../../support/common.functions';
|
import {
|
||||||
|
setRiskAccepted,
|
||||||
|
waitForSpinner,
|
||||||
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
vegaWalletFaucetAssetsWithoutCheck,
|
vegaWalletFaucetAssetsWithoutCheck,
|
||||||
vegaWalletTeardown,
|
vegaWalletTeardown,
|
||||||
@@ -11,7 +14,6 @@ const connectButton = 'connect-vega-wallet';
|
|||||||
const getVegaLink = 'link';
|
const getVegaLink = 'link';
|
||||||
const dialog = '[role="dialog"]:visible';
|
const dialog = '[role="dialog"]:visible';
|
||||||
const dialogHeader = 'dialog-title';
|
const dialogHeader = 'dialog-title';
|
||||||
const walletDialogHeader = 'wallet-dialog-title';
|
|
||||||
const connectorsList = 'connectors-list';
|
const connectorsList = 'connectors-list';
|
||||||
const dialogCloseBtn = 'dialog-close';
|
const dialogCloseBtn = 'dialog-close';
|
||||||
const accountNo = 'vega-account-truncated';
|
const accountNo = 'vega-account-truncated';
|
||||||
@@ -34,6 +36,7 @@ context(
|
|||||||
() => {
|
() => {
|
||||||
before('visit token home page', () => {
|
before('visit token home page', () => {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
|
setRiskAccepted();
|
||||||
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -63,17 +66,12 @@ context(
|
|||||||
|
|
||||||
it('should have Connect Vega header visible', () => {
|
it('should have Connect Vega header visible', () => {
|
||||||
cy.get(dialog).within(() => {
|
cy.get(dialog).within(() => {
|
||||||
cy.getByTestId(walletDialogHeader)
|
cy.getByTestId(connectorsList)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.and('have.text', 'Get a Vega wallet');
|
.and(
|
||||||
});
|
'have.text',
|
||||||
});
|
'Get the Vega WalletGet MetaMask>_Command Line WalletView as public key'
|
||||||
|
);
|
||||||
it('should have jsonRpc and hosted connection options visible on list', function () {
|
|
||||||
cy.getByTestId(connectorsList).within(() => {
|
|
||||||
cy.getByTestId('connector-jsonRpc')
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'Use the Desktop App/CLI');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,7 +86,6 @@ context(
|
|||||||
before('connect vega wallet', function () {
|
before('connect vega wallet', function () {
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.wait('@ChainId');
|
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletTeardown();
|
vegaWalletTeardown();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ export function turnTelemetryOff() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setRiskAccepted() {
|
||||||
|
cy.window().then((win) =>
|
||||||
|
win.localStorage.setItem('vega_wallet_risk_accepted', 'true')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function dissociateFromSecondWalletKey() {
|
export function dissociateFromSecondWalletKey() {
|
||||||
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
||||||
cy.getByTestId('vega-in-wallet')
|
cy.getByTestId('vega-in-wallet')
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
const { token, staking, vesting } = useContracts();
|
const { token, staking, vesting } = useContracts();
|
||||||
const setAssociatedBalances = useRefreshAssociatedBalances();
|
const setAssociatedBalances = useRefreshAssociatedBalances();
|
||||||
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
||||||
const vegaConnecting = useEagerConnect();
|
const vegaWalletStatus = useEagerConnect();
|
||||||
|
|
||||||
const loaded = balancesLoaded && !vegaConnecting;
|
const loaded = balancesLoaded && vegaWalletStatus !== 'connecting';
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
@@ -169,3 +169,5 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
}
|
}
|
||||||
return <Suspense fallback={loading}>{children}</Suspense>;
|
return <Suspense fallback={loading}>{children}</Suspense>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AppLoader.displayName = 'AppLoader';
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
useLinks,
|
||||||
|
DApp,
|
||||||
|
CONSOLE_REWARDS_PAGE,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
|
import {
|
||||||
|
ExternalLink,
|
||||||
|
Intent,
|
||||||
|
NotificationBanner,
|
||||||
|
VegaIcon,
|
||||||
|
VegaIconNames,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { Trans } from 'react-i18next';
|
||||||
|
import { useMatch } from 'react-router-dom';
|
||||||
|
import Routes from '../../routes/routes';
|
||||||
|
import { type ReactNode } from 'react';
|
||||||
|
|
||||||
|
const ConsoleRewardsLink = ({ children }: { children: ReactNode }) => {
|
||||||
|
const consoleLink = useLinks(DApp.Console);
|
||||||
|
return (
|
||||||
|
<ExternalLink
|
||||||
|
href={consoleLink(CONSOLE_REWARDS_PAGE)}
|
||||||
|
className="underline inline-flex gap-1 items-center"
|
||||||
|
title="Rewards in Console"
|
||||||
|
>
|
||||||
|
<span>{children}</span>
|
||||||
|
<VegaIcon size={12} name={VegaIconNames.OPEN_EXTERNAL} />
|
||||||
|
</ExternalLink>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RewardsMovedNotification = () => {
|
||||||
|
const onRewardsPage = useMatch(Routes.REWARDS);
|
||||||
|
if (!onRewardsPage) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NotificationBanner intent={Intent.Warning}>
|
||||||
|
<Trans
|
||||||
|
i18nKey="rewardsMovedNotification"
|
||||||
|
components={[<ConsoleRewardsLink>Console</ConsoleRewardsLink>]}
|
||||||
|
/>
|
||||||
|
</NotificationBanner>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
ProtocolUpgradeProposalNotification,
|
ProtocolUpgradeProposalNotification,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
|
||||||
|
|
||||||
interface AppLayoutProps {
|
interface AppLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -45,8 +46,10 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
|||||||
|
|
||||||
const NotificationsContainer = () => {
|
const NotificationsContainer = () => {
|
||||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="banners">
|
<div data-testid="banners">
|
||||||
|
<RewardsMovedNotification />
|
||||||
<ProtocolUpgradeProposalNotification
|
<ProtocolUpgradeProposalNotification
|
||||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -111,3 +111,4 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
|||||||
</ContractsContext.Provider>
|
</ContractsContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
ContractsProvider.displayName = 'ContractsProvider';
|
||||||
|
|||||||
+1
-1
@@ -288,7 +288,7 @@ describe('Consensus validators table', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
|
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
|
||||||
).toHaveTextContent('13.16%');
|
).toHaveTextContent('10.07%');
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
|
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
|
||||||
|
|||||||
+13
-3
@@ -185,15 +185,19 @@ export const ConsensusValidatorsTable = ({
|
|||||||
const { rawValidatorScore: previousEpochValidatorScore } =
|
const { rawValidatorScore: previousEpochValidatorScore } =
|
||||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||||
|
|
||||||
const overstakingPenalty = calculateOverallPenalty(
|
const overstakingPenalty = calculateOverstakedPenalty(
|
||||||
id,
|
id,
|
||||||
allNodesInPreviousEpoch
|
allNodesInPreviousEpoch
|
||||||
);
|
);
|
||||||
const totalPenalty = calculateOverstakedPenalty(
|
const totalPenalty = calculateOverallPenalty(
|
||||||
id,
|
id,
|
||||||
allNodesInPreviousEpoch
|
allNodesInPreviousEpoch
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const lastEpochDataForNode = allNodesInPreviousEpoch.find(
|
||||||
|
(node) => node.id === id
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||||
@@ -239,6 +243,12 @@ export const ConsensusValidatorsTable = ({
|
|||||||
: undefined,
|
: undefined,
|
||||||
[ValidatorFields.MULTISIG_ERROR]:
|
[ValidatorFields.MULTISIG_ERROR]:
|
||||||
multisigStatus?.showMultisigStatusError,
|
multisigStatus?.showMultisigStatusError,
|
||||||
|
[ValidatorFields.MULTISIG_PENALTY]: formatNumberPercentage(
|
||||||
|
new BigNumber(1)
|
||||||
|
.minus(lastEpochDataForNode?.rewardScore?.multisigScore ?? 1)
|
||||||
|
.times(100),
|
||||||
|
2
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -378,7 +388,6 @@ export const ConsensusValidatorsTable = ({
|
|||||||
headerTooltip: t('StakeDescription').toString(),
|
headerTooltip: t('StakeDescription').toString(),
|
||||||
cellRenderer: TotalStakeRenderer,
|
cellRenderer: TotalStakeRenderer,
|
||||||
width: 120,
|
width: 120,
|
||||||
sort: 'desc',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: ValidatorFields.PENDING_STAKE,
|
field: ValidatorFields.PENDING_STAKE,
|
||||||
@@ -400,6 +409,7 @@ export const ConsensusValidatorsTable = ({
|
|||||||
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
|
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
|
||||||
cellRenderer: VotingPowerRenderer,
|
cellRenderer: VotingPowerRenderer,
|
||||||
width: 120,
|
width: 120,
|
||||||
|
sort: 'desc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: ValidatorFields.TOTAL_PENALTIES,
|
field: ValidatorFields.TOTAL_PENALTIES,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export enum ValidatorFields {
|
|||||||
PENDING_USER_STAKE = 'pendingUserStake',
|
PENDING_USER_STAKE = 'pendingUserStake',
|
||||||
USER_STAKE_SHARE = 'userStakeShare',
|
USER_STAKE_SHARE = 'userStakeShare',
|
||||||
MULTISIG_ERROR = 'multisigError',
|
MULTISIG_ERROR = 'multisigError',
|
||||||
|
MULTISIG_PENALTY = 'multisigPenalty',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addUserDataToValidator = (
|
export const addUserDataToValidator = (
|
||||||
@@ -327,7 +328,7 @@ interface TotalPenaltiesRendererProps {
|
|||||||
overstakedAmount: string;
|
overstakedAmount: string;
|
||||||
overstakingPenalty: string;
|
overstakingPenalty: string;
|
||||||
totalPenalties: string;
|
totalPenalties: string;
|
||||||
multisigError?: boolean;
|
multisigPenalty: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,11 +347,9 @@ export const TotalPenaltiesRenderer = ({
|
|||||||
<div data-testid="overstaked-penalty-tooltip">
|
<div data-testid="overstaked-penalty-tooltip">
|
||||||
{t('overstakedPenalty')}: {data.overstakingPenalty}
|
{t('overstakedPenalty')}: {data.overstakingPenalty}
|
||||||
</div>
|
</div>
|
||||||
{data.multisigError && (
|
<div data-testid="multisig-error-tooltip">
|
||||||
<div data-testid="multisig-error-tooltip">
|
{t('multisigPenalty')}: {data.multisigPenalty}
|
||||||
{t('multisigPenalty')}: 100%
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import {
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
||||||
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||||
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
|
|
||||||
|
|
||||||
const statuses = {
|
const statuses = {
|
||||||
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
|
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
|
||||||
@@ -105,9 +104,10 @@ export const ValidatorTable = ({
|
|||||||
};
|
};
|
||||||
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
|
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
|
||||||
|
|
||||||
const multisigStatus = previousEpochData
|
const previousNodeData =
|
||||||
? getMultisigStatusInfo(previousEpochData)
|
previousEpochData?.epoch.validatorsConnection?.edges?.find(
|
||||||
: undefined;
|
(e) => e?.node.id === node.id
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -293,21 +293,15 @@ export const ValidatorTable = ({
|
|||||||
data-testid="multisig-penalty"
|
data-testid="multisig-penalty"
|
||||||
className="flex gap-2 items-baseline"
|
className="flex gap-2 items-baseline"
|
||||||
>
|
>
|
||||||
{multisigStatus?.zeroScoreNodes.find(
|
|
||||||
(n) => n.id === node.id
|
|
||||||
) ? (
|
|
||||||
<Tooltip
|
|
||||||
description={t('multisigPenaltyThisNodeIndicator')}
|
|
||||||
>
|
|
||||||
<span className="inline-block w-2 h-2 rounded-full bg-vega-red-500"></span>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
<Tooltip description={t('multisigPenaltyDescription')}>
|
<Tooltip description={t('multisigPenaltyDescription')}>
|
||||||
<span>
|
<span>
|
||||||
{formatNumberPercentage(
|
{formatNumberPercentage(
|
||||||
BigNumber(
|
new BigNumber(1)
|
||||||
multisigStatus?.showMultisigStatusError ? 100 : 0
|
.minus(
|
||||||
),
|
previousNodeData?.node.rewardScore?.multisigScore ??
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.times(100),
|
||||||
2
|
2
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
|
||||||
NX_VEGA_CONFIG_URL=''
|
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
|
||||||
NX_VEGA_ENV=CUSTOM
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
|
||||||
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|
||||||
NX_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
|
||||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
|
||||||
NX_SENTRY_DSN=https://dummy@o999999.ingest.sentry.io/9999999
|
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
|
||||||
|
|
||||||
|
|
||||||
# Expose some env vars to cypress environment for market setup
|
|
||||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
|
||||||
CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
|
|
||||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
|
||||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
CYPRESS_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
|
||||||
CYPRESS_ORACLE_PUBKEY=6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61
|
|
||||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
|
|
||||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
|
|
||||||
CYPRESS_VEGA_ENV=CUSTOM
|
|
||||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
|
||||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
|
||||||
CYPRESS_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|
||||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
|
||||||
|
|
||||||
# Cosmic elevator flags (MUST be doubled with CYPRESS_ prefix)
|
|
||||||
NX_SUCCESSOR_MARKETS=true
|
|
||||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
|
||||||
NX_VEGA_CONFIG_URL=''
|
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
|
||||||
NX_VEGA_ENV=CUSTOM
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
|
||||||
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|
||||||
NX_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
|
||||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
|
||||||
|
|
||||||
|
|
||||||
# Expose some env vars to cypress environment for market setup
|
|
||||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
|
||||||
CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
|
|
||||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
|
||||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
CYPRESS_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
|
||||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
|
|
||||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
|
|
||||||
CYPRESS_VEGA_ENV=CUSTOM
|
|
||||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
|
||||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
|
||||||
CYPRESS_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|
||||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
|
|
||||||
"ignorePatterns": ["!**/*"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
||||||
"rules": {
|
|
||||||
"cypress/unsafe-to-chain-command": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["src/plugins/index.js"],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"no-undef": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
const { defineConfig } = require('cypress');
|
|
||||||
|
|
||||||
module.exports = defineConfig({
|
|
||||||
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
|
||||||
e2e: {
|
|
||||||
setupNodeEvents(on, config) {
|
|
||||||
require('cypress-mochawesome-reporter/plugin')(on);
|
|
||||||
require('@cypress/grep/src/plugin')(config);
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
baseUrl: 'http://localhost:4200',
|
|
||||||
fileServerFolder: '.',
|
|
||||||
fixturesFolder: false,
|
|
||||||
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
|
|
||||||
supportFile: './src/support/index.js',
|
|
||||||
video: false,
|
|
||||||
videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
|
|
||||||
videoUploadOnPasses: false,
|
|
||||||
screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
|
|
||||||
chromeWebSecurity: false,
|
|
||||||
projectId: 'et4snf',
|
|
||||||
defaultCommandTimeout: 10000,
|
|
||||||
viewportWidth: 1800,
|
|
||||||
viewportHeight: 900,
|
|
||||||
responseTimeout: 50000,
|
|
||||||
requestTimeout: 20000,
|
|
||||||
retries: 1,
|
|
||||||
testIsolation: false,
|
|
||||||
experimentalMemoryManagement: true,
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
|
|
||||||
ETHEREUM_CHAIN_ID: 11155111,
|
|
||||||
TRADING_MODE_LINK:
|
|
||||||
'https://docs.vega.xyz/testnet/concepts/trading-on-vega/trading-modes#auction-type-liquidity-monitoring',
|
|
||||||
grepTags: '@regression @smoke @slow',
|
|
||||||
grepFilterSpecs: true,
|
|
||||||
grepOmitFiltered: true,
|
|
||||||
txTimeout: { timeout: 70000 },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
declare module '*.scss';
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "trading-e2e",
|
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
||||||
"sourceRoot": "apps/trading-e2e/src",
|
|
||||||
"projectType": "application",
|
|
||||||
"targets": {
|
|
||||||
"e2e": {
|
|
||||||
"executor": "@nx/cypress:cypress",
|
|
||||||
"options": {
|
|
||||||
"cypressConfig": "apps/trading-e2e/cypress.config.js",
|
|
||||||
"devServerTarget": "trading:serve"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"devServerTarget": "trading:serve:production"
|
|
||||||
},
|
|
||||||
"live": {
|
|
||||||
"devServerTarget": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"executor": "@nx/eslint:lint",
|
|
||||||
"outputs": ["{options.outputFile}"],
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": ["apps/trading-e2e/**/*.{js,ts}"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"executor": "nx:run-commands",
|
|
||||||
"outputs": [],
|
|
||||||
"options": {
|
|
||||||
"command": "yarn tsc --project ./apps/trading-e2e/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": [],
|
|
||||||
"implicitDependencies": ["trading"]
|
|
||||||
}
|
|
||||||
Vendored
-20
@@ -1,20 +0,0 @@
|
|||||||
/// <reference types="cypress" />
|
|
||||||
|
|
||||||
declare namespace Cypress {
|
|
||||||
// specify additional properties in the TestConfig object
|
|
||||||
// in our case we will add "tags" property
|
|
||||||
interface SuiteConfigOverrides {
|
|
||||||
/**
|
|
||||||
* List of tags for this test
|
|
||||||
* @example a single tag
|
|
||||||
* it('logs in', { tags: '@smoke' }, () => { ... })
|
|
||||||
* @example multiple tags
|
|
||||||
* it('works', { tags: ['@smoke', '@slow'] }, () => { ... })
|
|
||||||
*/
|
|
||||||
tags?: string | string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Cypress {
|
|
||||||
grep?: (grep?: string, tags?: string, burn?: string) => void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
|
||||||
import { selectAsset } from '../support/helpers';
|
|
||||||
|
|
||||||
const amountField = 'input[name="amount"]';
|
|
||||||
const txTimeout = Cypress.env('txTimeout');
|
|
||||||
const sepoliaUrl = Cypress.env('ETHERSCAN_URL');
|
|
||||||
const btcName = 0;
|
|
||||||
const vegaName = 4;
|
|
||||||
const btcSymbol = 'tBTC';
|
|
||||||
const vegaSymbol = 'VEGA';
|
|
||||||
const toastContent = 'toast-content';
|
|
||||||
const depositsTab = 'Deposits';
|
|
||||||
const toastCloseBtn = 'toast-close';
|
|
||||||
const completeWithdrawalBtn = 'complete-withdrawal';
|
|
||||||
const depositSubmit = 'deposit-submit';
|
|
||||||
const approveSubmit = 'approve-submit';
|
|
||||||
const dialogContent = 'dialog-content';
|
|
||||||
|
|
||||||
// Because the tests are run on a live network to optimize time, the tests are interdependent and must be run in the given order.
|
|
||||||
describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.createMarket();
|
|
||||||
cy.get('@markets').then((markets) => {
|
|
||||||
cy.wrap(markets[0]).as('market');
|
|
||||||
});
|
|
||||||
cy.setOnBoardingViewed();
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can deposit', function () {
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
|
|
||||||
// 1001-DEPO-001
|
|
||||||
// 1001-DEPO-002
|
|
||||||
// 1001-DEPO-003
|
|
||||||
// 1001-DEPO-005
|
|
||||||
// 1001-DEPO-006
|
|
||||||
// 1001-DEPO-007
|
|
||||||
// 1001-DEPO-008
|
|
||||||
// 1001-DEPO-009
|
|
||||||
// 1001-DEPO-010
|
|
||||||
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('Unknown');
|
|
||||||
selectAsset(btcName);
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
|
||||||
|
|
||||||
cy.getByTestId('approve-default').should(
|
|
||||||
'contain.text',
|
|
||||||
`Before you can make a deposit of your chosen asset, ${btcSymbol}, you need to approve its use in your Ethereum wallet`
|
|
||||||
);
|
|
||||||
cy.getByTestId(approveSubmit).click();
|
|
||||||
cy.getByTestId('approve-pending').should('exist');
|
|
||||||
cy.getByTestId('approve-confirmed').should('exist');
|
|
||||||
cy.get(amountField).focus();
|
|
||||||
cy.get(amountField).clear().type('10');
|
|
||||||
cy.getByTestId(depositSubmit).click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
`Transaction confirmedYour transaction has been confirmed.View on EtherscanDeposit 10.00 ${btcSymbol}`,
|
|
||||||
{ matchCase: false }
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastCloseBtn).click();
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
|
|
||||||
cy.highlight('deposit verification');
|
|
||||||
|
|
||||||
cy.get('[col-id="asset.symbol"]', txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
btcSymbol
|
|
||||||
);
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', btcSymbol);
|
|
||||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
|
||||||
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.should('have.length.above', 2)
|
|
||||||
.eq(1)
|
|
||||||
.parent()
|
|
||||||
.within(() => {
|
|
||||||
cy.get('[col-id="asset.symbol"]').should('have.text', btcSymbol);
|
|
||||||
cy.get('[col-id="amount"]').should('have.text', '10.00');
|
|
||||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
|
||||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.find('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can not withdrawal because of no MultiSign', function () {
|
|
||||||
// 1002-WITH-022
|
|
||||||
// 1002-WITH-023
|
|
||||||
// 0003-WTXN-011
|
|
||||||
cy.getByTestId('Withdrawals').click();
|
|
||||||
cy.getByTestId('withdraw-dialog-button').click();
|
|
||||||
selectAsset(btcName);
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
|
||||||
cy.get(amountField).focus();
|
|
||||||
cy.get(amountField).clear().type('1');
|
|
||||||
cy.getByTestId('submit-withdrawal').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Funds unlocked'
|
|
||||||
);
|
|
||||||
// cy.getByTestId(toastCloseBtn).click();
|
|
||||||
cy.highlight('withdrawals verification');
|
|
||||||
cy.getByTestId('toast-complete-withdrawal').last().click();
|
|
||||||
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Error occurredcannot estimate gas'
|
|
||||||
);
|
|
||||||
cy.getByTestId(completeWithdrawalBtn).should(
|
|
||||||
'contain.text',
|
|
||||||
'Complete withdrawal'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.updateCapsuleMultiSig();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.createMarket();
|
|
||||||
cy.get('@markets').then((markets) => {
|
|
||||||
cy.wrap(markets[0]).as('market');
|
|
||||||
});
|
|
||||||
cy.setOnBoardingViewed();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can withdrawal', function () {
|
|
||||||
// 1002-WITH-0014
|
|
||||||
// 1002-WITH-006
|
|
||||||
// 1002-WITH-009
|
|
||||||
// 1002-WITH-011
|
|
||||||
// 1002-WITH-024
|
|
||||||
// 1002-WITH-012
|
|
||||||
// 1002-WITH-013
|
|
||||||
// 1002-WITH-014
|
|
||||||
// 1002-WITH-015
|
|
||||||
// 1002-WITH-016
|
|
||||||
// 1002-WITH-017
|
|
||||||
// 1002-WITH-019
|
|
||||||
// 1002-WITH-020
|
|
||||||
// 1002-WITH-021
|
|
||||||
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
|
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
|
||||||
cy.getByTestId('Withdrawals').click();
|
|
||||||
cy.getByTestId('withdraw-dialog-button').click();
|
|
||||||
connectEthereumWallet('Unknown');
|
|
||||||
selectAsset(btcName);
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
|
||||||
cy.get(amountField).clear().type('1');
|
|
||||||
cy.getByTestId('submit-withdrawal').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Funds unlocked'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.highlight('withdrawals verification');
|
|
||||||
cy.getByTestId('toast-complete-withdrawal').click();
|
|
||||||
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Transaction confirmed'
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastContent, txTimeout)
|
|
||||||
.should('contain.text', 'Funds unlocked')
|
|
||||||
.and('contain.text', 'Your funds have been unlocked for withdrawal.')
|
|
||||||
.and(
|
|
||||||
'contain.text',
|
|
||||||
'View in block explorerYou can save your withdrawal details for extra security.'
|
|
||||||
)
|
|
||||||
.and('contain.text', 'Withdraw 1.00 tBTCComplete withdrawal');
|
|
||||||
cy.getByTestId('toast-withdrawal-details').click();
|
|
||||||
cy.getByTestId(dialogContent)
|
|
||||||
.last()
|
|
||||||
.within(() => {
|
|
||||||
cy.getByTestId('dialog-title').should(
|
|
||||||
'contain.text',
|
|
||||||
'Save withdrawal details'
|
|
||||||
);
|
|
||||||
cy.getByTestId('copy-button').should('be.visible');
|
|
||||||
cy.getByTestId('assetSource_value').should(
|
|
||||||
'have.text',
|
|
||||||
'0xb63D135B0a6854EEb765d69ca36210cC70BECAE0'
|
|
||||||
);
|
|
||||||
cy.getByTestId('amount_value').should('have.text', '100000');
|
|
||||||
cy.getByTestId('nonce_value').invoke('text').should('not.be.empty');
|
|
||||||
cy.getByTestId('signatures_value')
|
|
||||||
.invoke('text')
|
|
||||||
.should('not.be.empty');
|
|
||||||
cy.getByTestId('targetAddress_value').should(
|
|
||||||
'have.text',
|
|
||||||
ethWalletAddress
|
|
||||||
);
|
|
||||||
cy.getByTestId('creation_value').invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
cy.getByTestId('close-withdrawal-approval-dialog').click();
|
|
||||||
|
|
||||||
cy.get('.ag-center-cols-container')
|
|
||||||
.find('[col-id="status"]')
|
|
||||||
.eq(0, txTimeout)
|
|
||||||
.should('contain.text', 'Completed');
|
|
||||||
|
|
||||||
cy.get('[col-id="txHash"]', txTimeout)
|
|
||||||
.should('have.length.above', 1)
|
|
||||||
.eq(1)
|
|
||||||
.parent()
|
|
||||||
.within(() => {
|
|
||||||
cy.get('[col-id="asset.symbol"]').should('have.text', btcSymbol);
|
|
||||||
cy.get('[col-id="amount"]').should('have.text', '1.00');
|
|
||||||
cy.get('[col-id="details.receiverAddress"]')
|
|
||||||
.find('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', `${sepoliaUrl}/address/`);
|
|
||||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
|
||||||
cy.get('[col-id="withdrawnTimestamp"]').should('not.be.empty');
|
|
||||||
cy.get('[col-id="status"]').should('have.text', 'Completed');
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.find('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.getByTestId('withdraw-dialog-button').click({ force: true });
|
|
||||||
// cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '6.999');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('approved amount is less than deposit', function () {
|
|
||||||
// 1001-DEPO-006
|
|
||||||
// 1001-DEPO-007
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('Unknown');
|
|
||||||
selectAsset(btcName);
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
|
||||||
cy.contains('Deposits of tBTC not approved').should('not.exist');
|
|
||||||
cy.contains('Use maximum').should('be.visible');
|
|
||||||
cy.get(amountField).clear().type('20000000');
|
|
||||||
cy.getByTestId(depositSubmit).should('be.visible');
|
|
||||||
cy.getByTestId(depositSubmit).click();
|
|
||||||
cy.getByTestId('input-error-text').should(
|
|
||||||
'contain.text',
|
|
||||||
`You can't deposit more than you have in your Ethereum wallet`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('withdraw - delay verification', function () {
|
|
||||||
// 1001-DEPO-024
|
|
||||||
// 1002-WITH-007
|
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]', txTimeout).should('exist');
|
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('Unknown');
|
|
||||||
selectAsset(vegaName);
|
|
||||||
cy.getByTestId('approve-submit').click();
|
|
||||||
cy.getByTestId('approve-confirmed').should(
|
|
||||||
'contain.text',
|
|
||||||
'You approved deposits of up to VEGA'
|
|
||||||
);
|
|
||||||
cy.get(amountField).clear().type('10000');
|
|
||||||
cy.getByTestId('deposit-submit').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
`Your transaction has been confirmed.`,
|
|
||||||
{ matchCase: false }
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
|
|
||||||
cy.highlight('deposit verification');
|
|
||||||
|
|
||||||
cy.get('[col-id="asset.symbol"]', txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
vegaSymbol
|
|
||||||
);
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', vegaSymbol);
|
|
||||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
|
||||||
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.should('have.length.above', 2)
|
|
||||||
.eq(1)
|
|
||||||
.parent()
|
|
||||||
.within(() => {
|
|
||||||
cy.get('[col-id="asset.symbol"]').should('have.text', vegaSymbol);
|
|
||||||
cy.get('[col-id="amount"]').should('have.text', '10,000.00');
|
|
||||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
|
||||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.find('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.getByTestId('Withdrawals').click(txTimeout);
|
|
||||||
cy.getByTestId('withdraw-dialog-button').click();
|
|
||||||
selectAsset(1);
|
|
||||||
cy.get(amountField).clear().type('10000');
|
|
||||||
cy.getByTestId('DELAY_TIME_value').should('have.text', '5 days');
|
|
||||||
cy.getByTestId('submit-withdrawal').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Your funds have been unlocked'
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastCloseBtn).click();
|
|
||||||
cy.getByTestId(completeWithdrawalBtn).first().should('be.visible').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should('contain.text', 'Delayed');
|
|
||||||
cy.getByTestId('tab-withdrawals').within(() => {
|
|
||||||
cy.get('.ag-center-cols-container')
|
|
||||||
.children()
|
|
||||||
.first()
|
|
||||||
.within(() => {
|
|
||||||
cy.get('[col-id="status"]').contains(
|
|
||||||
/Delayed \(ready in (\d{1,2}:\d{2}:\d{2}:\d{2})\)/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
|
||||||
|
|
||||||
const connectEthWalletBtn = 'connect-eth-wallet-btn';
|
|
||||||
|
|
||||||
describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
// Using portfolio withdrawals tab is it requires Ethereum wallet connection
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
cy.getByTestId('Withdrawals').click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can connect', () => {
|
|
||||||
// 0004-EWAL-001
|
|
||||||
|
|
||||||
cy.getByTestId('Deposits').click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
cy.getByTestId('connect-eth-wallet-btn').click();
|
|
||||||
cy.getByTestId('web3-connector-list').should('exist');
|
|
||||||
cy.getByTestId('web3-connector-MetaMask').click();
|
|
||||||
cy.getByTestId('web3-connector-list').should('not.exist');
|
|
||||||
cy.getByTestId('tab-deposits').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('able to disconnect eth wallet', () => {
|
|
||||||
// 0004-EWAL-004
|
|
||||||
// 0004-EWAL-005
|
|
||||||
// 0004-EWAL-006
|
|
||||||
|
|
||||||
cy.getByTestId('Deposits').click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('MetaMask');
|
|
||||||
cy.getByTestId('ethereum-address').should('have.text', '0xEe7D…d94F');
|
|
||||||
cy.getByTestId('disconnect-ethereum-wallet')
|
|
||||||
.should('have.text', 'Disconnect')
|
|
||||||
.click();
|
|
||||||
cy.getByTestId(connectEthWalletBtn).should('exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { OrderType } from '@vegaprotocol/types';
|
|
||||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
|
||||||
|
|
||||||
const orderSizeField = 'order-size';
|
|
||||||
const orderPriceField = 'order-price';
|
|
||||||
const orderTIFDropDown = 'order-tif';
|
|
||||||
const placeOrderBtn = 'place-order';
|
|
||||||
|
|
||||||
export const createOrder = (order: OrderSubmission): void => {
|
|
||||||
cy.log('Placing order', order);
|
|
||||||
const { type, side, size, price, timeInForce, expiresAt } = order;
|
|
||||||
|
|
||||||
cy.getByTestId(
|
|
||||||
`order-type-${type === OrderType.TYPE_LIMIT ? 'Limit' : 'Market'}`
|
|
||||||
).click();
|
|
||||||
cy.getByTestId(`order-side-${side}`).click();
|
|
||||||
cy.getByTestId(orderSizeField).clear().type(size);
|
|
||||||
if (price) {
|
|
||||||
cy.getByTestId(orderPriceField).clear().type(price);
|
|
||||||
}
|
|
||||||
cy.getByTestId(orderTIFDropDown).select(timeInForce);
|
|
||||||
if (timeInForce === 'TIME_IN_FORCE_GTT') {
|
|
||||||
if (!expiresAt) {
|
|
||||||
throw new Error('Specify expiresAt if using GTT');
|
|
||||||
}
|
|
||||||
cy.getByTestId('date-picker-field').type(expiresAt);
|
|
||||||
}
|
|
||||||
cy.getByTestId(placeOrderBtn).click();
|
|
||||||
};
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
export const orderSizeField = 'order-size';
|
|
||||||
export const orderPriceField = 'order-price';
|
|
||||||
export const orderTIFDropDown = 'order-tif';
|
|
||||||
export const placeOrderBtn = 'place-order';
|
|
||||||
export const toggleShort = 'order-side-SIDE_SELL';
|
|
||||||
export const toggleLong = 'order-side-SIDE_BUY';
|
|
||||||
export const toggleLimit = 'order-type-Limit';
|
|
||||||
export const toggleMarket = 'order-type-Market';
|
|
||||||
|
|
||||||
export const TIFlist = Object.values(Schema.OrderTimeInForce).map((value) => {
|
|
||||||
return {
|
|
||||||
code: Schema.OrderTimeInForceCode[value],
|
|
||||||
value,
|
|
||||||
text: Schema.OrderTimeInForceMapping[value],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export const connectEthereumWallet = (connectorName: string) => {
|
|
||||||
cy.getByTestId('connect-eth-wallet-btn').should('be.enabled').click();
|
|
||||||
cy.getByTestId('web3-connector-list').should('be.visible');
|
|
||||||
cy.getByTestId(`web3-connector-${connectorName}`).click();
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
export const selectAsset = (assetIndex: number) => {
|
|
||||||
cy.log(`selecting asset: ${assetIndex}`);
|
|
||||||
cy.getByTestId('select-asset').click();
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(assetIndex).click();
|
|
||||||
|
|
||||||
// The asset only gets set once the queries (getWithdrawThreshold, getDelay)
|
|
||||||
// against the Ethereum change resolve, we should fix this but for now just force
|
|
||||||
// some wait time
|
|
||||||
// eslint-disable-next-line
|
|
||||||
cy.wait(100);
|
|
||||||
};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import '@vegaprotocol/cypress';
|
|
||||||
import 'cypress-real-events/support';
|
|
||||||
import registerCypressGrep from '@cypress/grep';
|
|
||||||
import { addMockTradingPage } from './trading';
|
|
||||||
import 'cypress-mochawesome-reporter/register';
|
|
||||||
|
|
||||||
registerCypressGrep();
|
|
||||||
addMockTradingPage();
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import type {
|
|
||||||
OrdersUpdateSubscription,
|
|
||||||
OrdersUpdateSubscriptionVariables,
|
|
||||||
OrderUpdateFieldsFragment,
|
|
||||||
} from '@vegaprotocol/orders';
|
|
||||||
import type { onMessage } from '@vegaprotocol/cypress';
|
|
||||||
import type { PartialDeep } from 'type-fest';
|
|
||||||
import { orderUpdateSubscription } from '@vegaprotocol/mock';
|
|
||||||
|
|
||||||
const sendOrderUpdate: ((data: OrdersUpdateSubscription) => void)[] = [];
|
|
||||||
const getOnOrderUpdate = () => {
|
|
||||||
const onOrderUpdate: onMessage<
|
|
||||||
OrdersUpdateSubscription,
|
|
||||||
OrdersUpdateSubscriptionVariables
|
|
||||||
> = (send) => {
|
|
||||||
sendOrderUpdate.push(send);
|
|
||||||
};
|
|
||||||
return onOrderUpdate;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getSubscriptionMocks = () => ({
|
|
||||||
OrdersUpdate: getOnOrderUpdate(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export function updateOrder(
|
|
||||||
override?: PartialDeep<OrderUpdateFieldsFragment>
|
|
||||||
): void {
|
|
||||||
const update: OrdersUpdateSubscription = orderUpdateSubscription({
|
|
||||||
// @ts-ignore partial deep check failing
|
|
||||||
orders: [override],
|
|
||||||
});
|
|
||||||
if (!sendOrderUpdate) {
|
|
||||||
throw new Error('OrderSub not called');
|
|
||||||
}
|
|
||||||
sendOrderUpdate.forEach((send) => send(update));
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import type {
|
|
||||||
OrderAmendment,
|
|
||||||
OrderAmendmentBody,
|
|
||||||
OrderCancellation,
|
|
||||||
OrderCancellationBody,
|
|
||||||
OrderSubmission,
|
|
||||||
OrderSubmissionBody,
|
|
||||||
Transaction,
|
|
||||||
} from '@vegaprotocol/wallet';
|
|
||||||
|
|
||||||
export const testOrderSubmission = (
|
|
||||||
order: OrderSubmission,
|
|
||||||
expected?: Partial<OrderSubmission>
|
|
||||||
) => {
|
|
||||||
const expectedOrder = {
|
|
||||||
...order,
|
|
||||||
...expected,
|
|
||||||
};
|
|
||||||
|
|
||||||
const transaction: OrderSubmissionBody = {
|
|
||||||
orderSubmission: expectedOrder,
|
|
||||||
};
|
|
||||||
vegaWalletTransaction(transaction);
|
|
||||||
verifyToast();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const testOrderAmendment = (
|
|
||||||
order: OrderAmendment,
|
|
||||||
expected?: Partial<OrderAmendment>
|
|
||||||
) => {
|
|
||||||
const expectedOrder = {
|
|
||||||
...order,
|
|
||||||
...expected,
|
|
||||||
};
|
|
||||||
|
|
||||||
const transaction: OrderAmendmentBody = {
|
|
||||||
orderAmendment: expectedOrder,
|
|
||||||
};
|
|
||||||
vegaWalletTransaction(transaction);
|
|
||||||
verifyToast();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const testOrderCancellation = (
|
|
||||||
order: OrderCancellation,
|
|
||||||
expected?: Partial<OrderCancellation>
|
|
||||||
) => {
|
|
||||||
const expectedOrder = {
|
|
||||||
...order,
|
|
||||||
...expected,
|
|
||||||
};
|
|
||||||
|
|
||||||
const transaction: OrderCancellationBody = {
|
|
||||||
orderCancellation: expectedOrder,
|
|
||||||
};
|
|
||||||
vegaWalletTransaction(transaction);
|
|
||||||
verifyToast();
|
|
||||||
};
|
|
||||||
|
|
||||||
const vegaWalletTransaction = (transaction: Transaction) => {
|
|
||||||
cy.wait('@VegaWalletTransaction')
|
|
||||||
.its('request')
|
|
||||||
.then((req) => {
|
|
||||||
expect(req.body.params).to.deep.equal({
|
|
||||||
publicKey: Cypress.env('VEGA_PUBLIC_KEY'),
|
|
||||||
sendingMode: 'TYPE_SYNC',
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
expect(req.headers.authorization).to.equal(
|
|
||||||
`VWT ${Cypress.env('VEGA_WALLET_API_TOKEN')}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const verifyToast = () => {
|
|
||||||
cy.getByTestId('toast').should('contain.text', 'Awaiting confirmation');
|
|
||||||
cy.getByTestId('toast')
|
|
||||||
.find('a')
|
|
||||||
.invoke('attr', 'href')
|
|
||||||
.should('include', `${Cypress.env('EXPLORER_URL')}/txs/test-tx-hash`);
|
|
||||||
cy.getByTestId('toast-close').click();
|
|
||||||
};
|
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import type { CyHttpMessages } from 'cypress/types/net-stubbing';
|
|
||||||
import type { Provider, Status } from '@vegaprotocol/markets';
|
|
||||||
import {
|
|
||||||
accountsQuery,
|
|
||||||
assetQuery,
|
|
||||||
assetsQuery,
|
|
||||||
candlesQuery,
|
|
||||||
chartQuery,
|
|
||||||
depositsQuery,
|
|
||||||
estimateFeesQuery,
|
|
||||||
marginsQuery,
|
|
||||||
marketCandlesQuery,
|
|
||||||
marketDataQuery,
|
|
||||||
marketDepthQuery,
|
|
||||||
marketInfoQuery,
|
|
||||||
marketsCandlesQuery,
|
|
||||||
marketsDataQuery,
|
|
||||||
marketsQuery,
|
|
||||||
networkParamsQuery,
|
|
||||||
nodeGuardQuery,
|
|
||||||
ordersQuery,
|
|
||||||
estimatePositionQuery,
|
|
||||||
positionsQuery,
|
|
||||||
proposalListQuery,
|
|
||||||
tradesQuery,
|
|
||||||
withdrawalsQuery,
|
|
||||||
protocolUpgradeProposalsQuery,
|
|
||||||
blockStatisticsQuery,
|
|
||||||
networkParamQuery,
|
|
||||||
liquidityProvisionsQuery,
|
|
||||||
successorMarketQuery,
|
|
||||||
parentMarketIdQuery,
|
|
||||||
successorMarketIdsQuery,
|
|
||||||
successorMarketProposalDetailsQuery,
|
|
||||||
liquidityProvidersQuery,
|
|
||||||
} from '@vegaprotocol/mock';
|
|
||||||
import type { PartialDeep } from 'type-fest';
|
|
||||||
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/markets';
|
|
||||||
|
|
||||||
type MarketPageMockData = {
|
|
||||||
state: Schema.MarketState;
|
|
||||||
tradingMode?: Schema.MarketTradingMode;
|
|
||||||
trigger?: Schema.AuctionTrigger;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ORACLE_PUBKEY = Cypress.env('ORACLE_PUBKEY');
|
|
||||||
|
|
||||||
const marketDataOverride = (
|
|
||||||
data: MarketPageMockData
|
|
||||||
): PartialDeep<MarketDataQuery> => ({
|
|
||||||
marketsConnection: {
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
data: {
|
|
||||||
// @ts-ignore conflict between incoming and outgoing types
|
|
||||||
trigger: data.trigger,
|
|
||||||
// @ts-ignore same as above
|
|
||||||
marketTradingMode: data.tradingMode,
|
|
||||||
marketState: data.state,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const marketsDataOverride = (
|
|
||||||
data: MarketPageMockData
|
|
||||||
): PartialDeep<MarketsQuery> => ({
|
|
||||||
marketsConnection: {
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
// @ts-ignore conflict between incoming and outgoing types
|
|
||||||
tradingMode: data.tradingMode,
|
|
||||||
state: data.state,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const mockTradingPage = (
|
|
||||||
req: CyHttpMessages.IncomingHttpRequest,
|
|
||||||
state: Schema.MarketState = Schema.MarketState.STATE_ACTIVE,
|
|
||||||
tradingMode?: Schema.MarketTradingMode,
|
|
||||||
trigger?: Schema.AuctionTrigger
|
|
||||||
) => {
|
|
||||||
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
|
|
||||||
aliasGQLQuery(
|
|
||||||
req,
|
|
||||||
'Markets',
|
|
||||||
marketsQuery(marketsDataOverride({ state, tradingMode, trigger }))
|
|
||||||
);
|
|
||||||
aliasGQLQuery(
|
|
||||||
req,
|
|
||||||
'MarketData',
|
|
||||||
marketDataQuery(marketDataOverride({ state, tradingMode, trigger }))
|
|
||||||
);
|
|
||||||
aliasGQLQuery(req, 'MarketsData', marketsDataQuery());
|
|
||||||
aliasGQLQuery(req, 'MarketsCandles', marketsCandlesQuery());
|
|
||||||
aliasGQLQuery(req, 'MarketCandles', marketCandlesQuery());
|
|
||||||
aliasGQLQuery(req, 'MarketDepth', marketDepthQuery());
|
|
||||||
aliasGQLQuery(req, 'Orders', ordersQuery());
|
|
||||||
aliasGQLQuery(req, 'Accounts', accountsQuery());
|
|
||||||
aliasGQLQuery(req, 'Positions', positionsQuery());
|
|
||||||
aliasGQLQuery(req, 'Margins', marginsQuery());
|
|
||||||
aliasGQLQuery(req, 'Assets', assetsQuery());
|
|
||||||
aliasGQLQuery(req, 'Asset', assetQuery());
|
|
||||||
aliasGQLQuery(
|
|
||||||
req,
|
|
||||||
'MarketInfo',
|
|
||||||
marketInfoQuery({
|
|
||||||
market: {
|
|
||||||
tradableInstrument: {
|
|
||||||
instrument: {
|
|
||||||
product: {
|
|
||||||
__typename: 'Future',
|
|
||||||
dataSourceSpecForSettlementData: {
|
|
||||||
data: {
|
|
||||||
sourceType: {
|
|
||||||
sourceType: {
|
|
||||||
signers: [
|
|
||||||
{
|
|
||||||
__typename: 'Signer',
|
|
||||||
signer: {
|
|
||||||
__typename: 'PubKey',
|
|
||||||
key: ORACLE_PUBKEY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dataSourceSpecForTradingTermination: {
|
|
||||||
data: {
|
|
||||||
sourceType: {
|
|
||||||
sourceType: {
|
|
||||||
signers: [
|
|
||||||
{
|
|
||||||
__typename: 'Signer',
|
|
||||||
signer: {
|
|
||||||
__typename: 'PubKey',
|
|
||||||
key: ORACLE_PUBKEY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
aliasGQLQuery(req, 'Trades', tradesQuery());
|
|
||||||
aliasGQLQuery(req, 'Chart', chartQuery());
|
|
||||||
aliasGQLQuery(req, 'LiquidityProvisions', liquidityProvisionsQuery());
|
|
||||||
aliasGQLQuery(req, 'LiquidityProviders', liquidityProvidersQuery());
|
|
||||||
aliasGQLQuery(req, 'Candles', candlesQuery());
|
|
||||||
aliasGQLQuery(req, 'Withdrawals', withdrawalsQuery());
|
|
||||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
|
||||||
aliasGQLQuery(req, 'NetworkParam', networkParamQuery);
|
|
||||||
aliasGQLQuery(req, 'EstimateFees', estimateFeesQuery());
|
|
||||||
aliasGQLQuery(req, 'EstimatePosition', estimatePositionQuery());
|
|
||||||
aliasGQLQuery(req, 'ProposalsList', proposalListQuery());
|
|
||||||
aliasGQLQuery(req, 'Deposits', depositsQuery());
|
|
||||||
aliasGQLQuery(
|
|
||||||
req,
|
|
||||||
'ProtocolUpgradeProposals',
|
|
||||||
protocolUpgradeProposalsQuery()
|
|
||||||
);
|
|
||||||
aliasGQLQuery(req, 'BlockStatistics', blockStatisticsQuery());
|
|
||||||
aliasGQLQuery(req, 'SuccessorMarket', successorMarketQuery());
|
|
||||||
aliasGQLQuery(req, 'ParentMarketId', parentMarketIdQuery());
|
|
||||||
aliasGQLQuery(req, 'SuccessorMarketIds', successorMarketIdsQuery());
|
|
||||||
aliasGQLQuery(
|
|
||||||
req,
|
|
||||||
'SuccessorMarketProposalDetails',
|
|
||||||
successorMarketProposalDetailsQuery()
|
|
||||||
);
|
|
||||||
};
|
|
||||||
declare global {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
namespace Cypress {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
interface Chainable<Subject> {
|
|
||||||
mockTradingPage(
|
|
||||||
state?: Schema.MarketState,
|
|
||||||
tradingMode?: Schema.MarketTradingMode,
|
|
||||||
trigger?: Schema.AuctionTrigger,
|
|
||||||
oracleStatus?: Status
|
|
||||||
): void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const addMockTradingPage = () => {
|
|
||||||
Cypress.Commands.add(
|
|
||||||
'mockTradingPage',
|
|
||||||
(
|
|
||||||
state = Schema.MarketState.STATE_ACTIVE,
|
|
||||||
tradingMode,
|
|
||||||
trigger,
|
|
||||||
oracleStatus
|
|
||||||
) => {
|
|
||||||
cy.mockChainId();
|
|
||||||
cy.mockGQL((req) => {
|
|
||||||
mockTradingPage(req, state, tradingMode, trigger);
|
|
||||||
});
|
|
||||||
|
|
||||||
const oracle: Provider = {
|
|
||||||
name: 'Another oracle',
|
|
||||||
url: 'https://zombo.com',
|
|
||||||
description_markdown:
|
|
||||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
|
||||||
oracle: {
|
|
||||||
status: oracleStatus || 'GOOD',
|
|
||||||
status_reason: '',
|
|
||||||
first_verified: '2022-01-01T00:00:00.000Z',
|
|
||||||
last_verified: '2022-12-31T00:00:00.000Z',
|
|
||||||
type: 'public_key',
|
|
||||||
public_key: ORACLE_PUBKEY,
|
|
||||||
},
|
|
||||||
proofs: [
|
|
||||||
{
|
|
||||||
format: 'signed_message',
|
|
||||||
available: true,
|
|
||||||
type: 'public_key',
|
|
||||||
public_key: ORACLE_PUBKEY,
|
|
||||||
message: 'SOMEHEX',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/public_key-${ORACLE_PUBKEY}.toml`,
|
|
||||||
};
|
|
||||||
// Prevent request to github, return some dummy content
|
|
||||||
cy.intercept(
|
|
||||||
'GET',
|
|
||||||
/^https:\/\/raw.githubusercontent.com\/vegaprotocol\/well-known/,
|
|
||||||
{
|
|
||||||
body: [oracle],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"sourceMap": false,
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"allowJs": true,
|
|
||||||
"types": ["cypress", "node", "cypress-real-events", "@cypress/grep"],
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitOverride": true,
|
|
||||||
"noPropertyAccessFromIndexSignature": false,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts", "src/**/*.js", "./declaration.d.ts"]
|
|
||||||
}
|
|
||||||
@@ -49,10 +49,4 @@ To run the minimal set of unit tests, run the following:
|
|||||||
yarn nx test trading
|
yarn nx test trading
|
||||||
```
|
```
|
||||||
|
|
||||||
To run the UI automation tests with a mocked API, run:
|
To run the UI automation tests please read [e2e/README.md](e2e/README.md)
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn nx run trading-e2e:e2e
|
|
||||||
```
|
|
||||||
|
|
||||||
To run tests with market sim please read [the readme](e2e/README.md).
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export { ProfileDialog } from './profile-dialog';
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
FormGroup,
|
||||||
|
Input,
|
||||||
|
InputError,
|
||||||
|
Intent,
|
||||||
|
TradingButton,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useProfileDialogStore } from '../../stores/profile-dialog-store';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { useT } from '../../lib/use-t';
|
||||||
|
import { useRequired } from '@vegaprotocol/utils';
|
||||||
|
import {
|
||||||
|
useSimpleTransaction,
|
||||||
|
type Status,
|
||||||
|
useVegaWallet,
|
||||||
|
} from '@vegaprotocol/wallet-react';
|
||||||
|
import {
|
||||||
|
usePartyProfilesQuery,
|
||||||
|
type PartyProfilesQuery,
|
||||||
|
} from '../vega-wallet-connect-button/__generated__/PartyProfiles';
|
||||||
|
|
||||||
|
export const ProfileDialog = () => {
|
||||||
|
const t = useT();
|
||||||
|
const { pubKeys } = useVegaWallet();
|
||||||
|
const { data, refetch } = usePartyProfilesQuery({
|
||||||
|
variables: { partyIds: pubKeys.map((pk) => pk.publicKey) },
|
||||||
|
skip: pubKeys.length <= 0,
|
||||||
|
});
|
||||||
|
const open = useProfileDialogStore((store) => store.open);
|
||||||
|
const pubKey = useProfileDialogStore((store) => store.pubKey);
|
||||||
|
const setOpen = useProfileDialogStore((store) => store.setOpen);
|
||||||
|
|
||||||
|
const { send, status, error, reset } = useSimpleTransaction({
|
||||||
|
onSuccess: () => {
|
||||||
|
refetch();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const profileEdge = data?.partiesProfilesConnection?.edges.find(
|
||||||
|
(e) => e.node.partyId === pubKey
|
||||||
|
);
|
||||||
|
|
||||||
|
const sendTx = (field: FormFields) => {
|
||||||
|
send({
|
||||||
|
updatePartyProfile: {
|
||||||
|
alias: field.alias,
|
||||||
|
metadata: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onChange={() => {
|
||||||
|
setOpen(undefined);
|
||||||
|
reset();
|
||||||
|
}}
|
||||||
|
title={t('Edit profile')}
|
||||||
|
>
|
||||||
|
<ProfileForm
|
||||||
|
profile={profileEdge?.node}
|
||||||
|
status={status}
|
||||||
|
error={error}
|
||||||
|
onSubmit={sendTx}
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface FormFields {
|
||||||
|
alias: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Profile = NonNullable<
|
||||||
|
PartyProfilesQuery['partiesProfilesConnection']
|
||||||
|
>['edges'][number]['node'];
|
||||||
|
|
||||||
|
const ProfileForm = ({
|
||||||
|
profile,
|
||||||
|
onSubmit,
|
||||||
|
status,
|
||||||
|
error,
|
||||||
|
}: {
|
||||||
|
profile: Profile | undefined;
|
||||||
|
onSubmit: (fields: FormFields) => void;
|
||||||
|
status: Status;
|
||||||
|
error: string | undefined;
|
||||||
|
}) => {
|
||||||
|
const t = useT();
|
||||||
|
const required = useRequired();
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<FormFields>({
|
||||||
|
defaultValues: {
|
||||||
|
alias: profile?.alias,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderButtonText = () => {
|
||||||
|
if (status === 'requested') {
|
||||||
|
return t('Confirm in wallet...');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'pending') {
|
||||||
|
return t('Confirming transaction...');
|
||||||
|
}
|
||||||
|
|
||||||
|
return t('Submit');
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorMessage = errors.alias?.message || error;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)} className="mt-3">
|
||||||
|
<FormGroup label="Alias" labelFor="alias">
|
||||||
|
<Input
|
||||||
|
{...register('alias', {
|
||||||
|
validate: {
|
||||||
|
required,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
{errorMessage && (
|
||||||
|
<InputError>
|
||||||
|
<p className="break-words max-w-full first-letter:uppercase">
|
||||||
|
{errorMessage}
|
||||||
|
</p>
|
||||||
|
</InputError>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status === 'confirmed' && (
|
||||||
|
<p className="mt-2 mb-4 text-sm text-success">
|
||||||
|
{t('Profile updated')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
|
<TradingButton
|
||||||
|
type="submit"
|
||||||
|
intent={Intent.Info}
|
||||||
|
disabled={status === 'requested' || status === 'pending'}
|
||||||
|
>
|
||||||
|
{renderButtonText()}
|
||||||
|
</TradingButton>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
query PartyProfiles($partyIds: [ID!]) {
|
||||||
|
partiesProfilesConnection(ids: $partyIds) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
partyId
|
||||||
|
alias
|
||||||
|
metadata {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
|
import * as Apollo from '@apollo/client';
|
||||||
|
const defaultOptions = {} as const;
|
||||||
|
export type PartyProfilesQueryVariables = Types.Exact<{
|
||||||
|
partyIds?: Types.InputMaybe<Array<Types.Scalars['ID']> | Types.Scalars['ID']>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type PartyProfilesQuery = { __typename?: 'Query', partiesProfilesConnection?: { __typename?: 'PartiesProfilesConnection', edges: Array<{ __typename?: 'PartyProfileEdge', node: { __typename?: 'PartyProfile', partyId: string, alias: string, metadata: Array<{ __typename?: 'Metadata', key: string, value: string }> } }> } | null };
|
||||||
|
|
||||||
|
|
||||||
|
export const PartyProfilesDocument = gql`
|
||||||
|
query PartyProfiles($partyIds: [ID!]) {
|
||||||
|
partiesProfilesConnection(ids: $partyIds) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
partyId
|
||||||
|
alias
|
||||||
|
metadata {
|
||||||
|
key
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __usePartyProfilesQuery__
|
||||||
|
*
|
||||||
|
* To run a query within a React component, call `usePartyProfilesQuery` and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `usePartyProfilesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||||
|
* you can use to render your UI.
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { data, loading, error } = usePartyProfilesQuery({
|
||||||
|
* variables: {
|
||||||
|
* partyIds: // value for 'partyIds'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function usePartyProfilesQuery(baseOptions?: Apollo.QueryHookOptions<PartyProfilesQuery, PartyProfilesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useQuery<PartyProfilesQuery, PartyProfilesQueryVariables>(PartyProfilesDocument, options);
|
||||||
|
}
|
||||||
|
export function usePartyProfilesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyProfilesQuery, PartyProfilesQueryVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useLazyQuery<PartyProfilesQuery, PartyProfilesQueryVariables>(PartyProfilesDocument, options);
|
||||||
|
}
|
||||||
|
export type PartyProfilesQueryHookResult = ReturnType<typeof usePartyProfilesQuery>;
|
||||||
|
export type PartyProfilesLazyQueryHookResult = ReturnType<typeof usePartyProfilesLazyQuery>;
|
||||||
|
export type PartyProfilesQueryResult = Apollo.QueryResult<PartyProfilesQuery, PartyProfilesQueryVariables>;
|
||||||
+52
-12
@@ -1,21 +1,57 @@
|
|||||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
import { act, fireEvent, render, screen, within } from '@testing-library/react';
|
||||||
import { VegaWalletConnectButton } from './vega-wallet-connect-button';
|
import { VegaWalletConnectButton } from './vega-wallet-connect-button';
|
||||||
import { truncateByChars } from '@vegaprotocol/utils';
|
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import {
|
import {
|
||||||
mockConfig,
|
mockConfig,
|
||||||
MockedWalletProvider,
|
MockedWalletProvider,
|
||||||
} from '@vegaprotocol/wallet-react/testing';
|
} from '@vegaprotocol/wallet-react/testing';
|
||||||
|
import { MockedProvider, type MockedResponse } from '@apollo/react-testing';
|
||||||
|
import {
|
||||||
|
PartyProfilesDocument,
|
||||||
|
type PartyProfilesQuery,
|
||||||
|
} from './__generated__/PartyProfiles';
|
||||||
|
|
||||||
jest.mock('../../lib/hooks/use-get-current-route-id', () => ({
|
jest.mock('../../lib/hooks/use-get-current-route-id', () => ({
|
||||||
useGetCurrentRouteId: jest.fn().mockReturnValue('current-route-id'),
|
useGetCurrentRouteId: jest.fn().mockReturnValue('current-route-id'),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const key = { publicKey: '123456__123456', name: 'test' };
|
||||||
|
const key2 = { publicKey: 'abcdef__abcdef', name: 'test2' };
|
||||||
|
const keys = [key, key2];
|
||||||
|
const keyProfile = {
|
||||||
|
__typename: 'PartyProfile' as const,
|
||||||
|
partyId: key.publicKey,
|
||||||
|
alias: `${key.name} alias`,
|
||||||
|
metadata: [],
|
||||||
|
};
|
||||||
|
|
||||||
const renderComponent = (mockOnClick = jest.fn()) => {
|
const renderComponent = (mockOnClick = jest.fn()) => {
|
||||||
|
const partyProfilesMock: MockedResponse<PartyProfilesQuery> = {
|
||||||
|
request: {
|
||||||
|
query: PartyProfilesDocument,
|
||||||
|
variables: { partyIds: keys.map((k) => k.publicKey) },
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
partiesProfilesConnection: {
|
||||||
|
__typename: 'PartiesProfilesConnection',
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
__typename: 'PartyProfileEdge',
|
||||||
|
node: keyProfile,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MockedWalletProvider>
|
<MockedProvider mocks={[partyProfilesMock]}>
|
||||||
<VegaWalletConnectButton onClick={mockOnClick} />
|
<MockedWalletProvider>
|
||||||
</MockedWalletProvider>
|
<VegaWalletConnectButton onClick={mockOnClick} />
|
||||||
|
</MockedWalletProvider>
|
||||||
|
</MockedProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,10 +79,6 @@ describe('VegaWalletConnectButton', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should open dropdown and refresh keys when connected', async () => {
|
it('should open dropdown and refresh keys when connected', async () => {
|
||||||
const key = { publicKey: '123456__123456', name: 'test' };
|
|
||||||
const key2 = { publicKey: 'abcdef__abcdef', name: 'test2' };
|
|
||||||
const keys = [key, key2];
|
|
||||||
|
|
||||||
mockConfig.store.setState({
|
mockConfig.store.setState({
|
||||||
status: 'connected',
|
status: 'connected',
|
||||||
keys,
|
keys,
|
||||||
@@ -61,14 +93,22 @@ describe('VegaWalletConnectButton', () => {
|
|||||||
|
|
||||||
expect(screen.queryByTestId('connect-vega-wallet')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('connect-vega-wallet')).not.toBeInTheDocument();
|
||||||
const button = screen.getByTestId('manage-vega-wallet');
|
const button = screen.getByTestId('manage-vega-wallet');
|
||||||
expect(button).toHaveTextContent(truncateByChars(key.publicKey));
|
expect(button).toHaveTextContent(key.name);
|
||||||
|
|
||||||
fireEvent.click(button);
|
fireEvent.click(button);
|
||||||
|
|
||||||
expect(await screen.findByRole('menu')).toBeInTheDocument();
|
expect(await screen.findByRole('menu')).toBeInTheDocument();
|
||||||
expect(await screen.findAllByRole('menuitemradio')).toHaveLength(
|
const menuItems = await screen.findAllByRole('menuitemradio');
|
||||||
keys.length
|
expect(menuItems).toHaveLength(keys.length);
|
||||||
|
|
||||||
|
expect(within(menuItems[0]).getByTestId('alias')).toHaveTextContent(
|
||||||
|
keyProfile.alias
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expect(within(menuItems[1]).getByTestId('alias')).toHaveTextContent(
|
||||||
|
'No alias'
|
||||||
|
);
|
||||||
|
|
||||||
expect(refreshKeys).toHaveBeenCalled();
|
expect(refreshKeys).toHaveBeenCalled();
|
||||||
|
|
||||||
fireEvent.click(screen.getByTestId(`key-${key2.publicKey}`));
|
fireEvent.click(screen.getByTestId(`key-${key2.publicKey}`));
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
TradingDropdownItem,
|
TradingDropdownItem,
|
||||||
TradingDropdownRadioItem,
|
TradingDropdownRadioItem,
|
||||||
TradingDropdownItemIndicator,
|
TradingDropdownItemIndicator,
|
||||||
|
Tooltip,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { isBrowserWalletInstalled, type Key } from '@vegaprotocol/wallet';
|
import { isBrowserWalletInstalled, type Key } from '@vegaprotocol/wallet';
|
||||||
import { useDialogStore, useVegaWallet } from '@vegaprotocol/wallet-react';
|
import { useDialogStore, useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||||
@@ -22,6 +23,8 @@ import classNames from 'classnames';
|
|||||||
import { ViewType, useSidebar } from '../sidebar';
|
import { ViewType, useSidebar } from '../sidebar';
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
|
import { usePartyProfilesQuery } from './__generated__/PartyProfiles';
|
||||||
|
import { useProfileDialogStore } from '../../stores/profile-dialog-store';
|
||||||
|
|
||||||
export const VegaWalletConnectButton = ({
|
export const VegaWalletConnectButton = ({
|
||||||
intent = Intent.None,
|
intent = Intent.None,
|
||||||
@@ -68,10 +71,10 @@ export const VegaWalletConnectButton = ({
|
|||||||
{activeKey ? (
|
{activeKey ? (
|
||||||
<>
|
<>
|
||||||
{activeKey && (
|
{activeKey && (
|
||||||
<span className="uppercase">{activeKey.name}</span>
|
<span className="uppercase">
|
||||||
|
{activeKey.name ? activeKey.name : t('Unnamed key')}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
{' | '}
|
|
||||||
{truncateByChars(activeKey.publicKey)}
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>{'Select key'}</>
|
<>{'Select key'}</>
|
||||||
@@ -88,20 +91,11 @@ export const VegaWalletConnectButton = ({
|
|||||||
onEscapeKeyDown={() => setDropdownOpen(false)}
|
onEscapeKeyDown={() => setDropdownOpen(false)}
|
||||||
>
|
>
|
||||||
<div className="min-w-[340px]" data-testid="keypair-list">
|
<div className="min-w-[340px]" data-testid="keypair-list">
|
||||||
<TradingDropdownRadioGroup
|
<KeypairRadioGroup
|
||||||
value={pubKey || undefined}
|
pubKey={pubKey}
|
||||||
onValueChange={(value) => {
|
pubKeys={pubKeys}
|
||||||
selectPubKey(value);
|
onSelect={selectPubKey}
|
||||||
}}
|
/>
|
||||||
>
|
|
||||||
{pubKeys.map((pk) => (
|
|
||||||
<KeypairItem
|
|
||||||
key={pk.publicKey}
|
|
||||||
pk={pk}
|
|
||||||
active={pk.publicKey === pubKey}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</TradingDropdownRadioGroup>
|
|
||||||
<TradingDropdownSeparator />
|
<TradingDropdownSeparator />
|
||||||
{!isReadOnly && (
|
{!isReadOnly && (
|
||||||
<TradingDropdownItem
|
<TradingDropdownItem
|
||||||
@@ -141,28 +135,52 @@ export const VegaWalletConnectButton = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const KeypairItem = ({ pk, active }: { pk: Key; active: boolean }) => {
|
const KeypairRadioGroup = ({
|
||||||
|
pubKey,
|
||||||
|
pubKeys,
|
||||||
|
onSelect,
|
||||||
|
}: {
|
||||||
|
pubKey: string | undefined;
|
||||||
|
pubKeys: Key[];
|
||||||
|
onSelect: (pubKey: string) => void;
|
||||||
|
}) => {
|
||||||
|
const { data } = usePartyProfilesQuery({
|
||||||
|
variables: { partyIds: pubKeys.map((pk) => pk.publicKey) },
|
||||||
|
skip: pubKeys.length <= 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TradingDropdownRadioGroup value={pubKey} onValueChange={onSelect}>
|
||||||
|
{pubKeys.map((pk) => {
|
||||||
|
const profile = data?.partiesProfilesConnection?.edges.find(
|
||||||
|
(e) => e.node.partyId === pk.publicKey
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<KeypairItem key={pk.publicKey} pk={pk} alias={profile?.node.alias} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</TradingDropdownRadioGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const KeypairItem = ({ pk, alias }: { pk: Key; alias: string | undefined }) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const [copied, setCopied] = useCopyTimeout();
|
const [copied, setCopied] = useCopyTimeout();
|
||||||
|
const setOpen = useProfileDialogStore((store) => store.setOpen);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TradingDropdownRadioItem value={pk.publicKey}>
|
<TradingDropdownRadioItem value={pk.publicKey}>
|
||||||
<div
|
<div>
|
||||||
className={classNames('flex-1 mr-2', {
|
<div className="flex items-center gap-2">
|
||||||
'text-default': active,
|
<span>{pk.name ? pk.name : t('Unnamed key')}</span>
|
||||||
'text-muted': !active,
|
|
||||||
})}
|
|
||||||
data-testid={`key-${pk.publicKey}`}
|
|
||||||
>
|
|
||||||
<span className={classNames('mr-2 uppercase')}>
|
|
||||||
{pk.name}
|
|
||||||
{' | '}
|
{' | '}
|
||||||
{truncateByChars(pk.publicKey)}
|
<span className="font-mono">
|
||||||
</span>
|
{truncateByChars(pk.publicKey, 3, 3)}
|
||||||
<span className="inline-flex items-center gap-1">
|
</span>
|
||||||
<CopyToClipboard text={pk.publicKey} onCopy={() => setCopied(true)}>
|
<CopyToClipboard text={pk.publicKey} onCopy={() => setCopied(true)}>
|
||||||
<button
|
<button
|
||||||
data-testid="copy-vega-public-key"
|
data-testid="copy-vega-public-key"
|
||||||
|
className="relative -top-px"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<span className="sr-only">{t('Copy')}</span>
|
<span className="sr-only">{t('Copy')}</span>
|
||||||
@@ -170,7 +188,17 @@ const KeypairItem = ({ pk, active }: { pk: Key; active: boolean }) => {
|
|||||||
</button>
|
</button>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
{copied && <span className="text-xs">{t('Copied')}</span>}
|
{copied && <span className="text-xs">{t('Copied')}</span>}
|
||||||
</span>
|
</div>
|
||||||
|
<div
|
||||||
|
className={classNames('flex-1 mr-2 text-secondary text-sm')}
|
||||||
|
data-testid={`key-${pk.publicKey}`}
|
||||||
|
>
|
||||||
|
<Tooltip description={t('Public facing key alias. Click to edit')}>
|
||||||
|
<button data-testid="alias" onClick={() => setOpen(pk.publicKey)}>
|
||||||
|
{alias ? alias : t('No alias')}
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<TradingDropdownItemIndicator />
|
<TradingDropdownItemIndicator />
|
||||||
</TradingDropdownRadioItem>
|
</TradingDropdownRadioItem>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||||
VEGA_VERSION=v0.74.6
|
VEGA_VERSION=v0.75.0-preview.2
|
||||||
LOCAL_SERVER=false
|
LOCAL_SERVER=false
|
||||||
|
|||||||
@@ -50,8 +50,6 @@ def truncate_middle(market_id, start=6, end=4):
|
|||||||
def change_keys(page: Page, vega: VegaServiceNull, key_name):
|
def change_keys(page: Page, vega: VegaServiceNull, key_name):
|
||||||
page.get_by_test_id("manage-vega-wallet").click()
|
page.get_by_test_id("manage-vega-wallet").click()
|
||||||
page.get_by_test_id("key-" + vega.wallet.public_key(key_name)).click()
|
page.get_by_test_id("key-" + vega.wallet.public_key(key_name)).click()
|
||||||
page.click(
|
|
||||||
f'data-testid=key-{vega.wallet.public_key(key_name)} >> .inline-flex')
|
|
||||||
page.reload()
|
page.reload()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Generated
+4
-4
@@ -877,13 +877,13 @@ testing = ["filelock"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dateutil"
|
name = "python-dateutil"
|
||||||
version = "2.8.2"
|
version = "2.9.0.post0"
|
||||||
description = "Extensions to the standard Python datetime module"
|
description = "Extensions to the standard Python datetime module"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
|
||||||
files = [
|
files = [
|
||||||
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
|
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
|
||||||
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
|
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1166,7 +1166,7 @@ profile = ["pytest-profiling", "snakeviz"]
|
|||||||
type = "git"
|
type = "git"
|
||||||
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
|
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
|
||||||
reference = "HEAD"
|
reference = "HEAD"
|
||||||
resolved_reference = "33fec45ce8044ef7f53b625584ce590d174f9057"
|
resolved_reference = "53eed8942acb670783105cb1115bab76710a46dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "websocket-client"
|
name = "websocket-client"
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ def test_market_info_market_volume(page: Page):
|
|||||||
page.get_by_test_id(market_title_test_id).get_by_text(
|
page.get_by_test_id(market_title_test_id).get_by_text(
|
||||||
"Market volume").click()
|
"Market volume").click()
|
||||||
fields = [
|
fields = [
|
||||||
["24 Hour Volume", "-"],
|
["24 Hour Volume", "0 (0 )"],
|
||||||
["Open Interest", "1"],
|
["Open Interest", "1"],
|
||||||
["Best Bid Volume", "99"],
|
["Best Bid Volume", "99"],
|
||||||
["Best Offer Volume", "99"],
|
["Best Offer Volume", "99"],
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class TestPerpetuals:
|
|||||||
page.goto(f"/#/markets/{perps_market}")
|
page.goto(f"/#/markets/{perps_market}")
|
||||||
page.get_by_test_id("Funding payments").click()
|
page.get_by_test_id("Funding payments").click()
|
||||||
row = page.locator(row_selector)
|
row = page.locator(row_selector)
|
||||||
expect(row.locator(col_amount)).to_have_text("9.00 tDAI")
|
expect(row.locator(col_amount)).to_have_text("4.45 tDAI")
|
||||||
|
|
||||||
@pytest.mark.usefixtures("risk_accepted", "auth")
|
@pytest.mark.usefixtures("risk_accepted", "auth")
|
||||||
def test_funding_payment_loss(self, perps_market, page: Page, vega):
|
def test_funding_payment_loss(self, perps_market, page: Page, vega):
|
||||||
@@ -65,7 +65,7 @@ class TestPerpetuals:
|
|||||||
change_keys(page, vega, "market_maker")
|
change_keys(page, vega, "market_maker")
|
||||||
page.get_by_test_id("Funding payments").click()
|
page.get_by_test_id("Funding payments").click()
|
||||||
row = page.locator(row_selector)
|
row = page.locator(row_selector)
|
||||||
expect(row.locator(col_amount)).to_have_text("-27.00 tDAI")
|
expect(row.locator(col_amount)).to_have_text("-13.35 tDAI")
|
||||||
|
|
||||||
@pytest.mark.usefixtures("risk_accepted", "auth")
|
@pytest.mark.usefixtures("risk_accepted", "auth")
|
||||||
def test_funding_header(self, perps_market, page: Page):
|
def test_funding_header(self, perps_market, page: Page):
|
||||||
|
|||||||
@@ -25,8 +25,12 @@ fragment GameFields on Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query Games($epochFrom: Int) {
|
query Games($epochFrom: Int, $teamId: ID) {
|
||||||
games(epochFrom: $epochFrom, entityScope: ENTITY_SCOPE_TEAMS) {
|
games(
|
||||||
|
epochFrom: $epochFrom
|
||||||
|
teamId: $teamId
|
||||||
|
entityScope: ENTITY_SCOPE_TEAMS
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...GameFields
|
...GameFields
|
||||||
|
|||||||
+4
-2
@@ -9,6 +9,7 @@ export type GameFieldsFragment = { __typename?: 'Game', id: string, epoch: numbe
|
|||||||
|
|
||||||
export type GamesQueryVariables = Types.Exact<{
|
export type GamesQueryVariables = Types.Exact<{
|
||||||
epochFrom?: Types.InputMaybe<Types.Scalars['Int']>;
|
epochFrom?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||||
|
teamId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
@@ -44,8 +45,8 @@ export const GameFieldsFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
${TeamEntityFragmentDoc}`;
|
${TeamEntityFragmentDoc}`;
|
||||||
export const GamesDocument = gql`
|
export const GamesDocument = gql`
|
||||||
query Games($epochFrom: Int) {
|
query Games($epochFrom: Int, $teamId: ID) {
|
||||||
games(epochFrom: $epochFrom, entityScope: ENTITY_SCOPE_TEAMS) {
|
games(epochFrom: $epochFrom, teamId: $teamId, entityScope: ENTITY_SCOPE_TEAMS) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...GameFields
|
...GameFields
|
||||||
@@ -68,6 +69,7 @@ export const GamesDocument = gql`
|
|||||||
* const { data, loading, error } = useGamesQuery({
|
* const { data, loading, error } = useGamesQuery({
|
||||||
* variables: {
|
* variables: {
|
||||||
* epochFrom: // value for 'epochFrom'
|
* epochFrom: // value for 'epochFrom'
|
||||||
|
* teamId: // value for 'teamId'
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export const useGames = (teamId?: string, epochFrom?: number): GamesData => {
|
|||||||
const { data, loading, error } = useGamesQuery({
|
const { data, loading, error } = useGamesQuery({
|
||||||
variables: {
|
variables: {
|
||||||
epochFrom: from,
|
epochFrom: from,
|
||||||
|
teamId: teamId,
|
||||||
},
|
},
|
||||||
skip: !from,
|
skip: !from,
|
||||||
fetchPolicy: 'cache-and-network',
|
fetchPolicy: 'cache-and-network',
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import {
|
|||||||
ProtocolUpgradeInProgressNotification,
|
ProtocolUpgradeInProgressNotification,
|
||||||
ProtocolUpgradeProposalNotification,
|
ProtocolUpgradeProposalNotification,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { ViewingBanner } from '../components/viewing-banner';
|
|
||||||
import { Telemetry } from '../components/telemetry';
|
import { Telemetry } from '../components/telemetry';
|
||||||
import { SSRLoader } from './ssr-loader';
|
import { SSRLoader } from './ssr-loader';
|
||||||
import { PartyActiveOrdersHandler } from './party-active-orders-handler';
|
import { PartyActiveOrdersHandler } from './party-active-orders-handler';
|
||||||
@@ -77,7 +76,6 @@ function AppBody({ Component }: AppProps) {
|
|||||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||||
/>
|
/>
|
||||||
<ProtocolUpgradeInProgressNotification />
|
<ProtocolUpgradeInProgressNotification />
|
||||||
<ViewingBanner />
|
|
||||||
</div>
|
</div>
|
||||||
<div data-testid={`pathname-${location.pathname}`}>
|
<div data-testid={`pathname-${location.pathname}`}>
|
||||||
<Component />
|
<Component />
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from '@vegaprotocol/web3';
|
} from '@vegaprotocol/web3';
|
||||||
import { WelcomeDialog } from '../components/welcome-dialog';
|
import { WelcomeDialog } from '../components/welcome-dialog';
|
||||||
import { VegaWalletConnectDialog } from '../components/vega-wallet-connect-dialog';
|
import { VegaWalletConnectDialog } from '../components/vega-wallet-connect-dialog';
|
||||||
|
import { ProfileDialog } from '../components/profile-dialog';
|
||||||
|
|
||||||
const DialogsContainer = () => {
|
const DialogsContainer = () => {
|
||||||
const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore();
|
const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore();
|
||||||
@@ -24,6 +25,7 @@ const DialogsContainer = () => {
|
|||||||
<WelcomeDialog />
|
<WelcomeDialog />
|
||||||
<Web3ConnectUncontrolledDialog />
|
<Web3ConnectUncontrolledDialog />
|
||||||
<WithdrawalApprovalDialogContainer />
|
<WithdrawalApprovalDialogContainer />
|
||||||
|
<ProfileDialog />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
interface ProfileDialogStore {
|
||||||
|
open: boolean;
|
||||||
|
pubKey: string | undefined;
|
||||||
|
setOpen: (pubKey: string | undefined) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useProfileDialogStore = create<ProfileDialogStore>((set) => ({
|
||||||
|
open: false,
|
||||||
|
pubKey: undefined,
|
||||||
|
setOpen: (pubKey) => {
|
||||||
|
if (pubKey) {
|
||||||
|
set({ open: true, pubKey });
|
||||||
|
} else {
|
||||||
|
set({ open: false, pubKey: undefined });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
@@ -12,7 +12,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chainId = 'test-id';
|
const chainId = 'vega-stagnet1-202307191148';
|
||||||
|
|
||||||
export function addMockChainId() {
|
export function addMockChainId() {
|
||||||
Cypress.Commands.add('mockChainId', () => {
|
Cypress.Commands.add('mockChainId', () => {
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ export function addVegaWalletConnect() {
|
|||||||
Cypress.Commands.add('connectVegaWallet', (isMobile) => {
|
Cypress.Commands.add('connectVegaWallet', (isMobile) => {
|
||||||
mockConnectWallet();
|
mockConnectWallet();
|
||||||
cy.highlight(`Connecting Vega Wallet`);
|
cy.highlight(`Connecting Vega Wallet`);
|
||||||
|
cy.get('[data-testid="splash-loader"]', { timeout: 20000 }).should(
|
||||||
|
'not.exist'
|
||||||
|
);
|
||||||
const connectVegaWalletButton = `[data-testid=connect-vega-wallet${
|
const connectVegaWalletButton = `[data-testid=connect-vega-wallet${
|
||||||
isMobile ? '-mobile' : ''
|
isMobile ? '-mobile' : ''
|
||||||
}]:visible`;
|
}]:visible`;
|
||||||
|
|
||||||
cy.get(connectVegaWalletButton).then((btn) => {
|
cy.get(connectVegaWalletButton).then((btn) => {
|
||||||
if (btn.length === 0) {
|
if (btn.length === 0) {
|
||||||
cy.log('could not find the button, perhaps already connected');
|
cy.log('could not find the button, perhaps already connected');
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ declare global {
|
|||||||
|
|
||||||
const hasMethod = (req: CyHttpMessages.IncomingHttpRequest, method: string) => {
|
const hasMethod = (req: CyHttpMessages.IncomingHttpRequest, method: string) => {
|
||||||
const { body } = req;
|
const { body } = req;
|
||||||
return 'method' in body && body.method === method;
|
const b = JSON.parse(body);
|
||||||
|
return 'method' in b && b.method === method;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function addMockWalletCommand() {
|
export function addMockWalletCommand() {
|
||||||
@@ -72,7 +73,7 @@ export const aliasWalletConnectQuery = (
|
|||||||
body: {
|
body: {
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
result: {
|
result: {
|
||||||
chainID: 'test-id',
|
chainID: 'vega-fairground-202305051805',
|
||||||
},
|
},
|
||||||
id: '1',
|
id: '1',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ export const CONSOLE_TRANSFER = '#/portfolio/assets/transfer';
|
|||||||
export const CONSOLE_TRANSFER_ASSET =
|
export const CONSOLE_TRANSFER_ASSET =
|
||||||
'#/portfolio/assets/transfer?assetId=:assetId';
|
'#/portfolio/assets/transfer?assetId=:assetId';
|
||||||
export const CONSOLE_MARKET_PAGE = '#/markets/:marketId';
|
export const CONSOLE_MARKET_PAGE = '#/markets/:marketId';
|
||||||
|
export const CONSOLE_REWARDS_PAGE = '#/rewards';
|
||||||
|
|
||||||
// Governance pages
|
// Governance pages
|
||||||
export const TOKEN_NEW_MARKET_PROPOSAL = '/proposals/propose/new-market';
|
export const TOKEN_NEW_MARKET_PROPOSAL = '/proposals/propose/new-market';
|
||||||
|
|||||||
@@ -971,5 +971,6 @@
|
|||||||
"YourIdentityAnonymous": "Your identity is always anonymous on Vega",
|
"YourIdentityAnonymous": "Your identity is always anonymous on Vega",
|
||||||
"yourStake": "Your stake",
|
"yourStake": "Your stake",
|
||||||
"yourVote": "Your vote",
|
"yourVote": "Your vote",
|
||||||
"youVoted": "You voted"
|
"youVoted": "You voted",
|
||||||
|
"rewardsMovedNotification": "Trading and liquidity rewards have moved. Visit <0>Console</0> to view your rewards."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
{
|
{
|
||||||
"{{liquidityPriceRange}} of mid price": "{{liquidityPriceRange}} of mid price",
|
"{{liquidityPriceRange}} of mid price": "{{liquidityPriceRange}} of mid price",
|
||||||
"{{probability}} probability price bounds": "{{probability}} probability price bounds",
|
"{{probability}} probability price bounds": "{{probability}} probability price bounds",
|
||||||
"24 hour change is unavailable at this time. The price change in the last 120 hours is:": "24 hour change is unavailable at this time. The price change in the last 120 hours is:",
|
|
||||||
"24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}": "24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}",
|
|
||||||
"A concept derived from traditional markets. It is a calculated value for the ‘current market price’ on a market.": "A concept derived from traditional markets. It is a calculated value for the ‘current market price’ on a market.",
|
"A concept derived from traditional markets. It is a calculated value for the ‘current market price’ on a market.": "A concept derived from traditional markets. It is a calculated value for the ‘current market price’ on a market.",
|
||||||
"A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.": "A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.",
|
"A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.": "A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.",
|
||||||
"A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.": "A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.",
|
"A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.": "A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.",
|
||||||
@@ -51,9 +49,6 @@
|
|||||||
"Market": "Market",
|
"Market": "Market",
|
||||||
"Market data": "Market data",
|
"Market data": "Market data",
|
||||||
"Market governance": "Market governance",
|
"Market governance": "Market governance",
|
||||||
"Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:": "Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:",
|
|
||||||
"Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is:": "Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is:",
|
|
||||||
"Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}": "Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}",
|
|
||||||
"Market ID": "Market ID",
|
"Market ID": "Market ID",
|
||||||
"Market price": "Market price",
|
"Market price": "Market price",
|
||||||
"Market specification": "Market specification",
|
"Market specification": "Market specification",
|
||||||
|
|||||||
@@ -86,6 +86,7 @@
|
|||||||
"Docs": "Docs",
|
"Docs": "Docs",
|
||||||
"Earn commission & stake rewards": "Earn commission & stake rewards",
|
"Earn commission & stake rewards": "Earn commission & stake rewards",
|
||||||
"Earned by me": "Earned by me",
|
"Earned by me": "Earned by me",
|
||||||
|
"Edit alias": "Edit alias",
|
||||||
"Eligible teams": "Eligible teams",
|
"Eligible teams": "Eligible teams",
|
||||||
"Enactment date reached and usual auction exit checks pass": "Enactment date reached and usual auction exit checks pass",
|
"Enactment date reached and usual auction exit checks pass": "Enactment date reached and usual auction exit checks pass",
|
||||||
"[empty]": "[empty]",
|
"[empty]": "[empty]",
|
||||||
@@ -162,6 +163,7 @@
|
|||||||
"Joined": "Joined",
|
"Joined": "Joined",
|
||||||
"Joined at": "Joined at",
|
"Joined at": "Joined at",
|
||||||
"Joined epoch": "Joined epoch",
|
"Joined epoch": "Joined epoch",
|
||||||
|
"Key name": "Key name",
|
||||||
"gameCount_one": "Last game result",
|
"gameCount_one": "Last game result",
|
||||||
"gameCount_other": "Last {{count}} game results",
|
"gameCount_other": "Last {{count}} game results",
|
||||||
"Learn about providing liquidity": "Learn about providing liquidity",
|
"Learn about providing liquidity": "Learn about providing liquidity",
|
||||||
@@ -194,6 +196,7 @@
|
|||||||
"My liquidity provision": "My liquidity provision",
|
"My liquidity provision": "My liquidity provision",
|
||||||
"My trading fees": "My trading fees",
|
"My trading fees": "My trading fees",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
|
"No alias": "No alias",
|
||||||
"No closed orders": "No closed orders",
|
"No closed orders": "No closed orders",
|
||||||
"No data": "No data",
|
"No data": "No data",
|
||||||
"No deposits": "No deposits",
|
"No deposits": "No deposits",
|
||||||
@@ -227,6 +230,7 @@
|
|||||||
"Not connected": "Not connected",
|
"Not connected": "Not connected",
|
||||||
"Number of epochs after distribution to delay vesting of rewards by": "Number of epochs after distribution to delay vesting of rewards by",
|
"Number of epochs after distribution to delay vesting of rewards by": "Number of epochs after distribution to delay vesting of rewards by",
|
||||||
"Number of traders": "Number of traders",
|
"Number of traders": "Number of traders",
|
||||||
|
"On-change alias": "On-change alias",
|
||||||
"Open": "Open",
|
"Open": "Open",
|
||||||
"Open a position": "Open a position",
|
"Open a position": "Open a position",
|
||||||
"Open markets": "Open markets",
|
"Open markets": "Open markets",
|
||||||
@@ -249,6 +253,7 @@
|
|||||||
"Portfolio": "Portfolio",
|
"Portfolio": "Portfolio",
|
||||||
"Positions": "Positions",
|
"Positions": "Positions",
|
||||||
"Price": "Price",
|
"Price": "Price",
|
||||||
|
"Profile updated": "Profile updated",
|
||||||
"Program ends:": "Program ends:",
|
"Program ends:": "Program ends:",
|
||||||
"Propose a new market": "Propose a new market",
|
"Propose a new market": "Propose a new market",
|
||||||
"Proposed final price is {{price}} {{assetSymbol}}.": "Proposed final price is {{price}} {{assetSymbol}}.",
|
"Proposed final price is {{price}} {{assetSymbol}}.": "Proposed final price is {{price}} {{assetSymbol}}.",
|
||||||
@@ -289,6 +294,7 @@
|
|||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
"See all markets": "See all markets",
|
"See all markets": "See all markets",
|
||||||
"Select market": "Select market",
|
"Select market": "Select market",
|
||||||
|
"Set party alias": "Set party alias",
|
||||||
"Settings": "Settings",
|
"Settings": "Settings",
|
||||||
"Settlement asset": "Settlement asset",
|
"Settlement asset": "Settlement asset",
|
||||||
"Settlement date": "Settlement date",
|
"Settlement date": "Settlement date",
|
||||||
@@ -311,6 +317,7 @@
|
|||||||
"Stop": "Stop",
|
"Stop": "Stop",
|
||||||
"Stop orders": "Stop orders",
|
"Stop orders": "Stop orders",
|
||||||
"Streak reward multiplier": "Streak reward multiplier",
|
"Streak reward multiplier": "Streak reward multiplier",
|
||||||
|
"Submit": "Submit",
|
||||||
"Successor of a market": "Successor of a market",
|
"Successor of a market": "Successor of a market",
|
||||||
"Successors to this market have been proposed": "Successors to this market have been proposed",
|
"Successors to this market have been proposed": "Successors to this market have been proposed",
|
||||||
"Supplied stake": "Supplied stake",
|
"Supplied stake": "Supplied stake",
|
||||||
@@ -371,6 +378,7 @@
|
|||||||
"Staking rewards": "Staking rewards",
|
"Staking rewards": "Staking rewards",
|
||||||
"Unknown": "Unknown",
|
"Unknown": "Unknown",
|
||||||
"Unknown settlement date": "Unknown settlement date",
|
"Unknown settlement date": "Unknown settlement date",
|
||||||
|
"Unnamed key": "Unnamed key",
|
||||||
"Update team": "Update team",
|
"Update team": "Update team",
|
||||||
"URL": "URL",
|
"URL": "URL",
|
||||||
"Use a comma separated list to allow only specific public keys to join the team": "Use a comma separated list to allow only specific public keys to join the team",
|
"Use a comma separated list to allow only specific public keys to join the team": "Use a comma separated list to allow only specific public keys to join the team",
|
||||||
@@ -407,8 +415,10 @@
|
|||||||
"You will no longer be able to hold a position on this market when it closes in {{duration}}.": "You will no longer be able to hold a position on this market when it closes in {{duration}}.",
|
"You will no longer be able to hold a position on this market when it closes in {{duration}}.": "You will no longer be able to hold a position on this market when it closes in {{duration}}.",
|
||||||
"Your code has been rejected": "Your code has been rejected",
|
"Your code has been rejected": "Your code has been rejected",
|
||||||
"Your identity is always anonymous on Vega": "Your identity is always anonymous on Vega",
|
"Your identity is always anonymous on Vega": "Your identity is always anonymous on Vega",
|
||||||
|
"Your key's private name, can be changed in your wallet": "Your key's private name, can be changed in your wallet",
|
||||||
"Your referral code": "Your referral code",
|
"Your referral code": "Your referral code",
|
||||||
"Your tier": "Your tier",
|
"Your tier": "Your tier",
|
||||||
|
"Your public alias, stored on chain": "Your public alias, stored on chain",
|
||||||
"checkOutProposalsAndVote": "Check out the terms of the proposals and vote:",
|
"checkOutProposalsAndVote": "Check out the terms of the proposals and vote:",
|
||||||
"checkOutProposalsAndVote_one": "Check out the terms of the proposal and vote:",
|
"checkOutProposalsAndVote_one": "Check out the terms of the proposal and vote:",
|
||||||
"checkOutProposalsAndVote_other": "Check out the terms of the proposals and vote:",
|
"checkOutProposalsAndVote_other": "Check out the terms of the proposals and vote:",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"Get MetaMask": "Get MetaMask",
|
"Get MetaMask": "Get MetaMask",
|
||||||
"Get the Vega Wallet": "Get the Vega Wallet",
|
"Get the Vega Wallet": "Get the Vega Wallet",
|
||||||
"I agree": "I agree",
|
"I agree": "I agree",
|
||||||
|
"Once you have the added the extension, <0>refresh</0> you browser.": "Once you have the added the extension, <0>refresh</0> you browser.",
|
||||||
"Successfully connected": "Successfully connected",
|
"Successfully connected": "Successfully connected",
|
||||||
"Transaction was not successful": "Transaction was not successful",
|
"Transaction was not successful": "Transaction was not successful",
|
||||||
"Wallet rejected transaction": "Wallet rejected transaction"
|
"Wallet rejected transaction": "Wallet rejected transaction"
|
||||||
|
|||||||
@@ -2,16 +2,14 @@ import { type ReactNode } from 'react';
|
|||||||
import {
|
import {
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
formatNumberPercentage,
|
formatNumberPercentage,
|
||||||
getDateTimeFormat,
|
|
||||||
priceChange,
|
priceChange,
|
||||||
priceChangePercentage,
|
priceChangePercentage,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { PriceChangeCell, signedNumberCssClass } from '@vegaprotocol/datagrid';
|
import { signedNumberCssClass } from '@vegaprotocol/datagrid';
|
||||||
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useCandles } from '../../hooks/use-candles';
|
import { useCandles } from '../../hooks/use-candles';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useT } from '../../use-t';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
marketId?: string;
|
marketId?: string;
|
||||||
@@ -24,7 +22,6 @@ export const Last24hPriceChange = ({
|
|||||||
decimalPlaces,
|
decimalPlaces,
|
||||||
fallback,
|
fallback,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const t = useT();
|
|
||||||
const { oneDayCandles, fiveDaysCandles, error } = useCandles({
|
const { oneDayCandles, fiveDaysCandles, error } = useCandles({
|
||||||
marketId,
|
marketId,
|
||||||
});
|
});
|
||||||
@@ -35,56 +32,6 @@ export const Last24hPriceChange = ({
|
|||||||
return nonIdeal;
|
return nonIdeal;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fiveDaysCandles.length < 24) {
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<span className="justify-start">
|
|
||||||
{t(
|
|
||||||
'Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:',
|
|
||||||
{
|
|
||||||
start: getDateTimeFormat().format(
|
|
||||||
new Date(fiveDaysCandles[0].periodStart)
|
|
||||||
),
|
|
||||||
end: getDateTimeFormat().format(
|
|
||||||
new Date(
|
|
||||||
fiveDaysCandles[fiveDaysCandles.length - 1].periodStart
|
|
||||||
)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
<PriceChangeCell
|
|
||||||
candles={fiveDaysCandles.map((c) => c.close) || []}
|
|
||||||
decimalPlaces={decimalPlaces}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span>{nonIdeal}</span>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oneDayCandles.length < 24) {
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<span className="justify-start">
|
|
||||||
{t(
|
|
||||||
'24 hour change is unavailable at this time. The price change in the last 120 hours is:'
|
|
||||||
)}{' '}
|
|
||||||
<PriceChangeCell
|
|
||||||
candles={fiveDaysCandles.map((c) => c.close) || []}
|
|
||||||
decimalPlaces={decimalPlaces}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span>{nonIdeal}</span>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const candles = oneDayCandles?.map((c) => c.close) || [];
|
const candles = oneDayCandles?.map((c) => c.close) || [];
|
||||||
const change = priceChange(candles);
|
const change = priceChange(candles);
|
||||||
const changePercentage = priceChangePercentage(candles);
|
const changePercentage = priceChangePercentage(candles);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { calcCandleVolume, calcCandleVolumePrice } from '../../market-utils';
|
|||||||
import {
|
import {
|
||||||
addDecimalsFormatNumber,
|
addDecimalsFormatNumber,
|
||||||
formatNumber,
|
formatNumber,
|
||||||
getDateTimeFormat,
|
|
||||||
isNumeric,
|
isNumeric,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
@@ -37,83 +36,6 @@ export const Last24hVolume = ({
|
|||||||
return nonIdeal;
|
return nonIdeal;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fiveDaysCandles.length < 24) {
|
|
||||||
const candleVolume = calcCandleVolume(fiveDaysCandles);
|
|
||||||
const candleVolumePrice = calcCandleVolumePrice(
|
|
||||||
fiveDaysCandles,
|
|
||||||
marketDecimals,
|
|
||||||
positionDecimalPlaces
|
|
||||||
);
|
|
||||||
const candleVolumeValue =
|
|
||||||
candleVolume && isNumeric(positionDecimalPlaces)
|
|
||||||
? addDecimalsFormatNumber(
|
|
||||||
candleVolume,
|
|
||||||
positionDecimalPlaces,
|
|
||||||
formatDecimals
|
|
||||||
)
|
|
||||||
: '-';
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<div>
|
|
||||||
<span className="flex flex-col">
|
|
||||||
{t(
|
|
||||||
'Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}',
|
|
||||||
{
|
|
||||||
start: getDateTimeFormat().format(
|
|
||||||
new Date(fiveDaysCandles[0].periodStart)
|
|
||||||
),
|
|
||||||
end: getDateTimeFormat().format(
|
|
||||||
new Date(
|
|
||||||
fiveDaysCandles[fiveDaysCandles.length - 1].periodStart
|
|
||||||
)
|
|
||||||
),
|
|
||||||
candleVolumeValue,
|
|
||||||
candleVolumePrice,
|
|
||||||
quoteUnit,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span>{nonIdeal}</span>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oneDayCandles.length < 24) {
|
|
||||||
const candleVolume = calcCandleVolume(fiveDaysCandles);
|
|
||||||
const candleVolumePrice = calcCandleVolumePrice(
|
|
||||||
fiveDaysCandles,
|
|
||||||
marketDecimals,
|
|
||||||
positionDecimalPlaces
|
|
||||||
);
|
|
||||||
const candleVolumeValue =
|
|
||||||
candleVolume && isNumeric(positionDecimalPlaces)
|
|
||||||
? addDecimalsFormatNumber(
|
|
||||||
candleVolume,
|
|
||||||
positionDecimalPlaces,
|
|
||||||
formatDecimals
|
|
||||||
)
|
|
||||||
: '-';
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
description={
|
|
||||||
<div>
|
|
||||||
<span className="flex flex-col">
|
|
||||||
{t(
|
|
||||||
'24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of ({{candleVolumePrice}} {{quoteUnit}})',
|
|
||||||
{ candleVolumeValue, candleVolumePrice, quoteUnit }
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<span>{nonIdeal}</span>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const candleVolume = oneDayCandles
|
const candleVolume = oneDayCandles
|
||||||
? calcCandleVolume(oneDayCandles)
|
? calcCandleVolume(oneDayCandles)
|
||||||
: initialValue;
|
: initialValue;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const useCandles = ({ marketId }: { marketId?: string }) => {
|
|||||||
const fiveDaysAgo = useFiveDaysAgo();
|
const fiveDaysAgo = useFiveDaysAgo();
|
||||||
const yesterday = useYesterday();
|
const yesterday = useYesterday();
|
||||||
const since = new Date(fiveDaysAgo).toISOString();
|
const since = new Date(fiveDaysAgo).toISOString();
|
||||||
const { data, error } = useThrottledDataProvider({
|
const { data: fiveDaysCandles, error } = useThrottledDataProvider({
|
||||||
dataProvider: marketCandlesProvider,
|
dataProvider: marketCandlesProvider,
|
||||||
variables: {
|
variables: {
|
||||||
marketId: marketId || '',
|
marketId: marketId || '',
|
||||||
@@ -18,13 +18,6 @@ export const useCandles = ({ marketId }: { marketId?: string }) => {
|
|||||||
skip: !marketId,
|
skip: !marketId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fiveDaysCandles = data?.filter((c) => {
|
|
||||||
if (c.open === '' || c.close === '' || c.high === '' || c.close === '') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
const oneDayCandles = fiveDaysCandles?.filter((candle) =>
|
const oneDayCandles = fiveDaysCandles?.filter((candle) =>
|
||||||
isCandleLessThan24hOld(candle, yesterday)
|
isCandleLessThan24hOld(candle, yesterday)
|
||||||
);
|
);
|
||||||
|
|||||||
Generated
+12
@@ -936,6 +936,8 @@ export enum DispatchMetric {
|
|||||||
/** Dispatch strategy for a recurring transfer */
|
/** Dispatch strategy for a recurring transfer */
|
||||||
export type DispatchStrategy = {
|
export type DispatchStrategy = {
|
||||||
__typename?: 'DispatchStrategy';
|
__typename?: 'DispatchStrategy';
|
||||||
|
/** Optional multiplier on taker fees used to cap the rewards a party may receive in an epoch */
|
||||||
|
capRewardFeeMultiple?: Maybe<Scalars['String']>;
|
||||||
/** Defines the data that will be used to compare markets so as to distribute rewards appropriately */
|
/** Defines the data that will be used to compare markets so as to distribute rewards appropriately */
|
||||||
dispatchMetric: DispatchMetric;
|
dispatchMetric: DispatchMetric;
|
||||||
/** The asset to use for measuring contribution to the metric */
|
/** The asset to use for measuring contribution to the metric */
|
||||||
@@ -2391,6 +2393,8 @@ export type Market = {
|
|||||||
state: MarketState;
|
state: MarketState;
|
||||||
/** Optional: Market ID of the successor to this market if one exists */
|
/** Optional: Market ID of the successor to this market if one exists */
|
||||||
successorMarketID?: Maybe<Scalars['ID']>;
|
successorMarketID?: Maybe<Scalars['ID']>;
|
||||||
|
/** The market minimum tick size */
|
||||||
|
tickSize: Scalars['String'];
|
||||||
/** An instance of, or reference to, a tradable instrument. */
|
/** An instance of, or reference to, a tradable instrument. */
|
||||||
tradableInstrument: TradableInstrument;
|
tradableInstrument: TradableInstrument;
|
||||||
/** @deprecated Simplify and consolidate trades query and remove nesting. Use trades query instead */
|
/** @deprecated Simplify and consolidate trades query and remove nesting. Use trades query instead */
|
||||||
@@ -2816,6 +2820,8 @@ export type NewMarket = {
|
|||||||
riskParameters: RiskModel;
|
riskParameters: RiskModel;
|
||||||
/** Successor market configuration. If this proposed market is meant to succeed a given market, then this needs to be set. */
|
/** Successor market configuration. If this proposed market is meant to succeed a given market, then this needs to be set. */
|
||||||
successorConfiguration?: Maybe<SuccessorConfiguration>;
|
successorConfiguration?: Maybe<SuccessorConfiguration>;
|
||||||
|
/** The market minimum tick size */
|
||||||
|
tickSize: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Configuration for a new spot market on Vega */
|
/** Configuration for a new spot market on Vega */
|
||||||
@@ -2839,6 +2845,8 @@ export type NewSpotMarket = {
|
|||||||
riskParameters?: Maybe<RiskModel>;
|
riskParameters?: Maybe<RiskModel>;
|
||||||
/** Specifies parameters related to liquidity target stake calculation */
|
/** Specifies parameters related to liquidity target stake calculation */
|
||||||
targetStakeParameters: TargetStakeParameters;
|
targetStakeParameters: TargetStakeParameters;
|
||||||
|
/** The market minimum tick size */
|
||||||
|
tickSize: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NewTransfer = {
|
export type NewTransfer = {
|
||||||
@@ -7074,6 +7082,8 @@ export type UpdateMarketConfiguration = {
|
|||||||
quadraticSlippageFactor: Scalars['String'];
|
quadraticSlippageFactor: Scalars['String'];
|
||||||
/** Updated futures market risk model parameters. */
|
/** Updated futures market risk model parameters. */
|
||||||
riskParameters: UpdateMarketRiskParameters;
|
riskParameters: UpdateMarketRiskParameters;
|
||||||
|
/** The market minimum tick size */
|
||||||
|
tickSize: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateMarketLogNormalRiskModel = {
|
export type UpdateMarketLogNormalRiskModel = {
|
||||||
@@ -7171,6 +7181,8 @@ export type UpdateSpotMarketConfiguration = {
|
|||||||
riskParameters: RiskModel;
|
riskParameters: RiskModel;
|
||||||
/** Specifies parameters related to target stake calculation */
|
/** Specifies parameters related to target stake calculation */
|
||||||
targetStakeParameters: TargetStakeParameters;
|
targetStakeParameters: TargetStakeParameters;
|
||||||
|
/** The market minimum tick size */
|
||||||
|
tickSize: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateVolumeDiscountProgram = {
|
export type UpdateVolumeDiscountProgram = {
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ export const InputError = ({
|
|||||||
...props
|
...props
|
||||||
}: InputErrorProps) => {
|
}: InputErrorProps) => {
|
||||||
const effectiveClassName = classNames(
|
const effectiveClassName = classNames(
|
||||||
'text-sm flex items-center first-letter:uppercase',
|
'text-sm block items-center first-letter:capitalize',
|
||||||
'mt-2',
|
'mt-2 min-w-0 break-words',
|
||||||
{
|
{
|
||||||
'border-danger': intent === 'danger',
|
'border-danger': intent === 'danger',
|
||||||
'border-warning': intent === 'warning',
|
'border-warning': intent === 'warning',
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { type ReactNode, type FunctionComponent, forwardRef } from 'react';
|
import {
|
||||||
|
type ReactNode,
|
||||||
|
type FunctionComponent,
|
||||||
|
forwardRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
ConnectorErrors,
|
ConnectorErrors,
|
||||||
isBrowserWalletInstalled,
|
isBrowserWalletInstalled,
|
||||||
@@ -12,6 +17,7 @@ import { useConnect } from '../../hooks/use-connect';
|
|||||||
import { Links } from '../../constants';
|
import { Links } from '../../constants';
|
||||||
import { ConnectorIcon } from './connector-icon';
|
import { ConnectorIcon } from './connector-icon';
|
||||||
import { useUserAgent } from '@vegaprotocol/react-helpers';
|
import { useUserAgent } from '@vegaprotocol/react-helpers';
|
||||||
|
import { Trans } from 'react-i18next';
|
||||||
|
|
||||||
const vegaExtensionsLinks = {
|
const vegaExtensionsLinks = {
|
||||||
chrome: Links.chromeExtension,
|
chrome: Links.chromeExtension,
|
||||||
@@ -29,48 +35,67 @@ export const ConnectionOptions = ({
|
|||||||
onConnect: (id: ConnectorType) => void;
|
onConnect: (id: ConnectorType) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const error = useWallet((store) => store.error);
|
|
||||||
const { connectors } = useConnect();
|
const { connectors } = useConnect();
|
||||||
|
const error = useWallet((store) => store.error);
|
||||||
|
const [isInstalling, setIsInstalling] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-start gap-4">
|
<div className="flex flex-col items-start gap-4">
|
||||||
<h2 className="text-xl">{t('Connect to Vega')}</h2>
|
<h2 className="text-xl">{t('Connect to Vega')}</h2>
|
||||||
<ul
|
{isInstalling ? (
|
||||||
className="grid grid-cols-1 sm:grid-cols-2 gap-1 -mx-2"
|
<p className="text-warning">
|
||||||
data-testid="connectors-list"
|
<Trans
|
||||||
>
|
i18nKey="Once you have the added the extension, <0>refresh</0> you browser."
|
||||||
{connectors.map((c) => {
|
components={[
|
||||||
const ConnectionOption = ConnectionOptionRecord[c.id];
|
<button
|
||||||
const props = {
|
onClick={() => window.location.reload()}
|
||||||
id: c.id,
|
className="underline underline-offset-4"
|
||||||
name: c.name,
|
/>,
|
||||||
description: c.description,
|
]}
|
||||||
showDescription: false,
|
/>
|
||||||
onClick: () => onConnect(c.id),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ConnectionOption) {
|
|
||||||
return (
|
|
||||||
<li key={c.id}>
|
|
||||||
<ConnectionOption {...props} />
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li key={c.id}>
|
|
||||||
<ConnectionOptionDefault {...props} />
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
{error && error.code !== ConnectorErrors.userRejected.code && (
|
|
||||||
<p
|
|
||||||
className="text-danger text-sm first-letter:uppercase"
|
|
||||||
data-testid="connection-error"
|
|
||||||
>
|
|
||||||
{error.message}
|
|
||||||
</p>
|
</p>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<ul
|
||||||
|
className="grid grid-cols-1 sm:grid-cols-2 gap-1 -mx-2"
|
||||||
|
data-testid="connectors-list"
|
||||||
|
>
|
||||||
|
{connectors.map((c) => {
|
||||||
|
const ConnectionOption = ConnectionOptionRecord[c.id];
|
||||||
|
const props = {
|
||||||
|
id: c.id,
|
||||||
|
name: c.name,
|
||||||
|
description: c.description,
|
||||||
|
showDescription: false,
|
||||||
|
onClick: () => onConnect(c.id),
|
||||||
|
onInstall: () => setIsInstalling(true),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ConnectionOption) {
|
||||||
|
return (
|
||||||
|
<li key={c.id}>
|
||||||
|
<ConnectionOption {...props} />
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={c.id}>
|
||||||
|
<ConnectionOptionDefault {...props} />
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
{error && error.code !== ConnectorErrors.userRejected.code && (
|
||||||
|
<p
|
||||||
|
className="text-danger text-sm first-letter:uppercase"
|
||||||
|
data-testid="connection-error"
|
||||||
|
>
|
||||||
|
{error.message}
|
||||||
|
{error.data ? `: ${error.data}` : ''}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<a
|
<a
|
||||||
href={Links.walletOverview}
|
href={Links.walletOverview}
|
||||||
@@ -90,6 +115,7 @@ interface ConnectionOptionProps {
|
|||||||
description: string;
|
description: string;
|
||||||
showDescription?: boolean;
|
showDescription?: boolean;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
|
onInstall?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONNECTION_OPTION_CLASSES =
|
const CONNECTION_OPTION_CLASSES =
|
||||||
@@ -142,6 +168,7 @@ export const ConnectionOptionInjected = ({
|
|||||||
description,
|
description,
|
||||||
showDescription = false,
|
showDescription = false,
|
||||||
onClick,
|
onClick,
|
||||||
|
onInstall,
|
||||||
}: ConnectionOptionProps) => {
|
}: ConnectionOptionProps) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const userAgent = useUserAgent();
|
const userAgent = useUserAgent();
|
||||||
@@ -158,7 +185,11 @@ export const ConnectionOptionInjected = ({
|
|||||||
</span>
|
</span>
|
||||||
</ConnectionOptionButtonWithDescription>
|
</ConnectionOptionButtonWithDescription>
|
||||||
) : (
|
) : (
|
||||||
<ConnectionOptionLinkWithDescription id={id} href={link}>
|
<ConnectionOptionLinkWithDescription
|
||||||
|
id={id}
|
||||||
|
href={link}
|
||||||
|
onClick={onInstall}
|
||||||
|
>
|
||||||
<span className="flex flex-col justify-start text-left">
|
<span className="flex flex-col justify-start text-left">
|
||||||
<span className="capitalize leading-5">
|
<span className="capitalize leading-5">
|
||||||
{t('Get the Vega Wallet')}
|
{t('Get the Vega Wallet')}
|
||||||
@@ -183,7 +214,7 @@ export const ConnectionOptionInjected = ({
|
|||||||
{name}
|
{name}
|
||||||
</ConnectionOptionButton>
|
</ConnectionOptionButton>
|
||||||
) : (
|
) : (
|
||||||
<ConnectionOptionLink id={id} href={link}>
|
<ConnectionOptionLink id={id} href={link} onClick={onInstall}>
|
||||||
{t('Get the Vega Wallet')}
|
{t('Get the Vega Wallet')}
|
||||||
</ConnectionOptionLink>
|
</ConnectionOptionLink>
|
||||||
)}
|
)}
|
||||||
@@ -275,8 +306,9 @@ const ConnectionOptionLink = forwardRef<
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
id: ConnectorType;
|
id: ConnectorType;
|
||||||
href: string;
|
href: string;
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
>(({ children, id, href }, ref) => {
|
>(({ children, id, href, onClick }, ref) => {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
href={href}
|
href={href}
|
||||||
@@ -285,6 +317,7 @@ const ConnectionOptionLink = forwardRef<
|
|||||||
className={CONNECTION_OPTION_CLASSES}
|
className={CONNECTION_OPTION_CLASSES}
|
||||||
data-testid={`connector-${id}`}
|
data-testid={`connector-${id}`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<ConnectorIcon id={id} />
|
<ConnectorIcon id={id} />
|
||||||
{children}
|
{children}
|
||||||
@@ -320,8 +353,10 @@ const ConnectionOptionLinkWithDescription = forwardRef<
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
id: ConnectorType;
|
id: ConnectorType;
|
||||||
href: string;
|
href: string;
|
||||||
|
|
||||||
|
onClick?: () => void;
|
||||||
}
|
}
|
||||||
>(({ children, id, href }, ref) => {
|
>(({ children, id, href, onClick }, ref) => {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@@ -329,6 +364,7 @@ const ConnectionOptionLinkWithDescription = forwardRef<
|
|||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
<ConnectorIcon id={id} />
|
<ConnectorIcon id={id} />
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useWallet } from './use-wallet';
|
import { useWallet } from './use-wallet';
|
||||||
import { useConnect } from './use-connect';
|
import { useConnect } from './use-connect';
|
||||||
|
|
||||||
export function useEagerConnect() {
|
export function useEagerConnect() {
|
||||||
const current = useWallet((store) => store.current);
|
const current = useWallet((store) => store.current);
|
||||||
|
const status = useWallet((store) => store.status);
|
||||||
const { connect } = useConnect();
|
const { connect } = useConnect();
|
||||||
const [connecting, setConnecting] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const attemptConnect = async () => {
|
const attemptConnect = async () => {
|
||||||
// No stored config, or config was malformed or no risk accepted
|
// No stored config, or config was malformed or no risk accepted
|
||||||
if (!current) {
|
if (!current) {
|
||||||
setConnecting(false);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status !== 'disconnected') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,15 +22,13 @@ export function useEagerConnect() {
|
|||||||
await connect(current);
|
await connect(current);
|
||||||
} catch {
|
} catch {
|
||||||
console.warn(`Failed to connect with connector: ${current}`);
|
console.warn(`Failed to connect with connector: ${current}`);
|
||||||
} finally {
|
|
||||||
setConnecting(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
attemptConnect();
|
attemptConnect();
|
||||||
}
|
}
|
||||||
}, [connect, current, connecting]);
|
}, [status, connect, current]);
|
||||||
|
|
||||||
return connecting;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ export const useSimpleTransaction = (opts?: Options) => {
|
|||||||
const [result, setResult] = useState<Result>();
|
const [result, setResult] = useState<Result>();
|
||||||
const [error, setError] = useState<string>();
|
const [error, setError] = useState<string>();
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
setStatus('idle');
|
||||||
|
setResult(undefined);
|
||||||
|
setError(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
const send = async (tx: Transaction) => {
|
const send = async (tx: Transaction) => {
|
||||||
if (!pubKey) {
|
if (!pubKey) {
|
||||||
throw new Error('no pubKey');
|
throw new Error('no pubKey');
|
||||||
@@ -59,12 +65,12 @@ export const useSimpleTransaction = (opts?: Options) => {
|
|||||||
if (err.code === ConnectorErrors.userRejected.code) {
|
if (err.code === ConnectorErrors.userRejected.code) {
|
||||||
setStatus('idle');
|
setStatus('idle');
|
||||||
} else {
|
} else {
|
||||||
setError(err.message);
|
setError(`${err.message}${err.data ? `: ${err.data}` : ''}`);
|
||||||
setStatus('idle');
|
setStatus('idle');
|
||||||
opts?.onError?.(err.message);
|
opts?.onError?.(err.message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const msg = t('Wallet rejected transaction');
|
const msg = t('Something went wrong');
|
||||||
setError(msg);
|
setError(msg);
|
||||||
setStatus('idle');
|
setStatus('idle');
|
||||||
opts?.onError?.(msg);
|
opts?.onError?.(msg);
|
||||||
@@ -114,5 +120,6 @@ export const useSimpleTransaction = (opts?: Options) => {
|
|||||||
error,
|
error,
|
||||||
status,
|
status,
|
||||||
send,
|
send,
|
||||||
|
reset,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
listKeysError,
|
listKeysError,
|
||||||
noWalletError,
|
noWalletError,
|
||||||
sendTransactionError,
|
sendTransactionError,
|
||||||
|
userRejectedError,
|
||||||
} from '../errors';
|
} from '../errors';
|
||||||
import {
|
import {
|
||||||
type TransactionParams,
|
type TransactionParams,
|
||||||
@@ -14,6 +15,19 @@ import {
|
|||||||
type VegaWalletEvent,
|
type VegaWalletEvent,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
|
interface InjectedError {
|
||||||
|
message: string;
|
||||||
|
code: number;
|
||||||
|
data:
|
||||||
|
| {
|
||||||
|
message: string;
|
||||||
|
code: number;
|
||||||
|
}
|
||||||
|
| string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const USER_REJECTED_CODE = -4;
|
||||||
|
|
||||||
export class InjectedConnector implements Connector {
|
export class InjectedConnector implements Connector {
|
||||||
readonly id = 'injected';
|
readonly id = 'injected';
|
||||||
readonly name = 'Vega Wallet';
|
readonly name = 'Vega Wallet';
|
||||||
@@ -85,15 +99,55 @@ export class InjectedConnector implements Connector {
|
|||||||
sentAt: res.sentAt,
|
sentAt: res.sentAt,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (this.isInjectedError(err)) {
|
||||||
|
if (err.code === USER_REJECTED_CODE) {
|
||||||
|
throw userRejectedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof err.data === 'string') {
|
||||||
|
throw sendTransactionError(err.data);
|
||||||
|
} else {
|
||||||
|
throw sendTransactionError(err.data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw sendTransactionError();
|
throw sendTransactionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
on(event: VegaWalletEvent, callback: () => void) {
|
on(event: VegaWalletEvent, callback: () => void) {
|
||||||
window.vega.on(event, callback);
|
// Check for on/off in case user is on older versions which don't support it
|
||||||
|
// We can remove this check once FF is at the latest version
|
||||||
|
if (
|
||||||
|
typeof window.vega !== 'undefined' &&
|
||||||
|
typeof window.vega.on === 'function'
|
||||||
|
) {
|
||||||
|
window.vega.on(event, callback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
off(event: VegaWalletEvent, callback: () => void) {
|
off(event: VegaWalletEvent, callback: () => void) {
|
||||||
window.vega.off(event, callback);
|
// Check for on/off in case user is on older versions which don't support it
|
||||||
|
// We can remove this check once FF is at the latest version
|
||||||
|
if (
|
||||||
|
typeof window.vega !== 'undefined' &&
|
||||||
|
typeof window.vega.off === 'function'
|
||||||
|
) {
|
||||||
|
window.vega.off(event, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private isInjectedError(obj: unknown): obj is InjectedError {
|
||||||
|
if (
|
||||||
|
obj !== undefined &&
|
||||||
|
obj !== null &&
|
||||||
|
typeof obj === 'object' &&
|
||||||
|
'code' in obj &&
|
||||||
|
'message' in obj &&
|
||||||
|
'data' in obj
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import {
|
|||||||
|
|
||||||
type JsonRpcConnectorConfig = { url: string; token?: string };
|
type JsonRpcConnectorConfig = { url: string; token?: string };
|
||||||
|
|
||||||
|
const USER_REJECTED_CODE = 3001;
|
||||||
|
|
||||||
export class JsonRpcConnector implements Connector {
|
export class JsonRpcConnector implements Connector {
|
||||||
readonly id = 'jsonRpc';
|
readonly id = 'jsonRpc';
|
||||||
readonly name = 'Command Line Wallet';
|
readonly name = 'Command Line Wallet';
|
||||||
@@ -27,7 +29,7 @@ export class JsonRpcConnector implements Connector {
|
|||||||
requestId: number = 0;
|
requestId: number = 0;
|
||||||
store: StoreApi<Store> | undefined;
|
store: StoreApi<Store> | undefined;
|
||||||
pollRef: NodeJS.Timer | undefined;
|
pollRef: NodeJS.Timer | undefined;
|
||||||
ee: EventEmitter;
|
ee: InstanceType<typeof EventEmitter>;
|
||||||
|
|
||||||
constructor(config: JsonRpcConnectorConfig) {
|
constructor(config: JsonRpcConnectorConfig) {
|
||||||
this.url = config.url;
|
this.url = config.url;
|
||||||
@@ -63,7 +65,7 @@ export class JsonRpcConnector implements Connector {
|
|||||||
const token = response.headers.get('Authorization');
|
const token = response.headers.get('Authorization');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if ('error' in data && data.error.code === 3001) {
|
if ('error' in data && data.error.code === USER_REJECTED_CODE) {
|
||||||
throw userRejectedError();
|
throw userRejectedError();
|
||||||
}
|
}
|
||||||
throw connectError('response not ok');
|
throw connectError('response not ok');
|
||||||
@@ -137,7 +139,7 @@ export class JsonRpcConnector implements Connector {
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if ('error' in data) {
|
if ('error' in data) {
|
||||||
if (data.error.code === 3001) {
|
if (data.error.code === USER_REJECTED_CODE) {
|
||||||
throw userRejectedError();
|
throw userRejectedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import EventEmitter from 'eventemitter3';
|
|
||||||
import {
|
import {
|
||||||
ConnectorError,
|
ConnectorError,
|
||||||
chainIdError,
|
chainIdError,
|
||||||
@@ -6,13 +5,13 @@ import {
|
|||||||
listKeysError,
|
listKeysError,
|
||||||
noWalletError,
|
noWalletError,
|
||||||
sendTransactionError,
|
sendTransactionError,
|
||||||
|
userRejectedError,
|
||||||
} from '../errors';
|
} from '../errors';
|
||||||
import { type Transaction } from '../transaction-types';
|
import { type Transaction } from '../transaction-types';
|
||||||
import {
|
import {
|
||||||
JsonRpcMethod,
|
JsonRpcMethod,
|
||||||
type Connector,
|
type Connector,
|
||||||
type TransactionParams,
|
type TransactionParams,
|
||||||
type VegaWalletEvent,
|
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
enum EthereumMethod {
|
enum EthereumMethod {
|
||||||
@@ -43,7 +42,6 @@ declare global {
|
|||||||
type WindowEthereumProvider = {
|
type WindowEthereumProvider = {
|
||||||
isMetaMask: boolean;
|
isMetaMask: boolean;
|
||||||
request<T = unknown>(args: RequestArguments): Promise<T>;
|
request<T = unknown>(args: RequestArguments): Promise<T>;
|
||||||
selectedAddress: string | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -52,6 +50,16 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SnapRPCError {
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
data?: {
|
||||||
|
originalError: { code: number };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const USER_REJECTED_CODE = -4;
|
||||||
|
|
||||||
export class SnapConnector implements Connector {
|
export class SnapConnector implements Connector {
|
||||||
readonly id = 'snap';
|
readonly id = 'snap';
|
||||||
readonly name = 'MetaMask Snap';
|
readonly name = 'MetaMask Snap';
|
||||||
@@ -61,8 +69,6 @@ export class SnapConnector implements Connector {
|
|||||||
node: string;
|
node: string;
|
||||||
version: string;
|
version: string;
|
||||||
snapId: string;
|
snapId: string;
|
||||||
pollRef: NodeJS.Timer | undefined;
|
|
||||||
ee: EventEmitter;
|
|
||||||
|
|
||||||
// Note: apps may not know which node is selected on start up so its up
|
// Note: apps may not know which node is selected on start up so its up
|
||||||
// to the app to make sure class intances are renewed if the node changes
|
// to the app to make sure class intances are renewed if the node changes
|
||||||
@@ -70,14 +76,21 @@ export class SnapConnector implements Connector {
|
|||||||
this.node = config.node;
|
this.node = config.node;
|
||||||
this.version = config.version;
|
this.version = config.version;
|
||||||
this.snapId = config.snapId;
|
this.snapId = config.snapId;
|
||||||
this.ee = new EventEmitter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bindStore() {}
|
bindStore() {}
|
||||||
|
|
||||||
async connectWallet(desiredChainId: string) {
|
async connectWallet(desiredChainId: string) {
|
||||||
try {
|
try {
|
||||||
await this.requestSnap();
|
const res = await this.requestSnap();
|
||||||
|
|
||||||
|
if (res[this.snapId].blocked) {
|
||||||
|
throw connectError('snap is blocked');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res[this.snapId].enabled) {
|
||||||
|
throw connectError('snap is not enabled');
|
||||||
|
}
|
||||||
|
|
||||||
const { chainId } = await this.getChainId();
|
const { chainId } = await this.getChainId();
|
||||||
|
|
||||||
@@ -87,7 +100,6 @@ export class SnapConnector implements Connector {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.startPoll();
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ConnectorError) {
|
if (err instanceof ConnectorError) {
|
||||||
@@ -98,57 +110,66 @@ export class SnapConnector implements Connector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async disconnectWallet() {
|
async disconnectWallet() {}
|
||||||
this.stopPoll();
|
|
||||||
}
|
|
||||||
|
|
||||||
// deprecated, pass chain on connect
|
// deprecated, pass chain on connect
|
||||||
async getChainId() {
|
async getChainId() {
|
||||||
try {
|
try {
|
||||||
const res = await this.invokeSnap<{ chainID: string }>(
|
const data = await this.invokeSnap<{ chainID: string }>(
|
||||||
JsonRpcMethod.GetChainId,
|
JsonRpcMethod.GetChainId,
|
||||||
{
|
{
|
||||||
networkEndpoints: [this.node],
|
networkEndpoints: [this.node],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return { chainId: res.chainID };
|
|
||||||
|
if ('error' in data) {
|
||||||
|
throw chainIdError(data.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { chainId: data.chainID };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.stopPoll();
|
if (err instanceof ConnectorError) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
throw chainIdError();
|
throw chainIdError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async listKeys() {
|
async listKeys() {
|
||||||
try {
|
try {
|
||||||
const res = await this.invokeSnap<{
|
const data = await this.invokeSnap<{
|
||||||
keys: Array<{ publicKey: string; name: string }>;
|
keys: Array<{ publicKey: string; name: string }>;
|
||||||
}>(JsonRpcMethod.ListKeys);
|
}>(JsonRpcMethod.ListKeys);
|
||||||
return res.keys;
|
|
||||||
|
if ('error' in data) {
|
||||||
|
throw listKeysError(data.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.keys;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.stopPoll();
|
if (err instanceof ConnectorError) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
throw listKeysError();
|
throw listKeysError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async isConnected() {
|
async isConnected() {
|
||||||
try {
|
try {
|
||||||
// Check if metamask is unlocked
|
|
||||||
if (!window.ethereum.selectedAddress) {
|
|
||||||
throw noWalletError();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this throws its likely the snap is disabled or has been uninstalled
|
// If this throws its likely the snap is disabled or has been uninstalled
|
||||||
await this.listKeys();
|
await this.listKeys();
|
||||||
return { connected: true };
|
return { connected: true };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.stopPoll();
|
|
||||||
return { connected: false };
|
return { connected: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendTransaction(params: TransactionParams) {
|
async sendTransaction(params: TransactionParams) {
|
||||||
try {
|
try {
|
||||||
const res = await this.invokeSnap<{
|
// If the transaction is invalid this will throw with SnapRPCError
|
||||||
|
// but if its rejected it will resolve with 'error' in data
|
||||||
|
const data = await this.invokeSnap<{
|
||||||
transactionHash: string;
|
transactionHash: string;
|
||||||
transaction: { signature: { value: string } };
|
transaction: { signature: { value: string } };
|
||||||
receivedAt: string;
|
receivedAt: string;
|
||||||
@@ -160,115 +181,99 @@ export class SnapConnector implements Connector {
|
|||||||
networkEndpoints: [this.node],
|
networkEndpoints: [this.node],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ('error' in data) {
|
||||||
|
if (data.error.code === USER_REJECTED_CODE) {
|
||||||
|
throw userRejectedError();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw sendTransactionError(`${data.error.message}: ${data.error.data}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transactionHash: res.transactionHash,
|
transactionHash: data.transactionHash,
|
||||||
signature: res.transaction.signature.value,
|
signature: data.transaction.signature.value,
|
||||||
receivedAt: res.receivedAt,
|
receivedAt: data.receivedAt,
|
||||||
sentAt: res.sentAt,
|
sentAt: data.sentAt,
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof ConnectorError) {
|
if (err instanceof ConnectorError) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isSnapRPCError(err)) {
|
||||||
|
throw sendTransactionError(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
throw sendTransactionError();
|
throw sendTransactionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
on(event: VegaWalletEvent, callback: () => void) {
|
on() {}
|
||||||
this.ee.on(event, callback);
|
|
||||||
}
|
off() {}
|
||||||
|
|
||||||
off(event: VegaWalletEvent, callback?: () => void) {
|
|
||||||
this.ee.off(event, callback);
|
|
||||||
}
|
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
// Snap methods
|
// Snap methods
|
||||||
////////////////////////////////////
|
////////////////////////////////////
|
||||||
|
|
||||||
private startPoll() {
|
|
||||||
// This only event we need to poll for right now is client.disconnect,
|
|
||||||
// if more events get added we will need more logic here
|
|
||||||
this.pollRef = setInterval(async () => {
|
|
||||||
const result = await this.isConnected();
|
|
||||||
if (result.connected) return;
|
|
||||||
this.ee.emit('client.disconnected');
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
private stopPoll() {
|
|
||||||
if (this.pollRef) {
|
|
||||||
clearInterval(this.pollRef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests permission for a website to communicate with the specified snaps
|
* Requests permission for a website to communicate with the specified snaps
|
||||||
* and attempts to install them if they're not already installed.
|
* 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.
|
* 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
|
* More informations here: https://docs.metamask.io/snaps/reference/rpc-api/#wallet_requestsnaps
|
||||||
*/
|
*/
|
||||||
private async requestSnap() {
|
private async requestSnap(): Promise<{
|
||||||
await this.request(EthereumMethod.RequestSnaps, {
|
[snapId: string]: {
|
||||||
[this.snapId]: {
|
blocked: boolean;
|
||||||
version: this.version,
|
enabled: boolean;
|
||||||
|
id: string;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
}> {
|
||||||
|
return window.ethereum.request({
|
||||||
|
method: EthereumMethod.RequestSnaps,
|
||||||
|
params: {
|
||||||
|
[this.snapId]: {
|
||||||
|
version: this.version,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check if this is needed, its used in use-snap-status
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Gets the list of all installed snaps.
|
|
||||||
// * More information here: https://docs.metamask.io/snaps/reference/rpc-api/#wallet_getsnaps
|
|
||||||
// */
|
|
||||||
// async getSnap() {
|
|
||||||
// const snaps = await this.request(EthereumMethod.GetSnaps);
|
|
||||||
// return Object.values(snaps).find(
|
|
||||||
// (s) => s.id === this.snapId && s.version === this.version
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calls a method on the specified snap, always vega in this case
|
* Calls a method on the specified snap, always vega in this case
|
||||||
* should always be npm:@vegaprotocol/snap
|
* should always be npm:@vegaprotocol/snap
|
||||||
*/
|
*/
|
||||||
private async invokeSnap<TResult>(
|
private async invokeSnap<TResult>(
|
||||||
method: JsonRpcMethod,
|
method: JsonRpcMethod,
|
||||||
params?: SnapInvocationParams
|
params: SnapInvocationParams = {}
|
||||||
): Promise<TResult> {
|
): Promise<TResult | { error: SnapRPCError }> {
|
||||||
return await this.request(EthereumMethod.InvokeSnap, {
|
// MetaMask in Firefox doesn't like undefined properties or some properties
|
||||||
snapId: this.snapId,
|
// on __proto__ so we need to strip them out with JSON.strinfify
|
||||||
request: {
|
params = JSON.parse(JSON.stringify(params));
|
||||||
method,
|
|
||||||
params,
|
return window.ethereum.request({
|
||||||
|
method: EthereumMethod.InvokeSnap,
|
||||||
|
params: {
|
||||||
|
snapId: this.snapId,
|
||||||
|
request: {
|
||||||
|
method,
|
||||||
|
params,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private isSnapRPCError(obj: unknown): obj is SnapRPCError {
|
||||||
* Calls window.ethereum.request with method and params
|
if (
|
||||||
*/
|
obj !== undefined &&
|
||||||
private async request<TResult>(
|
obj !== null &&
|
||||||
method: EthereumMethod,
|
typeof obj === 'object' &&
|
||||||
params?: object
|
'code' in obj &&
|
||||||
): Promise<TResult> {
|
'message' in obj
|
||||||
if (window.ethereum?.request && window.ethereum?.isMetaMask) {
|
) {
|
||||||
// MetaMask in Firefox doesn't like undefined properties or some properties
|
return true;
|
||||||
// on __proto__ so we need to strip them out with JSON.strinfify
|
|
||||||
try {
|
|
||||||
params = JSON.parse(JSON.stringify(params));
|
|
||||||
} catch (err) {
|
|
||||||
throw sendTransactionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
return window.ethereum.request({
|
|
||||||
method,
|
|
||||||
params,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
throw noWalletError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class ConnectorError extends Error {
|
|||||||
|
|
||||||
export const ConnectorErrors = {
|
export const ConnectorErrors = {
|
||||||
userRejected: { message: 'user rejected', code: 0 },
|
userRejected: { message: 'user rejected', code: 0 },
|
||||||
noConnector: { message: 'no connector', code: 1 },
|
noConnector: { message: 'not connected', code: 1 },
|
||||||
connect: { message: 'failed to connect', code: 2 },
|
connect: { message: 'failed to connect', code: 2 },
|
||||||
disconnect: { message: 'failed to disconnect', code: 3 },
|
disconnect: { message: 'failed to disconnect', code: 3 },
|
||||||
chainId: { message: 'incorrect chain id', code: 4 },
|
chainId: { message: 'incorrect chain id', code: 4 },
|
||||||
|
|||||||
@@ -492,6 +492,14 @@ export interface UpdateMarginMode {
|
|||||||
export interface UpdateMarginModeBody {
|
export interface UpdateMarginModeBody {
|
||||||
updateMarginMode: UpdateMarginMode;
|
updateMarginMode: UpdateMarginMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UpdatePartyProfile {
|
||||||
|
updatePartyProfile: {
|
||||||
|
alias: string;
|
||||||
|
metadata: Array<{ key: string; value: string }>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type Transaction =
|
export type Transaction =
|
||||||
| UpdateMarginModeBody
|
| UpdateMarginModeBody
|
||||||
| StopOrdersSubmissionBody
|
| StopOrdersSubmissionBody
|
||||||
@@ -510,7 +518,8 @@ export type Transaction =
|
|||||||
| ApplyReferralCode
|
| ApplyReferralCode
|
||||||
| JoinTeam
|
| JoinTeam
|
||||||
| CreateReferralSet
|
| CreateReferralSet
|
||||||
| UpdateReferralSet;
|
| UpdateReferralSet
|
||||||
|
| UpdatePartyProfile;
|
||||||
|
|
||||||
export interface TransactionResponse {
|
export interface TransactionResponse {
|
||||||
transactionHash: string;
|
transactionHash: string;
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ describe('disconnect', () => {
|
|||||||
expect(result).toEqual({ status: 'disconnected' });
|
expect(result).toEqual({ status: 'disconnected' });
|
||||||
expect(config.store.getState()).toMatchObject({
|
expect(config.store.getState()).toMatchObject({
|
||||||
status: 'disconnected',
|
status: 'disconnected',
|
||||||
error: noConnectorError(),
|
|
||||||
current: undefined,
|
current: undefined,
|
||||||
keys: [],
|
keys: [],
|
||||||
pubKey: undefined,
|
pubKey: undefined,
|
||||||
@@ -130,7 +129,7 @@ describe('refresh keys', () => {
|
|||||||
it('handles invalid connector', async () => {
|
it('handles invalid connector', async () => {
|
||||||
await config.refreshKeys();
|
await config.refreshKeys();
|
||||||
expect(config.store.getState()).toMatchObject({
|
expect(config.store.getState()).toMatchObject({
|
||||||
error: noConnectorError(),
|
error: undefined,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -132,18 +132,20 @@ export function createConfig(cfg: Config): Wallet {
|
|||||||
store.setState(getInitialState(), true);
|
store.setState(getInitialState(), true);
|
||||||
return { status: 'disconnected' as const };
|
return { status: 'disconnected' as const };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
store.setState({
|
store.setState(getInitialState(), true);
|
||||||
...getInitialState(),
|
|
||||||
error: err instanceof ConnectorError ? err : unknownError(),
|
|
||||||
});
|
|
||||||
return { status: 'disconnected' as const };
|
return { status: 'disconnected' as const };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshKeys() {
|
async function refreshKeys() {
|
||||||
const connector = connectors
|
const state = store.getState();
|
||||||
.getState()
|
const connector = connectors.getState().find((x) => x.id === state.current);
|
||||||
.find((x) => x.id === store.getState().current);
|
|
||||||
|
// Only refresh keys if connnected. If you aren't connect when you connect
|
||||||
|
// you will get the latest keys
|
||||||
|
if (state.status !== 'connected') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!connector) {
|
if (!connector) {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
"build:all": "nx run-many --all --target=build",
|
"build:all": "nx run-many --all --target=build",
|
||||||
"build-spec:all": "nx run-many --all --target=build-spec",
|
"build-spec:all": "nx run-many --all --target=build-spec",
|
||||||
"lint:all": "nx run-many --all --target=lint",
|
"lint:all": "nx run-many --all --target=lint",
|
||||||
"e2e:all": "nx run-many --all --target=e2e",
|
|
||||||
"vegacapsule": "vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl",
|
"vegacapsule": "vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl",
|
||||||
"release": "git checkout develop ; git pull ; node scripts/make-release.js",
|
"release": "git checkout develop ; git pull ; node scripts/make-release.js",
|
||||||
"trading:test": "cd apps/trading/e2e && poetry run pytest -k",
|
"trading:test": "cd apps/trading/e2e && poetry run pytest -k",
|
||||||
|
|||||||
+44
-35
@@ -7,18 +7,18 @@ projects = []
|
|||||||
projects_e2e = []
|
projects_e2e = []
|
||||||
|
|
||||||
previews = {
|
previews = {
|
||||||
'governance': 'not deployed',
|
'governance': 'not deployed',
|
||||||
'explorer': 'not deployed',
|
'explorer': 'not deployed',
|
||||||
'trading': 'not deployed',
|
'trading': 'not deployed',
|
||||||
'tools': 'not deployed',
|
'tools': 'not deployed',
|
||||||
}
|
}
|
||||||
|
|
||||||
main_apps = ['governance', 'explorer', 'trading']
|
main_apps = ['governance', 'explorer', 'trading']
|
||||||
|
|
||||||
preview_governance="not deployed"
|
preview_governance = "not deployed"
|
||||||
preview_trading="not deployed"
|
preview_trading = "not deployed"
|
||||||
preview_explorer="not deployed"
|
preview_explorer = "not deployed"
|
||||||
preview_tools="not deployed"
|
preview_tools = "not deployed"
|
||||||
|
|
||||||
# take input from the pipeline
|
# take input from the pipeline
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
@@ -30,7 +30,8 @@ parser.add_argument('--event-name', help='name of event in CI')
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# run yarn affected command
|
# run yarn affected command
|
||||||
affected=check_output(f'yarn nx print-affected --base={environ["NX_BASE"]} --head={environ["NX_HEAD"]} --select=projects'.split()).decode('utf-8')
|
affected = check_output(
|
||||||
|
f'yarn nx print-affected --base={environ["NX_BASE"]} --head={environ["NX_HEAD"]} --select=projects'.split()).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
# print useful information
|
# print useful information
|
||||||
@@ -44,46 +45,54 @@ print(affected)
|
|||||||
print(">>>> eof debug")
|
print(">>>> eof debug")
|
||||||
|
|
||||||
# define affection actions -> add to projects arrays and generate preview link
|
# define affection actions -> add to projects arrays and generate preview link
|
||||||
|
|
||||||
|
|
||||||
def affect_app(app, preview_name=None):
|
def affect_app(app, preview_name=None):
|
||||||
print(f"{app} is affected")
|
print(f"{app} is affected")
|
||||||
projects.append(app)
|
projects.append(app)
|
||||||
if not preview_name:
|
if not preview_name:
|
||||||
preview_name=app
|
preview_name = app
|
||||||
previews[app] = f'https://{preview_name}.{args.branch_slug}.vega.rocks'
|
previews[app] = f'https://{preview_name}.{args.branch_slug}.vega.rocks'
|
||||||
|
|
||||||
|
|
||||||
# check appearance in the affected string for main apps
|
# check appearance in the affected string for main apps
|
||||||
for app in main_apps:
|
for app in main_apps:
|
||||||
if app in affected:
|
if app in affected:
|
||||||
affect_app(app)
|
affect_app(app)
|
||||||
|
|
||||||
# if non of main apps is affected - test all of them
|
# if non of main apps is affected - test all of them
|
||||||
if not projects:
|
if not projects:
|
||||||
for app in main_apps:
|
for app in main_apps:
|
||||||
affect_app(app)
|
affect_app(app)
|
||||||
|
|
||||||
# generate e2e targets
|
# generate e2e targets
|
||||||
projects_e2e = [f'{app}-e2e' for app in projects]
|
projects_e2e = [f'{app}-e2e' for app in projects]
|
||||||
|
|
||||||
|
# remove trading-e2e because it doesn't exists any more (new target is: console-e2e)
|
||||||
|
if "trading-e2e" in projects_e2e:
|
||||||
|
projects_e2e.remove("trading-e2e")
|
||||||
|
|
||||||
# check affection for multisig-signer which is deployed only from develop and pull requests
|
# check affection for multisig-signer which is deployed only from develop and pull requests
|
||||||
if args.event_name == 'pull_request' or 'develop' in args.github_ref:
|
if args.event_name == 'pull_request' or 'develop' in args.github_ref:
|
||||||
if 'multisig-signer' in affected:
|
if 'multisig-signer' in affected:
|
||||||
affect_app('multisig-signer', 'tools')
|
affect_app('multisig-signer', 'tools')
|
||||||
|
|
||||||
# now parse apps that are deployed from develop but don't have previews
|
# now parse apps that are deployed from develop but don't have previews
|
||||||
if 'develop' in args.github_ref:
|
if 'develop' in args.github_ref:
|
||||||
for app in ['static', 'ui-toolkit']:
|
for app in ['static', 'ui-toolkit']:
|
||||||
if app in affected:
|
if app in affected:
|
||||||
projects.append(app)
|
projects.append(app)
|
||||||
|
|
||||||
# if ref is in format release/{env}-{app} then only {app} is deployed
|
# if ref is in format release/{env}-{app} then only {app} is deployed
|
||||||
if 'release' in args.github_ref:
|
if 'release' in args.github_ref:
|
||||||
for app in main_apps:
|
for app in main_apps:
|
||||||
if f'{args.github_ref}'.endswith(app):
|
if f'{args.github_ref}'.endswith(app):
|
||||||
projects = [app]
|
projects = [app]
|
||||||
projects_e2e = [f'{app}-e2e']
|
projects_e2e = [f'{app}-e2e']
|
||||||
|
|
||||||
|
|
||||||
projects = json.dumps(projects)
|
projects = json.dumps(projects)
|
||||||
|
|
||||||
projects_e2e = json.dumps(projects_e2e)
|
projects_e2e = json.dumps(projects_e2e)
|
||||||
|
|
||||||
print(f'Projects: {projects}')
|
print(f'Projects: {projects}')
|
||||||
@@ -91,20 +100,20 @@ print(f'Projects E2E: {projects_e2e}')
|
|||||||
|
|
||||||
print('>> Previews')
|
print('>> Previews')
|
||||||
for preview, preview_value in previews.items():
|
for preview, preview_value in previews.items():
|
||||||
print(f'{preview}: {preview_value}')
|
print(f'{preview}: {preview_value}')
|
||||||
print('>> EOF Previews')
|
print('>> EOF Previews')
|
||||||
|
|
||||||
|
|
||||||
lines_to_write = [
|
lines_to_write = [
|
||||||
f'PREVIEW_GOVERNANCE={previews["governance"]}',
|
f'PREVIEW_GOVERNANCE={previews["governance"]}',
|
||||||
f'PREVIEW_EXPLORER={previews["explorer"]}',
|
f'PREVIEW_EXPLORER={previews["explorer"]}',
|
||||||
f'PREVIEW_TRADING={previews["trading"]}',
|
f'PREVIEW_TRADING={previews["trading"]}',
|
||||||
f'PREVIEW_TOOLS={previews["tools"]}',
|
f'PREVIEW_TOOLS={previews["tools"]}',
|
||||||
f'PROJECTS={projects}',
|
f'PROJECTS={projects}',
|
||||||
f'PROJECTS_E2E={projects_e2e}',
|
f'PROJECTS_E2E={projects_e2e}',
|
||||||
]
|
]
|
||||||
env_file = environ['GITHUB_ENV']
|
env_file = environ['GITHUB_ENV']
|
||||||
print(f'Line to add to GITHUB_ENV file: {env_file}')
|
print(f'Line to add to GITHUB_ENV file: {env_file}')
|
||||||
print(lines_to_write)
|
print(lines_to_write)
|
||||||
with open(env_file, 'a') as _f:
|
with open(env_file, 'a') as _f:
|
||||||
_f.write('\n'.join(lines_to_write))
|
_f.write('\n'.join(lines_to_write))
|
||||||
|
|||||||
Reference in New Issue
Block a user