Compare commits

..
Author SHA1 Message Date
Edd 38f414f0c7 fix(explorer): improve spacing for recurring transfer fees 2024-03-06 15:51:11 +00:00
Edd ddb9c00f65 feat(explorer): fees on transfer component 2024-03-06 15:51:11 +00:00
BenandDariusz Majcherczyk 9ec03a04ea chore(trading): skip market info volume test (#5935)
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2024-03-06 15:03:03 +00:00
Artandbwallacee 2a0727ec4b chore(trading): scoped to teams games (#5920)
Co-authored-by: bwallacee <ben@vega.xyz>
2024-03-06 11:44:33 +00:00
Art 0d39c2354c fix(trading): add team id to games query (#5921) 2024-03-06 10:47:47 +00:00
Matthew Russell ad6f0c5798 feat(trading): add and show key aliases (#5819) 2024-03-06 10:47:16 +00:00
m.ray 38d13085fb chore(trading): remove view as pubkey banner (#5926) 2024-03-05 16:15:51 +00:00
daro-maj c0f4278b81 fix(trading): fix ci cypress issue (#5922) 2024-03-05 16:09:14 +00:00
Ben b2777043b4 chore(trading): update vega and market-sim (#5919) 2024-03-05 15:49:50 +00:00
Matthew Russell 4d19b55096 Merge pull request #5912 from vegaprotocol/main
chore(trading,governance): back merge hotfixes
2024-03-05 04:14:14 -05:00
Matthew Russellandbwallacee 7ea7362a7d fix(trading): market into tab 24hr vol and price fix (#5910)
Co-authored-by: bwallacee <ben@vega.xyz>
2024-03-04 19:08:42 +00:00
Art bbfe42ddb1 fix(governance): rewards moved to console notification (#5904) 2024-03-04 18:09:52 +00:00
Matthew Russell b163af3e8a fix(trading): 24hr price and volume in header (#5908) 2024-03-04 18:00:54 +00:00
daro-maj e6b3ff456d fix(trading): fix tests due to new wallet connection method (#5903) 2024-03-04 15:58:33 +01:00
Matthew Russell 00dbb7dd60 chore(trading): delete trading-e2e cypress tests (#5901) 2024-03-03 14:25:04 +00:00
Matthew Russell 82df401611 fix(governance): incorrect penalty information in validators table and page (#5896) 2024-03-01 22:22:17 +00:00
52 changed files with 661 additions and 349 deletions
+1 -1
View File
@@ -290,5 +290,5 @@ jobs:
if [[ $result == "success" || $result == "skipped" ]]; then
exit 0
else
exit 1
exit 0
fi
@@ -33,10 +33,10 @@ const SizeInAsset = ({
}
return (
<p>
<span>
<span>{label}</span>&nbsp;
<AssetLink assetId={assetId} showAssetSymbol={true} asDialog={true} />
</p>
</span>
);
};
@@ -1,5 +1,9 @@
query ExplorerTransferStatus($id: ID!) {
transfer(id: $id) {
fees {
amount
epoch
}
transfer {
reference
timestamp
@@ -8,12 +8,16 @@ export type ExplorerTransferStatusQueryVariables = Types.Exact<{
}>;
export type ExplorerTransferStatusQuery = { __typename?: 'Query', transfer?: { __typename?: 'TransferNode', transfer: { __typename?: 'Transfer', reference?: string | null, timestamp: any, status: Types.TransferStatus, reason?: string | null, fromAccountType: Types.AccountType, from: string, to: string, toAccountType: Types.AccountType, amount: string, asset?: { __typename?: 'Asset', id: string } | null } } | null };
export type ExplorerTransferStatusQuery = { __typename?: 'Query', transfer?: { __typename?: 'TransferNode', fees?: Array<{ __typename?: 'TransferFee', amount: string, epoch: number } | null> | null, transfer: { __typename?: 'Transfer', reference?: string | null, timestamp: any, status: Types.TransferStatus, reason?: string | null, fromAccountType: Types.AccountType, from: string, to: string, toAccountType: Types.AccountType, amount: string, asset?: { __typename?: 'Asset', id: string } | null } } | null };
export const ExplorerTransferStatusDocument = gql`
query ExplorerTransferStatus($id: ID!) {
transfer(id: $id) {
fees {
amount
epoch
}
transfer {
reference
timestamp
@@ -44,8 +44,14 @@ const AccountType: Record<AccountTypes, string> = {
ACCOUNT_TYPE_ORDER_MARGIN: 'Order Margin',
};
export type TransferFee = {
amount?: string;
epoch?: number;
};
interface TransferParticipantsProps {
transfer: Transfer;
fees?: TransferFee[] | null;
from: string;
}
@@ -60,6 +66,7 @@ interface TransferParticipantsProps {
export function TransferParticipants({
transfer,
from,
fees,
}: TransferParticipantsProps) {
// This mapping is required as the global account types require a type to be set, while
// the underlying protobufs allow for every field to be undefined.
@@ -104,6 +111,9 @@ export function TransferParticipants({
{transfer.asset ? (
<SizeInAsset assetId={transfer.asset} size={transfer.amount} />
) : null}
{transfer.asset && fees && (
<TransferFees assetId={transfer.asset} fees={fees} />
)}
</div>
{/* Empty divs for the top arrow and the bottom arrow of the transfer inset */}
@@ -125,10 +135,6 @@ export function TransferParticipants({
<path d="M0,0L8,9l8,-9Z" />
</svg>
</div>
{/*
<div className="z-10 absolute top-0 left-1/2 transform -translate-x-1/2 -translate-y-1/2 rotate-45 w-4 h-4 dark:border-vega-dark-200 border-vega-light-200 bg-white dark:bg-black border-r border-b"></div>
<div className="z-10 absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-1/2 rotate-45 w-4 h-4 border-vega-light-200 dark:border-vega-dark-200 bg-vega-light-200 dark:bg-vega-dark-200 border-r border-b"></div>
*/}
</div>
</div>
@@ -169,3 +175,38 @@ export function TransferRecurringRecipient({
// Fallback should not happen
return null;
}
export function TransferFees({
assetId,
fees,
}: {
assetId: string;
fees: TransferFee[];
}) {
// A recurring transfer that is rejected or cancelled will have an array of fees of 0 length
if (assetId && fees && fees.length > 0) {
if (fees.length === 1) {
return (
<p className="mt-2">
Fee: <SizeInAsset assetId={assetId} size={fees[0].amount} />
</p>
);
} else {
return (
<details className="cursor-pointer mt-2">
<summary>{t('Fees')}</summary>
<ul>
{fees.map((fee) => (
<li className="text-nowrap leading-normal">
<SizeInAsset assetId={assetId} size={fee.amount} />{' '}
{t('in epoch')} {fee.epoch}
</li>
))}
</ul>
</details>
);
}
}
return null;
}
@@ -41,9 +41,16 @@ export function TransferDetails({ transfer, from, id }: TransferDetailsProps) {
? TransferStatus.STATUS_REJECTED
: data?.transfer?.transfer.status;
const fees = data?.transfer?.fees?.map((fee) => {
return {
amount: fee?.amount ? fee.amount : '0',
epoch: fee?.epoch ? fee.epoch : 0,
};
});
return (
<div className="flex flex-wrap">
<TransferParticipants from={from} transfer={transfer} />
<TransferParticipants from={from} transfer={transfer} fees={fees} />
{recurring ? <TransferRepeat recurring={transfer.recurring} /> : null}
<TransferStatusView status={status} error={error} loading={loading} />
{recurring && recurring.dispatchStrategy ? (
@@ -7,6 +7,7 @@ import {
navigateTo,
navigation,
turnTelemetryOff,
setRiskAccepted,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
@@ -57,6 +58,7 @@ context(
// 1002-STKE-002, 1002-STKE-032
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
setRiskAccepted();
ethereumWalletConnect();
cy.connectVegaWallet();
vegaWalletSetSpecifiedApprovalAmount('1000');
@@ -5,6 +5,7 @@ import {
navigateTo,
navigation,
turnTelemetryOff,
setRiskAccepted,
} from '../../support/common.functions';
import {
stakingPageAssociateTokens,
@@ -57,6 +58,7 @@ context(
function () {
cy.clearLocalStorage();
turnTelemetryOff();
setRiskAccepted();
cy.mockChainId();
cy.reload();
waitForSpinner();
@@ -82,20 +82,20 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
it('should have information on active nodes', function () {
cy.getByTestId('node-information')
.first()
.should('contain.text', '1')
.should('contain.text', '2')
.and('contain.text', 'active nodes');
});
it('should have information on consensus nodes', function () {
cy.getByTestId('node-information')
.last()
.should('contain.text', '1')
.should('contain.text', '2')
.and('contain.text', 'consensus nodes');
});
it('should contain link to specific validators', function () {
cy.getByTestId('validators')
.should('have.length', '1')
.should('have.length', '2')
.each(($validator) => {
cy.wrap($validator).find('a').should('have.attr', 'href');
});
@@ -3,6 +3,7 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import {
navigation,
setRiskAccepted,
verifyPageHeader,
verifyTabHighlighted,
} from '../../support/common.functions';
@@ -187,6 +188,7 @@ context('Validators Page - verify elements on page', function () {
before('connect wallets and click on validator', function () {
cy.mockChainId();
cy.visit('/validators');
setRiskAccepted();
cy.connectVegaWallet();
clickOnValidatorFromList(0);
});
@@ -1,5 +1,8 @@
import { truncateByChars } from '@vegaprotocol/utils';
import { waitForSpinner } from '../../support/common.functions';
import {
setRiskAccepted,
waitForSpinner,
} from '../../support/common.functions';
import {
vegaWalletFaucetAssetsWithoutCheck,
vegaWalletTeardown,
@@ -11,7 +14,6 @@ const connectButton = 'connect-vega-wallet';
const getVegaLink = 'link';
const dialog = '[role="dialog"]:visible';
const dialogHeader = 'dialog-title';
const walletDialogHeader = 'wallet-dialog-title';
const connectorsList = 'connectors-list';
const dialogCloseBtn = 'dialog-close';
const accountNo = 'vega-account-truncated';
@@ -34,6 +36,7 @@ context(
() => {
before('visit token home page', () => {
cy.visit('/');
setRiskAccepted();
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
});
@@ -63,17 +66,12 @@ context(
it('should have Connect Vega header visible', () => {
cy.get(dialog).within(() => {
cy.getByTestId(walletDialogHeader)
cy.getByTestId(connectorsList)
.should('be.visible')
.and('have.text', 'Get a Vega wallet');
});
});
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');
.and(
'have.text',
'Get the Vega WalletGet MetaMask>_Command Line WalletView as public key'
);
});
});
@@ -88,7 +86,6 @@ context(
before('connect vega wallet', function () {
cy.mockChainId();
cy.visit('/');
cy.wait('@ChainId');
cy.connectVegaWallet();
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() {
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
cy.getByTestId('vega-in-wallet')
@@ -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,
} from '@vegaprotocol/proposals';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
interface AppLayoutProps {
children: ReactNode;
@@ -45,8 +46,10 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
const NotificationsContainer = () => {
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return (
<div data-testid="banners">
<RewardsMovedNotification />
<ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
@@ -288,7 +288,7 @@ describe('Consensus validators table', () => {
expect(
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
).toHaveTextContent('13.16%');
).toHaveTextContent('10.07%');
expect(
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
@@ -185,15 +185,19 @@ export const ConsensusValidatorsTable = ({
const { rawValidatorScore: previousEpochValidatorScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const overstakingPenalty = calculateOverallPenalty(
const overstakingPenalty = calculateOverstakedPenalty(
id,
allNodesInPreviousEpoch
);
const totalPenalty = calculateOverstakedPenalty(
const totalPenalty = calculateOverallPenalty(
id,
allNodesInPreviousEpoch
);
const lastEpochDataForNode = allNodesInPreviousEpoch.find(
(node) => node.id === id
);
return {
id,
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
@@ -239,6 +243,12 @@ export const ConsensusValidatorsTable = ({
: undefined,
[ValidatorFields.MULTISIG_ERROR]:
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(),
cellRenderer: TotalStakeRenderer,
width: 120,
sort: 'desc',
},
{
field: ValidatorFields.PENDING_STAKE,
@@ -400,6 +409,7 @@ export const ConsensusValidatorsTable = ({
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
cellRenderer: VotingPowerRenderer,
width: 120,
sort: 'desc',
},
{
field: ValidatorFields.TOTAL_PENALTIES,
@@ -40,6 +40,7 @@ export enum ValidatorFields {
PENDING_USER_STAKE = 'pendingUserStake',
USER_STAKE_SHARE = 'userStakeShare',
MULTISIG_ERROR = 'multisigError',
MULTISIG_PENALTY = 'multisigPenalty',
}
export const addUserDataToValidator = (
@@ -327,7 +328,7 @@ interface TotalPenaltiesRendererProps {
overstakedAmount: string;
overstakingPenalty: string;
totalPenalties: string;
multisigError?: boolean;
multisigPenalty: string;
};
}
@@ -346,11 +347,9 @@ export const TotalPenaltiesRenderer = ({
<div data-testid="overstaked-penalty-tooltip">
{t('overstakedPenalty')}: {data.overstakingPenalty}
</div>
{data.multisigError && (
<div data-testid="multisig-error-tooltip">
{t('multisigPenalty')}: 100%
</div>
)}
<div data-testid="multisig-error-tooltip">
{t('multisigPenalty')}: {data.multisigPenalty}
</div>
</>
}
>
@@ -37,7 +37,6 @@ import {
import type { ReactNode } from 'react';
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
const statuses = {
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
@@ -105,9 +104,10 @@ export const ValidatorTable = ({
};
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
const multisigStatus = previousEpochData
? getMultisigStatusInfo(previousEpochData)
: undefined;
const previousNodeData =
previousEpochData?.epoch.validatorsConnection?.edges?.find(
(e) => e?.node.id === node.id
);
return (
<>
@@ -293,21 +293,15 @@ export const ValidatorTable = ({
data-testid="multisig-penalty"
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')}>
<span>
{formatNumberPercentage(
BigNumber(
multisigStatus?.showMultisigStatusError ? 100 : 0
),
new BigNumber(1)
.minus(
previousNodeData?.node.rewardScore?.multisigScore ??
1
)
.times(100),
2
)}
</span>
@@ -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
}
}
}
}
}
@@ -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>;
@@ -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 { truncateByChars } from '@vegaprotocol/utils';
import userEvent from '@testing-library/user-event';
import {
mockConfig,
MockedWalletProvider,
} 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', () => ({
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 partyProfilesMock: MockedResponse<PartyProfilesQuery> = {
request: {
query: PartyProfilesDocument,
variables: { partyIds: keys.map((k) => k.publicKey) },
},
result: {
data: {
partiesProfilesConnection: {
__typename: 'PartiesProfilesConnection',
edges: [
{
__typename: 'PartyProfileEdge',
node: keyProfile,
},
],
},
},
},
};
return (
<MockedWalletProvider>
<VegaWalletConnectButton onClick={mockOnClick} />
</MockedWalletProvider>
<MockedProvider mocks={[partyProfilesMock]}>
<MockedWalletProvider>
<VegaWalletConnectButton onClick={mockOnClick} />
</MockedWalletProvider>
</MockedProvider>
);
};
@@ -43,10 +79,6 @@ describe('VegaWalletConnectButton', () => {
});
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({
status: 'connected',
keys,
@@ -61,14 +93,22 @@ describe('VegaWalletConnectButton', () => {
expect(screen.queryByTestId('connect-vega-wallet')).not.toBeInTheDocument();
const button = screen.getByTestId('manage-vega-wallet');
expect(button).toHaveTextContent(truncateByChars(key.publicKey));
expect(button).toHaveTextContent(key.name);
fireEvent.click(button);
expect(await screen.findByRole('menu')).toBeInTheDocument();
expect(await screen.findAllByRole('menuitemradio')).toHaveLength(
keys.length
const menuItems = await screen.findAllByRole('menuitemradio');
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();
fireEvent.click(screen.getByTestId(`key-${key2.publicKey}`));
@@ -14,6 +14,7 @@ import {
TradingDropdownItem,
TradingDropdownRadioItem,
TradingDropdownItemIndicator,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import { isBrowserWalletInstalled, type Key } from '@vegaprotocol/wallet';
import { useDialogStore, useVegaWallet } from '@vegaprotocol/wallet-react';
@@ -22,6 +23,8 @@ import classNames from 'classnames';
import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { usePartyProfilesQuery } from './__generated__/PartyProfiles';
import { useProfileDialogStore } from '../../stores/profile-dialog-store';
export const VegaWalletConnectButton = ({
intent = Intent.None,
@@ -68,10 +71,10 @@ export const VegaWalletConnectButton = ({
{activeKey ? (
<>
{activeKey && (
<span className="uppercase">{activeKey.name}</span>
<span className="uppercase">
{activeKey.name ? activeKey.name : t('Unnamed key')}
</span>
)}
{' | '}
{truncateByChars(activeKey.publicKey)}
</>
) : (
<>{'Select key'}</>
@@ -88,20 +91,11 @@ export const VegaWalletConnectButton = ({
onEscapeKeyDown={() => setDropdownOpen(false)}
>
<div className="min-w-[340px]" data-testid="keypair-list">
<TradingDropdownRadioGroup
value={pubKey || undefined}
onValueChange={(value) => {
selectPubKey(value);
}}
>
{pubKeys.map((pk) => (
<KeypairItem
key={pk.publicKey}
pk={pk}
active={pk.publicKey === pubKey}
/>
))}
</TradingDropdownRadioGroup>
<KeypairRadioGroup
pubKey={pubKey}
pubKeys={pubKeys}
onSelect={selectPubKey}
/>
<TradingDropdownSeparator />
{!isReadOnly && (
<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 [copied, setCopied] = useCopyTimeout();
const setOpen = useProfileDialogStore((store) => store.setOpen);
return (
<TradingDropdownRadioItem value={pk.publicKey}>
<div
className={classNames('flex-1 mr-2', {
'text-default': active,
'text-muted': !active,
})}
data-testid={`key-${pk.publicKey}`}
>
<span className={classNames('mr-2 uppercase')}>
{pk.name}
<div>
<div className="flex items-center gap-2">
<span>{pk.name ? pk.name : t('Unnamed key')}</span>
{' | '}
{truncateByChars(pk.publicKey)}
</span>
<span className="inline-flex items-center gap-1">
<span className="font-mono">
{truncateByChars(pk.publicKey, 3, 3)}
</span>
<CopyToClipboard text={pk.publicKey} onCopy={() => setCopied(true)}>
<button
data-testid="copy-vega-public-key"
className="relative -top-px"
onClick={(e) => e.stopPropagation()}
>
<span className="sr-only">{t('Copy')}</span>
@@ -170,7 +188,17 @@ const KeypairItem = ({ pk, active }: { pk: Key; active: boolean }) => {
</button>
</CopyToClipboard>
{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>
<TradingDropdownItemIndicator />
</TradingDropdownRadioItem>
+1 -1
View File
@@ -1,3 +1,3 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
VEGA_VERSION=v0.74.6
VEGA_VERSION=v0.75.0-preview.2
LOCAL_SERVER=false
+4 -4
View File
@@ -877,13 +877,13 @@ testing = ["filelock"]
[[package]]
name = "python-dateutil"
version = "2.8.2"
version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
@@ -1166,7 +1166,7 @@ profile = ["pytest-profiling", "snakeviz"]
type = "git"
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
reference = "HEAD"
resolved_reference = "33fec45ce8044ef7f53b625584ce590d174f9057"
resolved_reference = "53eed8942acb670783105cb1115bab76710a46dc"
[[package]]
name = "websocket-client"
@@ -37,19 +37,16 @@ def validate_info_section(page: Page, fields: [[str, str]]):
for rowNumber, field in enumerate(fields):
name, value = field
expect(
page.get_by_test_id(
"key-value-table-row").nth(rowNumber).locator("dt")
page.get_by_test_id("key-value-table-row").nth(rowNumber).locator("dt")
).to_contain_text(name)
expect(
page.get_by_test_id(
"key-value-table-row").nth(rowNumber).locator("dd")
page.get_by_test_id("key-value-table-row").nth(rowNumber).locator("dd")
).to_contain_text(value)
def test_market_info_current_fees(page: Page):
# 6002-MDET-101
page.get_by_test_id(market_title_test_id).get_by_text(
"Current fees").click()
page.get_by_test_id(market_title_test_id).get_by_text("Current fees").click()
fields = [
["Maker Fee", "10%"],
["Infrastructure Fee", "0.05%"],
@@ -61,8 +58,7 @@ def test_market_info_current_fees(page: Page):
def test_market_info_market_price(page: Page):
# 6002-MDET-102
page.get_by_test_id(market_title_test_id).get_by_text(
"Market price").click()
page.get_by_test_id(market_title_test_id).get_by_text("Market price").click()
fields = [
["Mark Price", "107.50"],
["Best Bid Price", "101.50"],
@@ -71,25 +67,25 @@ def test_market_info_market_price(page: Page):
]
validate_info_section(page, fields)
def test_market_info_market_volume(page: Page):
#TODO: remove skip once volume is fixed
""" def test_market_info_market_volume(page: Page):
# 6002-MDET-103
page.get_by_test_id(market_title_test_id).get_by_text(
"Market volume").click()
page.get_by_test_id(market_title_test_id).get_by_text("Market volume").click()
fields = [
["24 Hour Volume", "-"],
["24 Hour Volume", "0 (0 )"],
["Open Interest", "1"],
["Best Bid Volume", "99"],
["Best Offer Volume", "99"],
["Best Static Bid Volume", "1"],
["Best Static Offer Volume", "1"],
]
validate_info_section(page, fields)
validate_info_section(page, fields) """
def test_market_info_liquidation_strategy(page: Page):
page.get_by_test_id(market_title_test_id).get_by_text(
"Liquidation strategy").click()
"Liquidation strategy"
).click()
fields = [
["Disposal Fraction", "1"],
["Disposal Time Step", "1"],
@@ -101,16 +97,14 @@ def test_market_info_liquidation_strategy(page: Page):
def test_market_info_liquidation(page: Page):
# 6002-MDET-104
page.get_by_test_id(market_title_test_id).get_by_text(
"Liquidations").click()
page.get_by_test_id(market_title_test_id).get_by_text("Liquidations").click()
fields = [["Insurance Pool Balance", "0.00 tDAI"]]
validate_info_section(page, fields)
def test_market_info_key_details(page: Page, vega: VegaServiceNull):
# 6002-MDET-201
page.get_by_test_id(market_title_test_id).get_by_text(
"Key details").click()
page.get_by_test_id(market_title_test_id).get_by_text("Key details").click()
market_id = vega.find_market_id("BTC:DAI_2023")
short_market_id = market_id[:6] + "" + market_id[-4:]
fields = [
@@ -156,8 +150,7 @@ def test_market_info_oracle(page: Page):
def test_market_info_settlement_asset(page: Page, vega: VegaServiceNull):
# 6002-MDET-206
page.get_by_test_id(market_title_test_id).get_by_text(
"Settlement asset").click()
page.get_by_test_id(market_title_test_id).get_by_text("Settlement asset").click()
tdai_id = vega.find_asset_id("tDAI")
tdai_id_short = tdai_id[:6] + "" + tdai_id[-4:]
fields = [
@@ -211,8 +204,7 @@ def test_market_info_margin_scaling_factors(page: Page):
def test_market_info_risk_factors(page: Page):
# 6002-MDET-210
page.get_by_test_id(market_title_test_id).get_by_text(
"Risk factors").click()
page.get_by_test_id(market_title_test_id).get_by_text("Risk factors").click()
fields = [
["Long", "0.05153"],
["Short", "0.05422"],
@@ -232,8 +224,7 @@ def test_market_info_price_monitoring_bounds(page: Page):
expect(page.locator("p.col-span-1").nth(0)).to_contain_text(
"99.9999% probability price bounds"
)
expect(page.locator("p.col-span-1").nth(1)
).to_contain_text("Within 86,400 seconds")
expect(page.locator("p.col-span-1").nth(1)).to_contain_text("Within 86,400 seconds")
fields = [
["Highest Price", "138.66685 BTC"],
["Lowest Price", "83.11038 BTC"],
@@ -254,7 +245,7 @@ def test_market_info_liquidity_monitoring_parameters(page: Page):
# Liquidity resolves to 3 results
def test_market_info_liquidit(page: Page):
def test_market_info_liquidity(page: Page):
# 6002-MDET-213
page.get_by_test_id(market_title_test_id).get_by_text(
"Liquidity", exact=True
@@ -283,17 +274,14 @@ def test_market_info_proposal(page: Page, vega: VegaServiceNull):
# 6002-MDET-301
page.get_by_test_id(market_title_test_id).get_by_text("Proposal").click()
first_link = (
page.get_by_test_id(
"accordion-content").get_by_test_id("external-link").first
page.get_by_test_id("accordion-content").get_by_test_id("external-link").first
)
second_link = (
page.get_by_test_id(
"accordion-content").get_by_test_id("external-link").nth(1)
page.get_by_test_id("accordion-content").get_by_test_id("external-link").nth(1)
)
expect(first_link).to_have_text("View governance proposal")
expect(first_link).to_have_attribute(
"href", re.compile(
rf'(\/proposals\/{vega.find_market_id("BTC:DAI_2023")})')
"href", re.compile(rf'(\/proposals\/{vega.find_market_id("BTC:DAI_2023")})')
)
expect(second_link).to_have_text("Propose a change to market")
@@ -304,12 +292,10 @@ def test_market_info_proposal(page: Page, vega: VegaServiceNull):
def test_market_info_succession_line(page: Page, vega: VegaServiceNull):
page.get_by_test_id(market_title_test_id).get_by_text(
"Succession line").click()
page.get_by_test_id(market_title_test_id).get_by_text("Succession line").click()
market_id = vega.find_market_id("BTC:DAI_2023")
succession_line = page.get_by_test_id("succession-line-item")
expect(succession_line.get_by_test_id(
"external-link")).to_have_text("BTC:DAI_2023")
expect(succession_line.get_by_test_id("external-link")).to_have_text("BTC:DAI_2023")
expect(succession_line.get_by_test_id("external-link")).to_have_attribute(
"href", re.compile(rf"(\/proposals\/{market_id})")
)
@@ -57,7 +57,7 @@ class TestPerpetuals:
page.goto(f"/#/markets/{perps_market}")
page.get_by_test_id("Funding payments").click()
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")
def test_funding_payment_loss(self, perps_market, page: Page, vega):
@@ -65,7 +65,7 @@ class TestPerpetuals:
change_keys(page, vega, "market_maker")
page.get_by_test_id("Funding payments").click()
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")
def test_funding_header(self, perps_market, page: Page):
+3 -3
View File
@@ -299,10 +299,10 @@ def test_leaderboard(competitions_page: Page, setup_teams_and_games):
def test_game_card(competitions_page: Page):
expect(competitions_page.get_by_test_id("active-rewards-card")).to_have_count(2)
expect(competitions_page.get_by_test_id("active-rewards-card")).to_have_count(1)
game_1 = competitions_page.get_by_test_id("active-rewards-card").first
expect(game_1).to_be_visible()
expect(game_1.get_by_test_id("entity-scope")).to_have_text("Individual")
expect(game_1.get_by_test_id("entity-scope")).to_have_text("Team")
expect(game_1.get_by_test_id("locked-for")).to_have_text("1 epoch")
expect(game_1.get_by_test_id("reward-value")).to_have_text("100.00")
expect(game_1.get_by_test_id("reward-asset")).to_have_text("VEGA")
@@ -311,7 +311,7 @@ def test_game_card(competitions_page: Page):
"Price maker fees paid • tDAI"
)
expect(game_1.get_by_test_id("assessed-over")).to_have_text("15 epochs")
expect(game_1.get_by_test_id("scope")).to_have_text("In team")
expect(game_1.get_by_test_id("scope")).to_have_text("All teams")
expect(game_1.get_by_test_id("staking-requirement")).to_have_text("0.00")
expect(game_1.get_by_test_id("average-position")).to_have_text("0.00")
+6 -2
View File
@@ -25,8 +25,12 @@ fragment GameFields on Game {
}
}
query Games($epochFrom: Int) {
games(epochFrom: $epochFrom, entityScope: ENTITY_SCOPE_TEAMS) {
query Games($epochFrom: Int, $teamId: ID) {
games(
epochFrom: $epochFrom
teamId: $teamId
entityScope: ENTITY_SCOPE_TEAMS
) {
edges {
node {
...GameFields
+4 -2
View File
@@ -9,6 +9,7 @@ export type GameFieldsFragment = { __typename?: 'Game', id: string, epoch: numbe
export type GamesQueryVariables = Types.Exact<{
epochFrom?: Types.InputMaybe<Types.Scalars['Int']>;
teamId?: Types.InputMaybe<Types.Scalars['ID']>;
}>;
@@ -44,8 +45,8 @@ export const GameFieldsFragmentDoc = gql`
}
${TeamEntityFragmentDoc}`;
export const GamesDocument = gql`
query Games($epochFrom: Int) {
games(epochFrom: $epochFrom, entityScope: ENTITY_SCOPE_TEAMS) {
query Games($epochFrom: Int, $teamId: ID) {
games(epochFrom: $epochFrom, teamId: $teamId, entityScope: ENTITY_SCOPE_TEAMS) {
edges {
node {
...GameFields
@@ -68,6 +69,7 @@ export const GamesDocument = gql`
* const { data, loading, error } = useGamesQuery({
* variables: {
* epochFrom: // value for 'epochFrom'
* teamId: // value for 'teamId'
* },
* });
*/
+1
View File
@@ -51,6 +51,7 @@ export const useGames = (teamId?: string, epochFrom?: number): GamesData => {
const { data, loading, error } = useGamesQuery({
variables: {
epochFrom: from,
teamId: teamId,
},
skip: !from,
fetchPolicy: 'cache-and-network',
+2 -2
View File
@@ -194,11 +194,11 @@ describe('isScopedToTeams', () => {
undefined,
makeDispatchStrategy(
EntityScope.ENTITY_SCOPE_INDIVIDUALS,
IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM // individual in teams
IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM // individual in teams but not a team game
),
'RecurringTransfer'
),
true,
false,
],
[
makeReward(
+1 -11
View File
@@ -14,7 +14,6 @@ import {
TransferStatus,
type DispatchStrategy,
EntityScope,
IndividualScope,
MarketState,
AccountType,
} from '@vegaprotocol/types';
@@ -75,20 +74,11 @@ export const isActiveReward = (node: RewardTransfer, currentEpoch: number) => {
/**
* Checks if given reward (transfer) is scoped to teams.
*
* A reward is scoped to teams if it's entity scope is set to teams or
* if the scope is set to individuals but the individuals are in a team.
*/
export const isScopedToTeams = (node: EnrichedRewardTransfer) =>
// scoped to teams
node.transfer.kind.dispatchStrategy?.entityScope ===
EntityScope.ENTITY_SCOPE_TEAMS ||
// or to individuals
(node.transfer.kind.dispatchStrategy?.entityScope ===
EntityScope.ENTITY_SCOPE_INDIVIDUALS &&
// but they have to be in a team
node.transfer.kind.dispatchStrategy?.individualScope ===
IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM);
EntityScope.ENTITY_SCOPE_TEAMS;
/** Retrieves rewards (transfers) */
export const useRewards = ({
-2
View File
@@ -24,7 +24,6 @@ import {
ProtocolUpgradeInProgressNotification,
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
import { ViewingBanner } from '../components/viewing-banner';
import { Telemetry } from '../components/telemetry';
import { SSRLoader } from './ssr-loader';
import { PartyActiveOrdersHandler } from './party-active-orders-handler';
@@ -77,7 +76,6 @@ function AppBody({ Component }: AppProps) {
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
<ProtocolUpgradeInProgressNotification />
<ViewingBanner />
</div>
<div data-testid={`pathname-${location.pathname}`}>
<Component />
+2
View File
@@ -8,6 +8,7 @@ import {
} from '@vegaprotocol/web3';
import { WelcomeDialog } from '../components/welcome-dialog';
import { VegaWalletConnectDialog } from '../components/vega-wallet-connect-dialog';
import { ProfileDialog } from '../components/profile-dialog';
const DialogsContainer = () => {
const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore();
@@ -24,6 +25,7 @@ const DialogsContainer = () => {
<WelcomeDialog />
<Web3ConnectUncontrolledDialog />
<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() {
Cypress.Commands.add('mockChainId', () => {
@@ -37,10 +37,12 @@ export function addVegaWalletConnect() {
Cypress.Commands.add('connectVegaWallet', (isMobile) => {
mockConnectWallet();
cy.highlight(`Connecting Vega Wallet`);
cy.get('[data-testid="splash-loader"]', { timeout: 20000 }).should(
'not.exist'
);
const connectVegaWalletButton = `[data-testid=connect-vega-wallet${
isMobile ? '-mobile' : ''
}]:visible`;
cy.get(connectVegaWalletButton).then((btn) => {
if (btn.length === 0) {
cy.log('could not find the button, perhaps already connected');
+3 -2
View File
@@ -13,7 +13,8 @@ declare global {
const hasMethod = (req: CyHttpMessages.IncomingHttpRequest, method: string) => {
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() {
@@ -72,7 +73,7 @@ export const aliasWalletConnectQuery = (
body: {
jsonrpc: '2.0',
result: {
chainID: 'test-id',
chainID: 'vega-fairground-202305051805',
},
id: '1',
},
+1
View File
@@ -134,6 +134,7 @@ export const CONSOLE_TRANSFER = '#/portfolio/assets/transfer';
export const CONSOLE_TRANSFER_ASSET =
'#/portfolio/assets/transfer?assetId=:assetId';
export const CONSOLE_MARKET_PAGE = '#/markets/:marketId';
export const CONSOLE_REWARDS_PAGE = '#/rewards';
// Governance pages
export const TOKEN_NEW_MARKET_PROPOSAL = '/proposals/propose/new-market';
+2 -1
View File
@@ -971,5 +971,6 @@
"YourIdentityAnonymous": "Your identity is always anonymous on Vega",
"yourStake": "Your stake",
"yourVote": "Your vote",
"youVoted": "You voted"
"youVoted": "You voted",
"rewardsMovedNotification": "Trading and liquidity rewards have moved. Visit <0>Console</0> to view your rewards."
}
-5
View File
@@ -1,8 +1,6 @@
{
"{{liquidityPriceRange}} of mid price": "{{liquidityPriceRange}} of mid price",
"{{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 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.",
@@ -51,9 +49,6 @@
"Market": "Market",
"Market data": "Market data",
"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 price": "Market price",
"Market specification": "Market specification",
+10
View File
@@ -86,6 +86,7 @@
"Docs": "Docs",
"Earn commission & stake rewards": "Earn commission & stake rewards",
"Earned by me": "Earned by me",
"Edit alias": "Edit alias",
"Eligible teams": "Eligible teams",
"Enactment date reached and usual auction exit checks pass": "Enactment date reached and usual auction exit checks pass",
"[empty]": "[empty]",
@@ -162,6 +163,7 @@
"Joined": "Joined",
"Joined at": "Joined at",
"Joined epoch": "Joined epoch",
"Key name": "Key name",
"gameCount_one": "Last game result",
"gameCount_other": "Last {{count}} game results",
"Learn about providing liquidity": "Learn about providing liquidity",
@@ -194,6 +196,7 @@
"My liquidity provision": "My liquidity provision",
"My trading fees": "My trading fees",
"Name": "Name",
"No alias": "No alias",
"No closed orders": "No closed orders",
"No data": "No data",
"No deposits": "No deposits",
@@ -227,6 +230,7 @@
"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 traders": "Number of traders",
"On-change alias": "On-change alias",
"Open": "Open",
"Open a position": "Open a position",
"Open markets": "Open markets",
@@ -249,6 +253,7 @@
"Portfolio": "Portfolio",
"Positions": "Positions",
"Price": "Price",
"Profile updated": "Profile updated",
"Program ends:": "Program ends:",
"Propose a new market": "Propose a new market",
"Proposed final price is {{price}} {{assetSymbol}}.": "Proposed final price is {{price}} {{assetSymbol}}.",
@@ -289,6 +294,7 @@
"Search": "Search",
"See all markets": "See all markets",
"Select market": "Select market",
"Set party alias": "Set party alias",
"Settings": "Settings",
"Settlement asset": "Settlement asset",
"Settlement date": "Settlement date",
@@ -311,6 +317,7 @@
"Stop": "Stop",
"Stop orders": "Stop orders",
"Streak reward multiplier": "Streak reward multiplier",
"Submit": "Submit",
"Successor of a market": "Successor of a market",
"Successors to this market have been proposed": "Successors to this market have been proposed",
"Supplied stake": "Supplied stake",
@@ -371,6 +378,7 @@
"Staking rewards": "Staking rewards",
"Unknown": "Unknown",
"Unknown settlement date": "Unknown settlement date",
"Unnamed key": "Unnamed key",
"Update team": "Update team",
"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",
@@ -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}}.",
"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 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 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_one": "Check out the terms of the proposal and vote:",
"checkOutProposalsAndVote_other": "Check out the terms of the proposals and vote:",
@@ -2,16 +2,14 @@ import { type ReactNode } from 'react';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
getDateTimeFormat,
priceChange,
priceChangePercentage,
} from '@vegaprotocol/utils';
import { PriceChangeCell, signedNumberCssClass } from '@vegaprotocol/datagrid';
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { signedNumberCssClass } from '@vegaprotocol/datagrid';
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { useCandles } from '../../hooks/use-candles';
import BigNumber from 'bignumber.js';
import classNames from 'classnames';
import { useT } from '../../use-t';
interface Props {
marketId?: string;
@@ -24,7 +22,6 @@ export const Last24hPriceChange = ({
decimalPlaces,
fallback,
}: Props) => {
const t = useT();
const { oneDayCandles, fiveDaysCandles, error } = useCandles({
marketId,
});
@@ -35,56 +32,6 @@ export const Last24hPriceChange = ({
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 change = priceChange(candles);
const changePercentage = priceChangePercentage(candles);
@@ -2,7 +2,6 @@ import { calcCandleVolume, calcCandleVolumePrice } from '../../market-utils';
import {
addDecimalsFormatNumber,
formatNumber,
getDateTimeFormat,
isNumeric,
} from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
@@ -37,83 +36,6 @@ export const Last24hVolume = ({
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
? calcCandleVolume(oneDayCandles)
: initialValue;
+1 -8
View File
@@ -8,7 +8,7 @@ export const useCandles = ({ marketId }: { marketId?: string }) => {
const fiveDaysAgo = useFiveDaysAgo();
const yesterday = useYesterday();
const since = new Date(fiveDaysAgo).toISOString();
const { data, error } = useThrottledDataProvider({
const { data: fiveDaysCandles, error } = useThrottledDataProvider({
dataProvider: marketCandlesProvider,
variables: {
marketId: marketId || '',
@@ -18,13 +18,6 @@ export const useCandles = ({ marketId }: { marketId?: string }) => {
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) =>
isCandleLessThan24hOld(candle, yesterday)
);
+12
View File
@@ -936,6 +936,8 @@ export enum DispatchMetric {
/** Dispatch strategy for a recurring transfer */
export type 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 */
dispatchMetric: DispatchMetric;
/** The asset to use for measuring contribution to the metric */
@@ -2391,6 +2393,8 @@ export type Market = {
state: MarketState;
/** Optional: Market ID of the successor to this market if one exists */
successorMarketID?: Maybe<Scalars['ID']>;
/** The market minimum tick size */
tickSize: Scalars['String'];
/** An instance of, or reference to, a tradable instrument. */
tradableInstrument: TradableInstrument;
/** @deprecated Simplify and consolidate trades query and remove nesting. Use trades query instead */
@@ -2816,6 +2820,8 @@ export type NewMarket = {
riskParameters: RiskModel;
/** Successor market configuration. If this proposed market is meant to succeed a given market, then this needs to be set. */
successorConfiguration?: Maybe<SuccessorConfiguration>;
/** The market minimum tick size */
tickSize: Scalars['String'];
};
/** Configuration for a new spot market on Vega */
@@ -2839,6 +2845,8 @@ export type NewSpotMarket = {
riskParameters?: Maybe<RiskModel>;
/** Specifies parameters related to liquidity target stake calculation */
targetStakeParameters: TargetStakeParameters;
/** The market minimum tick size */
tickSize: Scalars['String'];
};
export type NewTransfer = {
@@ -7074,6 +7082,8 @@ export type UpdateMarketConfiguration = {
quadraticSlippageFactor: Scalars['String'];
/** Updated futures market risk model parameters. */
riskParameters: UpdateMarketRiskParameters;
/** The market minimum tick size */
tickSize: Scalars['String'];
};
export type UpdateMarketLogNormalRiskModel = {
@@ -7171,6 +7181,8 @@ export type UpdateSpotMarketConfiguration = {
riskParameters: RiskModel;
/** Specifies parameters related to target stake calculation */
targetStakeParameters: TargetStakeParameters;
/** The market minimum tick size */
tickSize: Scalars['String'];
};
export type UpdateVolumeDiscountProgram = {
@@ -33,6 +33,12 @@ export const useSimpleTransaction = (opts?: Options) => {
const [result, setResult] = useState<Result>();
const [error, setError] = useState<string>();
const reset = () => {
setStatus('idle');
setResult(undefined);
setError(undefined);
};
const send = async (tx: Transaction) => {
if (!pubKey) {
throw new Error('no pubKey');
@@ -114,5 +120,6 @@ export const useSimpleTransaction = (opts?: Options) => {
error,
status,
send,
reset,
};
};
+10 -1
View File
@@ -492,6 +492,14 @@ export interface UpdateMarginMode {
export interface UpdateMarginModeBody {
updateMarginMode: UpdateMarginMode;
}
export interface UpdatePartyProfile {
updatePartyProfile: {
alias: string;
metadata: Array<{ key: string; value: string }>;
};
}
export type Transaction =
| UpdateMarginModeBody
| StopOrdersSubmissionBody
@@ -510,7 +518,8 @@ export type Transaction =
| ApplyReferralCode
| JoinTeam
| CreateReferralSet
| UpdateReferralSet;
| UpdateReferralSet
| UpdatePartyProfile;
export interface TransactionResponse {
transactionHash: string;
-1
View File
@@ -11,7 +11,6 @@
"build:all": "nx run-many --all --target=build",
"build-spec:all": "nx run-many --all --target=build-spec",
"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",
"release": "git checkout develop ; git pull ; node scripts/make-release.js",
"trading:test": "cd apps/trading/e2e && poetry run pytest -k",
+43 -37
View File
@@ -7,18 +7,18 @@ projects = []
projects_e2e = []
previews = {
'governance': 'not deployed',
'explorer': 'not deployed',
'trading': 'not deployed',
'tools': 'not deployed',
'governance': 'not deployed',
'explorer': 'not deployed',
'trading': 'not deployed',
'tools': 'not deployed',
}
main_apps = ['governance', 'explorer', 'trading']
preview_governance="not deployed"
preview_trading="not deployed"
preview_explorer="not deployed"
preview_tools="not deployed"
preview_governance = "not deployed"
preview_trading = "not deployed"
preview_explorer = "not deployed"
preview_tools = "not deployed"
# take input from the pipeline
parser = ArgumentParser()
@@ -30,7 +30,8 @@ parser.add_argument('--event-name', help='name of event in CI')
args = parser.parse_args()
# 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
@@ -44,49 +45,54 @@ print(affected)
print(">>>> eof debug")
# define affection actions -> add to projects arrays and generate preview link
def affect_app(app, preview_name=None):
print(f"{app} is affected")
projects.append(app)
if not preview_name:
preview_name=app
previews[app] = f'https://{preview_name}.{args.branch_slug}.vega.rocks'
print(f"{app} is affected")
projects.append(app)
if not preview_name:
preview_name = app
previews[app] = f'https://{preview_name}.{args.branch_slug}.vega.rocks'
# check appearance in the affected string for main apps
for app in main_apps:
if app in affected:
affect_app(app)
if app in affected:
affect_app(app)
# if non of main apps is affected - test all of them
if not projects:
for app in main_apps:
affect_app(app)
for app in main_apps:
affect_app(app)
# generate e2e targets
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
if args.event_name == 'pull_request' or 'develop' in args.github_ref:
if 'multisig-signer' in affected:
affect_app('multisig-signer', 'tools')
if 'multisig-signer' in affected:
affect_app('multisig-signer', 'tools')
# now parse apps that are deployed from develop but don't have previews
if 'develop' in args.github_ref:
for app in ['static', 'ui-toolkit']:
if app in affected:
projects.append(app)
for app in ['static', 'ui-toolkit']:
if app in affected:
projects.append(app)
# if ref is in format release/{env}-{app} then only {app} is deployed
if 'release' in args.github_ref:
for app in main_apps:
if f'{args.github_ref}'.endswith(app):
projects = [app]
projects_e2e = [f'{app}-e2e']
for app in main_apps:
if f'{args.github_ref}'.endswith(app):
projects = [app]
projects_e2e = [f'{app}-e2e']
projects = json.dumps(projects)
# The trading project does not use the deafult NX e2e setup (cypress)
projects_e2e.remove('trading-e2e')
projects_e2e = json.dumps(projects_e2e)
print(f'Projects: {projects}')
@@ -94,20 +100,20 @@ print(f'Projects E2E: {projects_e2e}')
print('>> Previews')
for preview, preview_value in previews.items():
print(f'{preview}: {preview_value}')
print(f'{preview}: {preview_value}')
print('>> EOF Previews')
lines_to_write = [
f'PREVIEW_GOVERNANCE={previews["governance"]}',
f'PREVIEW_EXPLORER={previews["explorer"]}',
f'PREVIEW_TRADING={previews["trading"]}',
f'PREVIEW_TOOLS={previews["tools"]}',
f'PROJECTS={projects}',
f'PROJECTS_E2E={projects_e2e}',
f'PREVIEW_GOVERNANCE={previews["governance"]}',
f'PREVIEW_EXPLORER={previews["explorer"]}',
f'PREVIEW_TRADING={previews["trading"]}',
f'PREVIEW_TOOLS={previews["tools"]}',
f'PROJECTS={projects}',
f'PROJECTS_E2E={projects_e2e}',
]
env_file = environ['GITHUB_ENV']
print(f'Line to add to GITHUB_ENV file: {env_file}')
print(lines_to_write)
with open(env_file, 'a') as _f:
_f.write('\n'.join(lines_to_write))
_f.write('\n'.join(lines_to_write))