Compare commits

...
Author SHA1 Message Date
Matthew Russell b4a2e7d604 feat: add reload prompt after extension is installed 2024-03-07 13:06:58 +00:00
Matthew Russell 6c7d4808e1 fix: clear full state on disconnect 2024-03-07 12:45:58 +00:00
bwallacee 3c931e180e chore(trading): fix change keys 2024-03-07 09:33:15 +00:00
Dariusz Majcherczyk d760e4cc8e fix(trading): skip flaky tests 2024-03-06 17:16:15 +01:00
Matthew Russell 6f59083169 fix: wallet check in governance bootstrap 2024-03-06 15:20:47 +00:00
Matthew Russell f0b953fb71 fix: check for on/off methods before calling 2024-03-06 15:00:51 +00:00
Matthew Russell d46af0009a fix: tests and only show error data if present 2024-03-06 13:54:59 +00:00
Matthew Russell cda3a5050e fix: ensure json check for all snap invocations, make is snap error check less strict for ff 2024-03-06 13:18:10 +00:00
Matthew Russell 76048fea66 fix: add missing hook dep in eager connect 2024-03-06 11:02:23 +00:00
Matthew Russell 5bbb592b01 fix: pipe error through from injected wallet 2024-03-06 11:02:23 +00:00
Matthew Russell 722897fdc6 fix: handle errors thrown by snap better 2024-03-06 11:02:23 +00:00
Matthew Russell 961c6e2ee6 fix: check connector status before eager connect, fix input error text wrapping 2024-03-06 11:02:23 +00:00
Matthew Russell 9e413e66ae chore: fix unnecessary calls to listKeys 2024-03-06 11:02:23 +00:00
Matthew Russell c46d701361 fix: don't use deprecated selectedWallet property in snap connector 2024-03-06 11:02:23 +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
35 changed files with 725 additions and 265 deletions
@@ -79,14 +79,14 @@ 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')
.first()
.should('contain.text', '2')
.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')
.last()
.should('contain.text', '2')
+4 -2
View File
@@ -26,9 +26,9 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
const { token, staking, vesting } = useContracts();
const setAssociatedBalances = useRefreshAssociatedBalances();
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
const vegaConnecting = useEagerConnect();
const vegaWalletStatus = useEagerConnect();
const loaded = balancesLoaded && !vegaConnecting;
const loaded = balancesLoaded && vegaWalletStatus !== 'connecting';
React.useEffect(() => {
const run = async () => {
@@ -169,3 +169,5 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
}
return <Suspense fallback={loading}>{children}</Suspense>;
};
AppLoader.displayName = 'AppLoader';
@@ -111,3 +111,4 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
</ContractsContext.Provider>
);
};
ContractsProvider.displayName = 'ContractsProvider';
@@ -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
-2
View File
@@ -50,8 +50,6 @@ def truncate_middle(market_id, start=6, end=4):
def change_keys(page: Page, vega: VegaServiceNull, key_name):
page.get_by_test_id("manage-vega-wallet").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()
+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"
@@ -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):
+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
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 });
}
},
}));
+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:",
@@ -7,6 +7,7 @@
"Get MetaMask": "Get MetaMask",
"Get the Vega Wallet": "Get the Vega Wallet",
"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",
"Transaction was not successful": "Transaction was not successful",
"Wallet rejected transaction": "Wallet rejected transaction"
+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 = {
@@ -17,8 +17,8 @@ export const InputError = ({
...props
}: InputErrorProps) => {
const effectiveClassName = classNames(
'text-sm flex items-center first-letter:uppercase',
'mt-2',
'text-sm block items-center first-letter:capitalize',
'mt-2 min-w-0 break-words',
{
'border-danger': intent === 'danger',
'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 {
ConnectorErrors,
isBrowserWalletInstalled,
@@ -12,6 +17,7 @@ import { useConnect } from '../../hooks/use-connect';
import { Links } from '../../constants';
import { ConnectorIcon } from './connector-icon';
import { useUserAgent } from '@vegaprotocol/react-helpers';
import { Trans } from 'react-i18next';
const vegaExtensionsLinks = {
chrome: Links.chromeExtension,
@@ -29,48 +35,67 @@ export const ConnectionOptions = ({
onConnect: (id: ConnectorType) => void;
}) => {
const t = useT();
const error = useWallet((store) => store.error);
const { connectors } = useConnect();
const error = useWallet((store) => store.error);
const [isInstalling, setIsInstalling] = useState(false);
return (
<div className="flex flex-col items-start gap-4">
<h2 className="text-xl">{t('Connect to Vega')}</h2>
<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),
};
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}
{isInstalling ? (
<p className="text-warning">
<Trans
i18nKey="Once you have the added the extension, <0>refresh</0> you browser."
components={[
<button
onClick={() => window.location.reload()}
className="underline underline-offset-4"
/>,
]}
/>
</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
href={Links.walletOverview}
@@ -90,6 +115,7 @@ interface ConnectionOptionProps {
description: string;
showDescription?: boolean;
onClick: () => void;
onInstall?: () => void;
}
const CONNECTION_OPTION_CLASSES =
@@ -142,6 +168,7 @@ export const ConnectionOptionInjected = ({
description,
showDescription = false,
onClick,
onInstall,
}: ConnectionOptionProps) => {
const t = useT();
const userAgent = useUserAgent();
@@ -158,7 +185,11 @@ export const ConnectionOptionInjected = ({
</span>
</ConnectionOptionButtonWithDescription>
) : (
<ConnectionOptionLinkWithDescription id={id} href={link}>
<ConnectionOptionLinkWithDescription
id={id}
href={link}
onClick={onInstall}
>
<span className="flex flex-col justify-start text-left">
<span className="capitalize leading-5">
{t('Get the Vega Wallet')}
@@ -183,7 +214,7 @@ export const ConnectionOptionInjected = ({
{name}
</ConnectionOptionButton>
) : (
<ConnectionOptionLink id={id} href={link}>
<ConnectionOptionLink id={id} href={link} onClick={onInstall}>
{t('Get the Vega Wallet')}
</ConnectionOptionLink>
)}
@@ -275,8 +306,9 @@ const ConnectionOptionLink = forwardRef<
children: ReactNode;
id: ConnectorType;
href: string;
onClick?: () => void;
}
>(({ children, id, href }, ref) => {
>(({ children, id, href, onClick }, ref) => {
return (
<a
href={href}
@@ -285,6 +317,7 @@ const ConnectionOptionLink = forwardRef<
className={CONNECTION_OPTION_CLASSES}
data-testid={`connector-${id}`}
ref={ref}
onClick={onClick}
>
<ConnectorIcon id={id} />
{children}
@@ -320,8 +353,10 @@ const ConnectionOptionLinkWithDescription = forwardRef<
children: ReactNode;
id: ConnectorType;
href: string;
onClick?: () => void;
}
>(({ children, id, href }, ref) => {
>(({ children, id, href, onClick }, ref) => {
return (
<a
ref={ref}
@@ -329,6 +364,7 @@ const ConnectionOptionLinkWithDescription = forwardRef<
href={href}
target="_blank"
rel="noreferrer"
onClick={onClick}
>
<span>
<ConnectorIcon id={id} />
@@ -1,17 +1,20 @@
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
import { useWallet } from './use-wallet';
import { useConnect } from './use-connect';
export function useEagerConnect() {
const current = useWallet((store) => store.current);
const status = useWallet((store) => store.status);
const { connect } = useConnect();
const [connecting, setConnecting] = useState(true);
useEffect(() => {
const attemptConnect = async () => {
// No stored config, or config was malformed or no risk accepted
if (!current) {
setConnecting(false);
return;
}
if (status !== 'disconnected') {
return;
}
@@ -19,15 +22,13 @@ export function useEagerConnect() {
await connect(current);
} catch {
console.warn(`Failed to connect with connector: ${current}`);
} finally {
setConnecting(false);
}
};
if (typeof window !== 'undefined') {
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 [error, setError] = useState<string>();
const reset = () => {
setStatus('idle');
setResult(undefined);
setError(undefined);
};
const send = async (tx: Transaction) => {
if (!pubKey) {
throw new Error('no pubKey');
@@ -59,12 +65,12 @@ export const useSimpleTransaction = (opts?: Options) => {
if (err.code === ConnectorErrors.userRejected.code) {
setStatus('idle');
} else {
setError(err.message);
setError(`${err.message}${err.data ? `: ${err.data}` : ''}`);
setStatus('idle');
opts?.onError?.(err.message);
}
} else {
const msg = t('Wallet rejected transaction');
const msg = t('Something went wrong');
setError(msg);
setStatus('idle');
opts?.onError?.(msg);
@@ -114,5 +120,6 @@ export const useSimpleTransaction = (opts?: Options) => {
error,
status,
send,
reset,
};
};
@@ -7,6 +7,7 @@ import {
listKeysError,
noWalletError,
sendTransactionError,
userRejectedError,
} from '../errors';
import {
type TransactionParams,
@@ -14,6 +15,19 @@ import {
type VegaWalletEvent,
} from '../types';
interface InjectedError {
message: string;
code: number;
data:
| {
message: string;
code: number;
}
| string;
}
const USER_REJECTED_CODE = -4;
export class InjectedConnector implements Connector {
readonly id = 'injected';
readonly name = 'Vega Wallet';
@@ -85,15 +99,55 @@ export class InjectedConnector implements Connector {
sentAt: res.sentAt,
};
} 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();
}
}
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) {
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 };
const USER_REJECTED_CODE = 3001;
export class JsonRpcConnector implements Connector {
readonly id = 'jsonRpc';
readonly name = 'Command Line Wallet';
@@ -27,7 +29,7 @@ export class JsonRpcConnector implements Connector {
requestId: number = 0;
store: StoreApi<Store> | undefined;
pollRef: NodeJS.Timer | undefined;
ee: EventEmitter;
ee: InstanceType<typeof EventEmitter>;
constructor(config: JsonRpcConnectorConfig) {
this.url = config.url;
@@ -63,7 +65,7 @@ export class JsonRpcConnector implements Connector {
const token = response.headers.get('Authorization');
if (!response.ok) {
if ('error' in data && data.error.code === 3001) {
if ('error' in data && data.error.code === USER_REJECTED_CODE) {
throw userRejectedError();
}
throw connectError('response not ok');
@@ -137,7 +139,7 @@ export class JsonRpcConnector implements Connector {
if (!response.ok) {
if ('error' in data) {
if (data.error.code === 3001) {
if (data.error.code === USER_REJECTED_CODE) {
throw userRejectedError();
}
+102 -97
View File
@@ -1,4 +1,3 @@
import EventEmitter from 'eventemitter3';
import {
ConnectorError,
chainIdError,
@@ -6,13 +5,13 @@ import {
listKeysError,
noWalletError,
sendTransactionError,
userRejectedError,
} from '../errors';
import { type Transaction } from '../transaction-types';
import {
JsonRpcMethod,
type Connector,
type TransactionParams,
type VegaWalletEvent,
} from '../types';
enum EthereumMethod {
@@ -43,7 +42,6 @@ declare global {
type WindowEthereumProvider = {
isMetaMask: boolean;
request<T = unknown>(args: RequestArguments): Promise<T>;
selectedAddress: string | null;
};
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 {
readonly id = 'snap';
readonly name = 'MetaMask Snap';
@@ -61,8 +69,6 @@ export class SnapConnector implements Connector {
node: string;
version: string;
snapId: string;
pollRef: NodeJS.Timer | undefined;
ee: EventEmitter;
// 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
@@ -70,14 +76,21 @@ export class SnapConnector implements Connector {
this.node = config.node;
this.version = config.version;
this.snapId = config.snapId;
this.ee = new EventEmitter();
}
bindStore() {}
async connectWallet(desiredChainId: string) {
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();
@@ -87,7 +100,6 @@ export class SnapConnector implements Connector {
);
}
this.startPoll();
return { success: true };
} catch (err) {
if (err instanceof ConnectorError) {
@@ -98,57 +110,66 @@ export class SnapConnector implements Connector {
}
}
async disconnectWallet() {
this.stopPoll();
}
async disconnectWallet() {}
// deprecated, pass chain on connect
async getChainId() {
try {
const res = await this.invokeSnap<{ chainID: string }>(
const data = await this.invokeSnap<{ chainID: string }>(
JsonRpcMethod.GetChainId,
{
networkEndpoints: [this.node],
}
);
return { chainId: res.chainID };
if ('error' in data) {
throw chainIdError(data.error.message);
}
return { chainId: data.chainID };
} catch (err) {
this.stopPoll();
if (err instanceof ConnectorError) {
throw err;
}
throw chainIdError();
}
}
async listKeys() {
try {
const res = await this.invokeSnap<{
const data = await this.invokeSnap<{
keys: Array<{ publicKey: string; name: string }>;
}>(JsonRpcMethod.ListKeys);
return res.keys;
if ('error' in data) {
throw listKeysError(data.error.message);
}
return data.keys;
} catch (err) {
this.stopPoll();
if (err instanceof ConnectorError) {
throw err;
}
throw listKeysError();
}
}
async isConnected() {
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
await this.listKeys();
return { connected: true };
} catch (err) {
this.stopPoll();
return { connected: false };
}
}
async sendTransaction(params: TransactionParams) {
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;
transaction: { signature: { value: string } };
receivedAt: string;
@@ -160,115 +181,99 @@ export class SnapConnector implements Connector {
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 {
transactionHash: res.transactionHash,
signature: res.transaction.signature.value,
receivedAt: res.receivedAt,
sentAt: res.sentAt,
transactionHash: data.transactionHash,
signature: data.transaction.signature.value,
receivedAt: data.receivedAt,
sentAt: data.sentAt,
};
} catch (err) {
if (err instanceof ConnectorError) {
throw err;
}
if (this.isSnapRPCError(err)) {
throw sendTransactionError(err.message);
}
throw sendTransactionError();
}
}
on(event: VegaWalletEvent, callback: () => void) {
this.ee.on(event, callback);
}
on() {}
off() {}
off(event: VegaWalletEvent, callback?: () => void) {
this.ee.off(event, callback);
}
////////////////////////////////////
// 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
* and attempts to install them if they're not already installed.
* If the installation of any snap fails, returns the error that caused the failure.
* More informations here: https://docs.metamask.io/snaps/reference/rpc-api/#wallet_requestsnaps
*/
private async requestSnap() {
await this.request(EthereumMethod.RequestSnaps, {
[this.snapId]: {
version: this.version,
private async requestSnap(): Promise<{
[snapId: string]: {
blocked: boolean;
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
* should always be npm:@vegaprotocol/snap
*/
private async invokeSnap<TResult>(
method: JsonRpcMethod,
params?: SnapInvocationParams
): Promise<TResult> {
return await this.request(EthereumMethod.InvokeSnap, {
snapId: this.snapId,
request: {
method,
params,
params: SnapInvocationParams = {}
): Promise<TResult | { error: SnapRPCError }> {
// MetaMask in Firefox doesn't like undefined properties or some properties
// on __proto__ so we need to strip them out with JSON.strinfify
params = JSON.parse(JSON.stringify(params));
return window.ethereum.request({
method: EthereumMethod.InvokeSnap,
params: {
snapId: this.snapId,
request: {
method,
params,
},
},
});
}
/**
* Calls window.ethereum.request with method and params
*/
private async request<TResult>(
method: EthereumMethod,
params?: object
): Promise<TResult> {
if (window.ethereum?.request && window.ethereum?.isMetaMask) {
// MetaMask in Firefox doesn't like undefined properties or some properties
// 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,
});
private isSnapRPCError(obj: unknown): obj is SnapRPCError {
if (
obj !== undefined &&
obj !== null &&
typeof obj === 'object' &&
'code' in obj &&
'message' in obj
) {
return true;
}
throw noWalletError();
return false;
}
}
+1 -1
View File
@@ -12,7 +12,7 @@ export class ConnectorError extends Error {
export const ConnectorErrors = {
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 },
disconnect: { message: 'failed to disconnect', code: 3 },
chainId: { message: 'incorrect chain id', code: 4 },
+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 -2
View File
@@ -93,7 +93,6 @@ describe('disconnect', () => {
expect(result).toEqual({ status: 'disconnected' });
expect(config.store.getState()).toMatchObject({
status: 'disconnected',
error: noConnectorError(),
current: undefined,
keys: [],
pubKey: undefined,
@@ -130,7 +129,7 @@ describe('refresh keys', () => {
it('handles invalid connector', async () => {
await config.refreshKeys();
expect(config.store.getState()).toMatchObject({
error: noConnectorError(),
error: undefined,
});
});
+9 -7
View File
@@ -132,18 +132,20 @@ export function createConfig(cfg: Config): Wallet {
store.setState(getInitialState(), true);
return { status: 'disconnected' as const };
} catch (err) {
store.setState({
...getInitialState(),
error: err instanceof ConnectorError ? err : unknownError(),
});
store.setState(getInitialState(), true);
return { status: 'disconnected' as const };
}
}
async function refreshKeys() {
const connector = connectors
.getState()
.find((x) => x.id === store.getState().current);
const state = store.getState();
const connector = connectors.getState().find((x) => x.id === state.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 {
if (!connector) {
-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))