Compare commits

..
55 changed files with 373 additions and 1583 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ name: 'Add Issues To Project Board'
types:
- opened
env:
GH_TOKEN: ${{ secrets.PROJECT_MANAGE_ACTION }}
GH_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }}
PROJECT_ID: ${{ secrets.FRONT_END_PROJECT_ID }}
ISSUE_ID: ${{ github.event.issue.node_id }}
USER: ${{ github.actor }}
+8 -31
View File
@@ -5,7 +5,10 @@ on:
branches:
- release/*
- develop
pull_request:
- main
# uncomment pull_request and comment pull_request_target to test CI changes against feature branch not target branch (develop)
# pull_request:
pull_request_target:
types:
- opened
- ready_for_review
@@ -46,7 +49,7 @@ jobs:
lint-pr-title:
needs: node-modules
if: ${{ github.event_name == 'pull_request' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
name: Verify PR title
uses: ./.github/workflows/lint-pr.yml
secrets: inherit
@@ -107,7 +110,6 @@ jobs:
echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo "Affected: ${affected}"
echo "Branch slug: ${branch_slug}"
echo "Current ref: ${{ github.ref }}"
echo ">>>> eof debug"
projects_array=()
@@ -178,31 +180,6 @@ jobs:
fi
fi
# if branch starts with release/ and ends with trading / governance or explorer - overwrite the array of affected projects with fixed single application
if [[ "${{ github.ref }}" == *release* ]]; then
echo ">> This is a relase branch"
case "${{ github.ref }}" in
*trading)
echo ">> Only trading will be deployed"
projects_array=(trading)
projects_e2e_array=(trading)
;;
*governance)
echo ">> Only governance will be deployed"
projects_array=(governance)
projects_e2e_array=(governance)
;;
*explorer)
echo ">> Only explorer will be deployed"
projects_array=(explorer)
projects_e2e_array=(explorer)
;;
*)
echo ">> All apps will be deployed"
;;
esac
fi
echo "Projects: ${projects_array[@]}"
echo "Projects E2E: ${projects_e2e_array[@]}"
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}")
@@ -237,7 +214,7 @@ jobs:
publish-dist:
needs: lint-test-build
name: '(CD) publish dist'
if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo') || github.event_name == 'push' }}
# if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
uses: ./.github/workflows/publish-dist.yml
secrets: inherit
with:
@@ -248,7 +225,7 @@ jobs:
needs:
- publish-dist
- lint-test-build
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'vegaprotocol/frontend-monorepo' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
timeout-minutes: 60
name: '(CD) comment preview links'
steps:
@@ -294,7 +271,7 @@ jobs:
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
Previews
Previews:
* governance: ${{ needs.lint-test-build.outputs.preview_governance }}
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
* trading: ${{ needs.lint-test-build.outputs.preview_trading }}
-59
View File
@@ -1,59 +0,0 @@
name: console-test-run
on:
workflow_call:
inputs:
github-sha:
required: true
type: string
jobs:
console-test:
timeout-minutes: 5
runs-on: self-hosted-runner
steps:
- name: Checkout console test repo
uses: actions/checkout@v3
with:
repository: vegaprotocol/console-test
path: './console-test'
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10.11'
- name: Install Poetry
uses: snok/install-poetry@v1
- name: Install dependencies
run: |
poetry install --no-root
working-directory: ./console-test
- name: load Binaries
run: |
poetry run python -m vega_sim.tools.load_binaries
working-directory: ./console-test
- name: pull console
run: |
poetry run docker pull ghcr.io/vegaprotocol/frontend/trading:${{ inputs.github-sha }}
- name: Update container_name in config.py
run: |
sed -i "s/container_name = \".*\"/container_name = \"vegaprotocol\/frontend\/trading:${{ inputs.github-sha }}\"/g" config.py
- name: install playwright
run: poetry run playwright install
working-directory: ./console-test
- name: run tests
run: poetry run pytest --numprocesses auto
working-directory: ./console-test
- name: Upload Playwright Trace
uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-trace
path: ./traces/
retention-days: 15
+21 -48
View File
@@ -22,33 +22,6 @@ jobs:
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Init variables
run: |
echo IS_PR=false >> $GITHUB_ENV
echo IS_MAINNET_RELEASE=false >> $GITHUB_ENV
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
- name: Is PR
if: ${{ github.event_name == 'pull_request' }}
run: |
echo IS_PR=true >> $GITHUB_ENV
- name: Is mainnet release
if: ${{ contains(github.ref, 'release/mainnnet') && !contains(github.ref, 'mirror') }}
run: |
echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV
- name: Is testnet release
if: ${{ contains(github.ref, 'release/testnet') }}
run: |
echo IS_TESTNET_RELEASE=true >> $GITHUB_ENV
- name: Is IPFS Release
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( env.IS_MAINNET_RELEASE == 'true' || env.IS_TESTNET_RELEASE == 'true' ) }}
run: |
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
@@ -60,7 +33,7 @@ jobs:
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry (ghcr)
if: ${{ env.IS_PR == 'true' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
uses: docker/login-action@v2
with:
registry: ghcr.io
@@ -69,8 +42,9 @@ jobs:
- name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
with:
# registry: registry.hub.docker.com
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -96,8 +70,7 @@ jobs:
bucketName=''
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
# remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading)
envName="$(echo ${{ github.ref }} | sed -e "s|refs/heads/release/||" | cut -d '-' -f 1 )"
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
@@ -112,7 +85,7 @@ jobs:
envName="mainnet"
bucketName="ui.vega.rocks"
fi
elif [[ "${{ github.ref }}" =~ .*mainnet$ ]]; then
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
fi
@@ -172,7 +145,7 @@ jobs:
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
- name: Image digest
if: ${{ env.IS_PR == 'true' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
run: echo ${{ steps.docker_build.outputs.digest }}
- name: Sanity check docker image
@@ -187,7 +160,7 @@ jobs:
uses: docker/build-push-action@v3
continue-on-error: true
id: ghcr-push
if: ${{ env.IS_PR == 'true' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -202,7 +175,7 @@ jobs:
uses: docker/build-push-action@v3
continue-on-error: true
id: dockerhub-push
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -212,7 +185,7 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }}
tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
vegaprotocol/${{ matrix.app }}:${{ endsWith(github.ref, 'main') && 'mainnet' || endsWith(github.ref, 'release/testnet') && 'testnet' || '' }}
- name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3
@@ -239,13 +212,13 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }}
tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
vegaprotocol/${{ matrix.app }}:${{ endsWith(github.ref, 'main') && 'mainnet' || endsWith(github.ref, 'release/testnet') && 'testnet' || '' }}
# bucket creation in github.com/vegaprotocol/terraform//frontend
- name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master
# s3 releases are not happening for trading on mainnet - it's IPFS
if: ${{ env.IS_IPFS_RELEASE == 'false' }}
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
with:
args: --acl private --follow-symlinks --delete
env:
@@ -256,11 +229,11 @@ jobs:
SOURCE_DIR: 'dist-result'
- name: Install aws CLI
if: ${{ env.IS_IPFS_RELEASE == 'false' }}
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
uses: unfor19/install-aws-cli-action@master
- name: Perform cache invalidation
if: ${{ env.IS_IPFS_RELEASE == 'false' }}
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -273,16 +246,16 @@ jobs:
- name: Add preview label
uses: actions-ecosystem/action-add-labels@v1
if: ${{ env.IS_PR == 'true' }}
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
with:
labels: ${{ matrix.app }}-preview
number: ${{ github.event.number }}
- name: Trigger fleek deployment
# release to ipfs happens only on mainnet (represented by main branch) for trading
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) }}
run: |
if [[ "${{ env.IS_MAINNET_RELEASE }}" = "true" ]]; then
if echo ${{ github.ref }} | grep -q main; then
# display info about app
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
@@ -295,7 +268,7 @@ jobs:
-d '{"query": "mutation{triggerDeploy(siteId:\"f8f2e051-f18e-49e6-b876-0a39369dc0d8\"){id status}}"}' \
https://api.fleek.co/graphql
elif [[ "${{ env.IS_TESTNET_RELEASE }}" = "true" ]]; then
elif echo ${{ github.ref }} | grep -q release/testnet; then
# display info about app
curl --fail -H "Authorization: ${{ secrets.FLEEK_API_KEY }}" \
-H "Content-Type: application/json" \
@@ -310,7 +283,7 @@ jobs:
fi
- name: Check out ipfs-redirect
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
uses: actions/checkout@v3
with:
repository: 'vegaprotocol/ipfs-redirect'
@@ -319,7 +292,7 @@ jobs:
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
- name: Update interstitial page to point to the new console
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
env:
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
run: |
@@ -341,11 +314,11 @@ jobs:
git config --global user.name "vega-ci-bot"
# update CID files
if [[ "${{ env.IS_MAINNET_RELEASE }}" = "true" ]]; then
if echo ${{ github.ref }} | grep -q main; then
echo $new_hash > cidv0-mainnet.txt
echo $new_cid > cidv1-mainnet.txt
git add cidv0-mainnet.txt cidv1-mainnet.txt
elif [[ "${{ env.IS_TESTNET_RELEASE }}" = "true" ]]; then
elif echo ${{ github.ref }} | grep -q release/testnet; then
echo $new_hash > cidv0-fairground.txt
echo $new_cid > cidv1-fairground.txt
git add cidv0-fairground.txt cidv1-fairground.txt
@@ -320,8 +320,8 @@ context(
// 3001-VOTE-076
cy.getByTestId(connectToVegaWalletButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
cy.getByTestId(connectToVegaWalletButton).click();
.and('have.text', 'Connect Vega wallet')
.click();
cy.getByTestId('connector-jsonRpc').click();
cy.getByTestId(vegaWalletNameElement).should('be.visible');
cy.getByTestId(connectToVegaWalletButton).should('not.exist');
@@ -161,7 +161,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.getByTestId('menu-drawer').should('be.visible');
});
it('should have link for proposal page', function () {
it.skip('should have link for proposal page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
@@ -1,12 +1,13 @@
/// <reference types="cypress" />
import {
navigateTo,
navigation,
turnTelemetryOff,
waitForSpinner,
} from '../../support/common.functions';
import {
createTenDigitUnixTimeStampForSpecifiedDays,
enterRawProposalBody,
enterUniqueFreeFormProposalBody,
goToMakeNewProposal,
governanceProposalType,
} from '../../support/governance.functions';
@@ -46,11 +47,12 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
.and('contain.text', 'USDC (fake)');
});
it('Unable to submit proposal with public key', function () {
it.skip('Unable to submit proposal with public key', function () {
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', 'pub key proposal test');
cy.getByTestId('dialog-content')
.first()
.within(() => {
@@ -78,7 +78,7 @@ context(
cy.getByTestId('connector-jsonRpc')
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
cy.getByTestId('connector-rest')
cy.getByTestId('connector-hosted')
.should('be.visible')
.and('have.text', 'Hosted Fairground wallet');
});
@@ -94,7 +94,7 @@ context(
describe('when rest connector form opened', function () {
before('click hosted wallet app button', function () {
cy.getByTestId(connectorsList).within(() => {
cy.getByTestId('connector-rest').click();
cy.getByTestId('connector-hosted').click();
});
});
@@ -2,8 +2,13 @@ import { Button } from '@vegaprotocol/ui-toolkit';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
export const ConnectToVega = () => {
const { appDispatch } = useAppState();
const { t } = useTranslation();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
@@ -11,6 +16,10 @@ export const ConnectToVega = () => {
return (
<Button
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
data-testid="connect-to-vega-wallet-btn"
@@ -3,6 +3,11 @@ import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import React from 'react';
import { useTranslation } from 'react-i18next';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
interface VegaWalletContainerProps {
children: (key: string) => React.ReactElement;
}
@@ -10,6 +15,7 @@ interface VegaWalletContainerProps {
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
const { t } = useTranslation();
const { pubKey } = useVegaWallet();
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
@@ -19,6 +25,10 @@ export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
<Button
data-testid="connect-to-vega-wallet-btn"
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
>
@@ -13,6 +13,12 @@ export const VegaWalletDialogs = () => {
<>
<VegaConnectDialog
connectors={Connectors}
onChangeOpen={(open) =>
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: open,
})
}
riskMessage={<RiskMessage />}
/>
@@ -71,6 +71,7 @@ export const VegaWallet = () => {
const VegaWalletNotConnected = () => {
const { t } = useTranslation();
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
@@ -78,6 +79,10 @@ const VegaWalletNotConnected = () => {
<>
<Button
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
fill={true}
@@ -28,6 +28,9 @@ export interface AppState {
/** Total number of VEGA Tokens, both vesting and unlocked, associated for staking */
totalAssociated: BigNumber;
/** Whether or not the connect to VEGA wallet overlay is open */
vegaWalletOverlay: boolean;
/** Whether or not the manage VEGA wallet overlay is open */
vegaWalletManageOverlay: boolean;
@@ -49,7 +52,9 @@ export enum AppStateActionType {
SET_TOKEN,
SET_ALLOWANCE,
REFRESH_BALANCES,
SET_VEGA_WALLET_OVERLAY,
SET_VEGA_WALLET_MANAGE_OVERLAY,
SET_DRAWER,
REFRESH_ASSOCIATED_BALANCES,
SET_ASSOCIATION_BREAKDOWN,
SET_TRANSACTION_OVERLAY,
@@ -64,10 +69,18 @@ export type AppStateAction =
totalSupply: BigNumber;
totalAssociated: BigNumber;
}
| {
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY;
isOpen: boolean;
}
| {
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY;
isOpen: boolean;
}
| {
type: AppStateActionType.SET_DRAWER;
isOpen: boolean;
}
| {
type: AppStateActionType.SET_TRANSACTION_OVERLAY;
isOpen: boolean;
@@ -14,6 +14,7 @@ const initialAppState: AppState = {
totalAssociated: new BigNumber(0),
decimals: 0,
totalSupply: new BigNumber(0),
vegaWalletOverlay: false,
vegaWalletManageOverlay: false,
transactionOverlay: false,
bannerMessage: '',
@@ -30,10 +31,23 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
totalAssociated: action.totalAssociated,
};
}
case AppStateActionType.SET_VEGA_WALLET_OVERLAY: {
return {
...state,
vegaWalletOverlay: action.isOpen,
};
}
case AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY: {
return {
...state,
vegaWalletManageOverlay: action.isOpen,
vegaWalletOverlay: action.isOpen ? false : state.vegaWalletOverlay,
};
}
case AppStateActionType.SET_DRAWER: {
return {
...state,
vegaWalletOverlay: false,
};
}
case AppStateActionType.SET_TRANSACTION_OVERLAY: {
@@ -2,18 +2,15 @@ import {
RestConnector,
JsonRpcConnector,
ViewConnector,
InjectedConnector,
} from '@vegaprotocol/wallet';
const urlParams = new URLSearchParams(window.location.search);
export const injected = new InjectedConnector();
export const rest = new RestConnector();
export const jsonRpc = new JsonRpcConnector();
export const view = new ViewConnector(urlParams.get('address'));
export const Connectors = {
injected,
rest,
jsonRpc,
view,
@@ -10,7 +10,10 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { addDecimal, toBigNum } from '@vegaprotocol/utils';
import { ProposalState, VoteValue } from '@vegaprotocol/types';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import {
AppStateActionType,
useAppState,
} from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import { DATE_FORMAT_LONG } from '../../../../lib/date-formats';
import { VoteState } from './use-user-vote';
@@ -70,6 +73,7 @@ export const VoteButtons = ({
dialog: Dialog,
}: VoteButtonsProps) => {
const { t } = useTranslation();
const { appDispatch } = useAppState();
const { pubKey } = useVegaWallet();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
@@ -94,6 +98,10 @@ export const VoteButtons = ({
<div data-testid="connect-wallet">
<ButtonLink
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
>
@@ -134,6 +142,7 @@ export const VoteButtons = ({
minVoterBalance,
spamProtectionMinTokens,
t,
appDispatch,
openVegaWalletDialog,
]);
@@ -14,6 +14,7 @@ const mockAppState: AppState = {
totalAssociated: new BigNumber('50063005'),
decimals: 18,
totalSupply: mockTotalSupply,
vegaWalletOverlay: false,
vegaWalletManageOverlay: false,
transactionOverlay: false,
bannerMessage: '',
@@ -2,9 +2,14 @@ import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Button } from '@vegaprotocol/ui-toolkit';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { SubHeading } from '../../components/heading';
export const ConnectToSeeRewards = () => {
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
@@ -21,6 +26,10 @@ export const ConnectToSeeRewards = () => {
<Button
data-testid="connect-to-vega-wallet-btn"
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
>
@@ -2,6 +2,7 @@ import { removeDecimal } from '@vegaprotocol/cypress';
import * as Schema from '@vegaprotocol/types';
import {
OrderStatusMapping,
OrderTimeInForceMapping,
OrderTypeMapping,
Side,
} from '@vegaprotocol/types';
@@ -16,6 +17,7 @@ const orderStatus = 'status';
const orderRemaining = 'remaining';
const orderPrice = 'price';
const orderTimeInForce = 'timeInForce';
const orderCreatedAt = 'createdAt';
const orderUpdatedAt = 'updatedAt';
const assetSelectField = 'select[name="asset"]';
const amountField = 'input[name="amount"]';
@@ -258,7 +260,10 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
OrderStatusMapping.STATUS_ACTIVE
);
cy.get(`[col-id='${orderRemaining}']`).should('contain.text', '0.00');
cy.get(`[col-id='${orderRemaining}']`).should(
'contain.text',
`0.00/${order.size}`
);
cy.get(`[col-id='${orderPrice}']`).then(($price) => {
expect(parseFloat($price.text())).to.equal(parseFloat(order.price));
@@ -266,10 +271,10 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
cy.get(`[col-id='${orderTimeInForce}']`).should(
'contain.text',
'GTC'
OrderTimeInForceMapping[order.timeInForce]
);
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt);
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderCreatedAt);
});
});
});
@@ -68,15 +68,14 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
validateMarketDataRow(0, 'Name', 'BTCUSD Monthly (30 Jun 2022)');
validateMarketDataRow(1, 'Market ID', 'market-0');
validateMarketDataRow(2, 'Parent Market ID', 'market-1');
validateMarketDataRow(
3,
2,
'Trading Mode',
MarketTradingModeMapping.TRADING_MODE_CONTINUOUS
);
validateMarketDataRow(4, 'Market Decimal Places', '5');
validateMarketDataRow(5, 'Position Decimal Places', '0');
validateMarketDataRow(6, 'Settlement Asset Decimal Places', '5');
validateMarketDataRow(3, 'Market Decimal Places', '5');
validateMarketDataRow(4, 'Position Decimal Places', '0');
validateMarketDataRow(5, 'Settlement Asset Decimal Places', '5');
});
it('instrument displayed', () => {
@@ -63,7 +63,7 @@ describe(
cy.contains('Hosted Fairground wallet');
cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]')
.find('[data-testid="connector-hosted"]')
.click();
cy.getByTestId(form).find('#wallet').click().type('user');
cy.getByTestId(form).find('#passphrase').click().type('pass');
@@ -89,7 +89,7 @@ describe(
);
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]')
.find('[data-testid="connector-hosted"]')
.click();
cy.getByTestId(form).find('#wallet').click().type('invalid name');
cy.getByTestId(form).find('#passphrase').click().type('invalid password');
@@ -100,7 +100,7 @@ describe(
it('doesnt connect with empty fields', () => {
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
.find('[data-testid="connector-rest"]')
.find('[data-testid="connector-hosted"]')
.click();
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
+1 -1
View File
@@ -14,4 +14,4 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
# TAG name of the current app version - TODO: bump to the latest upon release
NX_APP_VERSION=v0.20.21-core-0.71.6
NX_APP_VERSION=v0.20.19-core-0.71.6
@@ -27,7 +27,6 @@ import {
import { TradingViews } from './trade-views';
import { MarketSelector } from './market-selector';
import { HeaderStats } from './header-stats';
import { MarketSuccessorBanner } from '../../components/market-banner';
interface TradeGridProps {
market: Market | null;
@@ -318,7 +317,6 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
<HeaderStats market={market} />
</div>
<div className="col-span-2">
<MarketSuccessorBanner market={market} />
<OracleBanner marketId={market?.id || ''} />
</div>
{sidebarOpen && (
@@ -21,7 +21,6 @@ import { HeaderStats } from './header-stats';
import * as DialogPrimitives from '@radix-ui/react-dialog';
import { HeaderTitle } from '../../components/header';
import { MarketSelector } from './market-selector';
import { MarketSuccessorBanner } from '../../components/market-banner';
interface TradePanelsProps {
market: Market | null;
@@ -93,7 +92,6 @@ export const TradePanels = ({
<HeaderStats market={market} />
</div>
<div>
<MarketSuccessorBanner market={market} />
<OracleBanner marketId={market?.id || ''} />
</div>
<div className="h-full">
@@ -1 +0,0 @@
export * from './market-successor-banner';
@@ -1,188 +0,0 @@
import { render, screen } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import * as dataProviders from '@vegaprotocol/data-provider';
import { MarketSuccessorBanner } from './market-successor-banner';
import * as Types from '@vegaprotocol/types';
import * as allUtils from '@vegaprotocol/utils';
import type { Market } from '@vegaprotocol/markets';
import type { PartialDeep } from 'type-fest';
const market = {
id: 'marketId',
tradableInstrument: {
instrument: {
metadata: {
tags: [],
},
},
},
marketTimestamps: {
close: null,
},
successorMarketID: 'successorMarketID',
} as unknown as Market;
let mockDataSuccessorMarket: PartialDeep<Market> | null = null;
jest.mock('@vegaprotocol/data-provider', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useDataProvider: jest.fn().mockImplementation((args) => {
if (args.skip) {
return {
data: null,
error: null,
};
}
return {
data: mockDataSuccessorMarket,
error: null,
};
}),
}));
jest.mock('@vegaprotocol/utils', () => ({
...jest.requireActual('@vegaprotocol/utils'),
getMarketExpiryDate: jest.fn(),
}));
let mockCandles = {};
jest.mock('@vegaprotocol/markets', () => ({
...jest.requireActual('@vegaprotocol/markets'),
useCandles: () => mockCandles,
}));
describe('MarketSuccessorBanner', () => {
beforeEach(() => {
jest.clearAllMocks();
mockDataSuccessorMarket = {
id: 'successorMarketID',
state: Types.MarketState.STATE_ACTIVE,
tradingMode: Types.MarketTradingMode.TRADING_MODE_CONTINUOUS,
tradableInstrument: {
instrument: {
name: 'Successor Market Name',
},
},
};
});
describe('should be hidden', () => {
it('when no market', () => {
const { container } = render(<MarketSuccessorBanner market={null} />, {
wrapper: MockedProvider,
});
expect(container).toBeEmptyDOMElement();
});
it('when no successorMarketID', () => {
const amendedMarket = {
...market,
successorMarketID: null,
};
const { container } = render(
<MarketSuccessorBanner market={amendedMarket} />,
{
wrapper: MockedProvider,
}
);
expect(container).toBeEmptyDOMElement();
expect(dataProviders.useDataProvider).lastCalledWith(
expect.objectContaining({ skip: true })
);
});
it('no successor market data', () => {
mockDataSuccessorMarket = null;
const { container } = render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(container).toBeEmptyDOMElement();
expect(dataProviders.useDataProvider).lastCalledWith(
expect.objectContaining({
variables: { marketId: 'successorMarketID' },
skip: false,
})
);
});
it('successor market not in continuous mode', () => {
mockDataSuccessorMarket = {
...mockDataSuccessorMarket,
tradingMode: Types.MarketTradingMode.TRADING_MODE_NO_TRADING,
};
const { container } = render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(container).toBeEmptyDOMElement();
expect(dataProviders.useDataProvider).lastCalledWith(
expect.objectContaining({
variables: { marketId: 'successorMarketID' },
skip: false,
})
);
expect(allUtils.getMarketExpiryDate).toHaveBeenCalled();
});
it('successor market is not active', () => {
mockDataSuccessorMarket = {
...mockDataSuccessorMarket,
state: Types.MarketState.STATE_PENDING,
};
const { container } = render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(container).toBeEmptyDOMElement();
expect(dataProviders.useDataProvider).lastCalledWith(
expect.objectContaining({
variables: { marketId: 'successorMarketID' },
skip: false,
})
);
expect(allUtils.getMarketExpiryDate).toHaveBeenCalled();
});
});
describe('should be displayed', () => {
it('should be rendered', () => {
render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(
screen.getByText('This market has been succeeded')
).toBeInTheDocument();
expect(
screen.getByRole('link', { name: 'Successor Market Name' })
).toHaveAttribute('href', '/#/markets/successorMarketID');
});
it('should display optionally successor volume', () => {
mockDataSuccessorMarket = {
...mockDataSuccessorMarket,
positionDecimalPlaces: 3,
};
mockCandles = {
oneDayCandles: [
{ volume: 123 },
{ volume: 456 },
{ volume: 789 },
{ volume: 99999 },
],
};
render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(screen.getByText('has 101.367 24h vol.')).toBeInTheDocument();
});
it('should display optionally duration', () => {
jest
.spyOn(allUtils, 'getMarketExpiryDate')
.mockReturnValue(
new Date(Date.now() + 24 * 60 * 60 * 1000 + 60 * 1000)
);
render(<MarketSuccessorBanner market={market} />, {
wrapper: MockedProvider,
});
expect(
screen.getByText(/^This market expires in 1 day/)
).toBeInTheDocument();
});
});
});
@@ -1,115 +0,0 @@
import { useState } from 'react';
import { isBefore, formatDuration, intervalToDuration } from 'date-fns';
import { useDataProvider } from '@vegaprotocol/data-provider';
import type { Market } from '@vegaprotocol/markets';
import {
calcCandleVolume,
marketProvider,
useCandles,
} from '@vegaprotocol/markets';
import {
ExternalLink,
Intent,
NotificationBanner,
} from '@vegaprotocol/ui-toolkit';
import {
addDecimalsFormatNumber,
getMarketExpiryDate,
isNumeric,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import * as Types from '@vegaprotocol/types';
const getExpiryDate = (tags: string[], close?: string): Date | null => {
const expiryDate = getMarketExpiryDate(tags);
return expiryDate || (close && new Date(close)) || null;
};
export const MarketSuccessorBanner = ({
market,
}: {
market: Market | null;
}) => {
const { data: successorData } = useDataProvider({
dataProvider: marketProvider,
variables: {
marketId: market?.successorMarketID || '',
},
skip: !market?.successorMarketID,
});
const [visible, setVisible] = useState(true);
const expiry = market
? getExpiryDate(
market.tradableInstrument.instrument.metadata.tags || [],
market.marketTimestamps.close
)
: null;
const duration =
expiry && isBefore(new Date(), expiry)
? intervalToDuration({ start: new Date(), end: expiry })
: null;
const isInContinuesMode =
successorData?.state === Types.MarketState.STATE_ACTIVE &&
successorData?.tradingMode ===
Types.MarketTradingMode.TRADING_MODE_CONTINUOUS;
const { oneDayCandles } = useCandles({
marketId: successorData?.id,
});
const candleVolume = oneDayCandles?.length
? calcCandleVolume(oneDayCandles)
: null;
const successorVolume =
candleVolume && isNumeric(successorData?.positionDecimalPlaces)
? addDecimalsFormatNumber(
candleVolume,
successorData?.positionDecimalPlaces as number
)
: null;
if (isInContinuesMode && visible) {
return (
<NotificationBanner
intent={Intent.Primary}
onClose={() => {
setVisible(false);
}}
>
<div className="uppercase mb-1">
{t('This market has been succeeded')}
</div>
<div>
{duration && (
<span>
{t('This market expires in %s.', [
formatDuration(duration, {
format: [
'years',
'months',
'weeks',
'days',
'hours',
'minutes',
],
}),
])}
</span>
)}{' '}
{t('The successor market')}{' '}
<ExternalLink href={`/#/markets/${successorData?.id}`}>
{successorData?.tradableInstrument.instrument.name}
</ExternalLink>
{successorVolume && (
<span> {t('has %s 24h vol.', [successorVolume])}</span>
)}
</div>
</NotificationBanner>
);
}
return null;
};
@@ -190,9 +190,6 @@ export const VegaWalletConnectButton = () => {
>
<DropdownMenuContent
onInteractOutside={() => setDropdownOpen(false)}
sideOffset={20}
side="bottom"
align="end"
>
<div className="min-w-[340px]" data-testid="keypair-list">
<DropdownMenuRadioGroup
-3
View File
@@ -2,12 +2,10 @@ import {
RestConnector,
JsonRpcConnector,
ViewConnector,
InjectedConnector,
} from '@vegaprotocol/wallet';
export const rest = new RestConnector();
export const jsonRpc = new JsonRpcConnector();
export const injected = new InjectedConnector();
let view: ViewConnector;
if (typeof window !== 'undefined') {
@@ -18,7 +16,6 @@ if (typeof window !== 'undefined') {
}
export const Connectors = {
injected,
rest,
jsonRpc,
view,
+1 -1
View File
@@ -180,4 +180,4 @@ export function useLiquidityProviderFeeShareLazyQuery(baseOptions?: Apollo.LazyQ
}
export type LiquidityProviderFeeShareQueryHookResult = ReturnType<typeof useLiquidityProviderFeeShareQuery>;
export type LiquidityProviderFeeShareLazyQueryHookResult = ReturnType<typeof useLiquidityProviderFeeShareLazyQuery>;
export type LiquidityProviderFeeShareQueryResult = Apollo.QueryResult<LiquidityProviderFeeShareQuery, LiquidityProviderFeeShareQueryVariables>;
export type LiquidityProviderFeeShareQueryResult = Apollo.QueryResult<LiquidityProviderFeeShareQuery, LiquidityProviderFeeShareQueryVariables>;
+2 -3
View File
@@ -7,12 +7,12 @@ export type DataSourceFilterFragment = { __typename?: 'Filter', key: { __typenam
export type DataSourceSpecFragment = { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } };
export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, successorMarketID?: string | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } };
export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } };
export type MarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, successorMarketID?: string | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null };
export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number, quantum: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null };
export const DataSourceFilterFragmentDoc = gql`
fragment DataSourceFilter on Filter {
@@ -104,7 +104,6 @@ export const MarketFieldsFragmentDoc = gql`
open
close
}
successorMarketID
}
${DataSourceSpecFragmentDoc}`;
export const MarketsDocument = gql`
@@ -142,6 +142,5 @@ query MarketInfo($marketId: ID!) {
}
}
}
parentMarketID
}
}
@@ -10,7 +10,7 @@ export type MarketInfoQueryVariables = Types.Exact<{
}>;
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, parentMarketID?: string | null, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
export const DataSourceFragmentDoc = gql`
fragment DataSource on DataSourceDefinition {
@@ -158,7 +158,6 @@ export const MarketInfoDocument = gql`
}
}
}
parentMarketID
}
}
${DataSourceFragmentDoc}`;
@@ -144,7 +144,6 @@ export const KeyDetailsInfoPanel = ({ market }: MarketInfoProps) => {
data={{
name: market.tradableInstrument.instrument.name,
marketID: market.id,
parentMarketID: market.parentMarketID,
tradingMode:
market.tradingMode && MarketTradingModeMapping[market.tradingMode],
marketDecimalPlaces: market.decimalPlaces,
@@ -191,7 +191,6 @@ export const marketInfoQuery = (
},
},
},
parentMarketID: 'market-1',
},
};
@@ -102,5 +102,4 @@ export const tooltipMapping: Record<string, ReactNode> = {
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
),
suppliedStake: t('The current amount of liquidity supplied for this market.'),
parentMarketID: t('The ID of the market this market succeeds'),
};
-1
View File
@@ -85,7 +85,6 @@ fragment MarketFields on Market {
open
close
}
successorMarketID
}
query Markets {
+8 -320
View File
@@ -356,13 +356,6 @@ export enum BusEventType {
Withdrawal = 'Withdrawal'
}
/** Allows for cancellation of an existing governance transfer */
export type CancelTransfer = {
__typename?: 'CancelTransfer';
/** The governance transfer to cancel */
transferId: Scalars['ID'];
};
/** Candle stick representation of trading */
export type Candle = {
__typename?: 'Candle';
@@ -374,8 +367,6 @@ export type Candle = {
lastUpdateInPeriod: Scalars['Timestamp'];
/** Low price (uint64) */
low: Scalars['String'];
/** Total notional value of trades (uint64) */
notional: Scalars['String'];
/** Open price (uint64) */
open: Scalars['String'];
/** RFC3339Nano formatted date and time for the candle start time */
@@ -1132,17 +1123,6 @@ export type FutureProduct = {
settlementAsset: Asset;
};
export type GovernanceTransferKind = OneOffGovernanceTransfer | RecurringGovernanceTransfer;
export enum GovernanceTransferType {
/** Transfers the specified amount or does not transfer anything */
GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING = 'GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING',
/** Transfers the specified amount or the max allowable amount if this is less than the specified amount */
GOVERNANCE_TRANSFER_TYPE_BEST_EFFORT = 'GOVERNANCE_TRANSFER_TYPE_BEST_EFFORT',
/** Default value, always invalid */
GOVERNANCE_TRANSFER_TYPE_UNSPECIFIED = 'GOVERNANCE_TRANSFER_TYPE_UNSPECIFIED'
}
/** A segment of data node history */
export type HistorySegment = {
__typename?: 'HistorySegment';
@@ -1154,17 +1134,6 @@ export type HistorySegment = {
toHeight: Scalars['Int'];
};
/** Details of the iceberg order */
export type IcebergOrder = {
__typename?: 'IcebergOrder';
/** If the visible size of the order falls below this value, it will be replenished back to the peak size using the reserved amount */
minimumVisibleSize: Scalars['String'];
/** Size of the order that will be made visible if the iceberg order is replenished after trading */
peakSize: Scalars['String'];
/** Size of the order that is reserved and used to restore the iceberg's peak when it is refreshed */
reservedRemaining: Scalars['String'];
};
/** Describes something that can be traded on Vega */
export type Instrument = {
__typename?: 'Instrument';
@@ -1336,12 +1305,10 @@ export type LiquidityProviderFeeShare = {
averageEntryValuation: Scalars['String'];
/** The average liquidity score */
averageScore: Scalars['String'];
/** The share owned by this liquidity provider */
/** The share owned by this liquidity provider (float) */
equityLikeShare: Scalars['String'];
/** The liquidity provider party ID */
party: Party;
/** The virtual stake for this liquidity provider */
virtualStake: Scalars['String'];
};
/** The command to be sent to the chain for a liquidity provision submission */
@@ -1356,7 +1323,7 @@ export type LiquidityProvision = {
/** Nominated liquidity fee factor, which is an input to the calculation of liquidity fees on the market, as per setting fees and rewarding liquidity providers. */
fee: Scalars['String'];
/** Unique identifier for the order (set by the system after consensus) */
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
/** Market for the order */
market: Market;
/** The party making this commitment */
@@ -1405,7 +1372,7 @@ export type LiquidityProvisionUpdate = {
/** Nominated liquidity fee factor, which is an input to the calculation of liquidity fees on the market, as per setting fees and rewarding liquidity providers. */
fee: Scalars['String'];
/** Unique identifier for the order (set by the system after consensus) */
id: Scalars['ID'];
id?: Maybe<Scalars['ID']>;
/** Market for the order */
marketID: Scalars['ID'];
/** The party making this commitment */
@@ -1579,8 +1546,6 @@ export type Market = {
fees: Fees;
/** Market ID */
id: Scalars['ID'];
/** Optional: When a successor market is created, a fraction of the parent market's insurance pool can be transferred to the successor market */
insurancePoolFraction?: Maybe<Scalars['String']>;
/** Linear slippage factor is used to cap the slippage component of maintainence margin - it is applied to the slippage volume */
linearSlippageFactor: Scalars['String'];
/** Liquidity monitoring parameters for the market */
@@ -1598,11 +1563,6 @@ export type Market = {
openingAuction: AuctionDuration;
/** Orders on a market */
ordersConnection?: Maybe<OrderConnection>;
/**
* Optional: Parent market ID. A market can be a successor to another market. If this market is a successor to a previous market,
* this field will be populated with the ID of the previous market.
*/
parentMarketID?: Maybe<Scalars['ID']>;
/**
* The number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
@@ -1620,8 +1580,6 @@ export type Market = {
riskFactors?: Maybe<RiskFactor>;
/** Current state of the market */
state: MarketState;
/** Optional: Market ID of the successor to this market if one exists */
successorMarketID?: Maybe<Scalars['ID']>;
/** An instance of, or reference to, a tradable instrument. */
tradableInstrument: TradableInstrument;
/** @deprecated Simplify and consolidate trades query and remove nesting. Use trades query instead */
@@ -1714,16 +1672,12 @@ export type MarketData = {
indicativePrice: Scalars['String'];
/** Indicative volume if the auction ended now, 0 if not in auction mode */
indicativeVolume: Scalars['String'];
/** The last traded price (an unsigned integer) */
lastTradedPrice: Scalars['String'];
/** The equity like share of liquidity fee for each liquidity provider */
liquidityProviderFeeShare?: Maybe<Array<LiquidityProviderFeeShare>>;
/** The mark price (an unsigned integer) */
markPrice: Scalars['String'];
/** Market of the associated mark price */
market: Market;
/** The market growth factor for the last market time window */
marketGrowth: Scalars['String'];
/** Current state of the market */
marketState: MarketState;
/** What mode the market is in (auction, continuous, etc) */
@@ -1821,7 +1775,7 @@ export type MarketDepthUpdate = {
sequenceNumber: Scalars['String'];
};
/** Edge type containing the market and cursor information returned by a MarketConnection */
/** Edge type containing the order and cursor information returned by a OrderConnection */
export type MarketEdge = {
__typename?: 'MarketEdge';
/** The cursor for this market */
@@ -1978,7 +1932,7 @@ export type NewMarket = {
decimalPlaces: Scalars['Int'];
/** New market instrument configuration */
instrument: InstrumentConfiguration;
/** Linear slippage factor is used to cap the slippage component of maintenance margin - it is applied to the slippage volume */
/** Linear slippage factor is used to cap the slippage component of maintainence margin - it is applied to the slippage volume */
linearSlippageFactor: Scalars['String'];
/** Liquidity monitoring parameters */
liquidityMonitoringParameters: LiquidityMonitoringParameters;
@@ -1990,34 +1944,10 @@ export type NewMarket = {
positionDecimalPlaces: Scalars['Int'];
/** Price monitoring parameters */
priceMonitoringParameters: PriceMonitoringParameters;
/** Quadratic slippage factor is used to cap the slippage component of maintenance margin - it is applied to the square of the slippage volume */
/** Quadratic slippage factor is used to cap the slippage component of maintainence margin - it is applied to the square of the slippage volume */
quadraticSlippageFactor: Scalars['String'];
/** New market risk configuration */
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>;
};
export type NewTransfer = {
__typename?: 'NewTransfer';
/** The maximum amount to be transferred */
amount: Scalars['String'];
/** The asset to transfer */
asset: Asset;
/** The destination account */
destination: Scalars['String'];
/** The type of destination account */
destinationType: AccountType;
/** The fraction of the balance to be transferred */
fraction_of_balance: Scalars['String'];
/** The type of governance transfer being made, i.e. a one-off or recurring transfer */
kind: GovernanceTransferKind;
/** The source account */
source: Scalars['String'];
/** The type of source account */
sourceType: AccountType;
/** The type of the governance transfer */
transferType: GovernanceTransferType;
};
/** Information available for a node */
@@ -2231,14 +2161,10 @@ export type ObservableMarketData = {
indicativePrice: Scalars['String'];
/** Indicative volume if the auction ended now, 0 if not in auction mode */
indicativeVolume: Scalars['String'];
/** The last traded price (an unsigned integer) */
lastTradedPrice: Scalars['String'];
/** The equity like share of liquidity fee for each liquidity provider */
liquidityProviderFeeShare?: Maybe<Array<ObservableLiquidityProviderFeeShare>>;
/** The mark price (an unsigned integer) */
markPrice: Scalars['String'];
/** The market growth factor for the last market time window */
marketGrowth: Scalars['String'];
/** Market ID of the associated mark price */
marketId: Scalars['ID'];
/** Current state of the market */
@@ -2303,13 +2229,6 @@ export type ObservableMarketDepthUpdate = {
sequenceNumber: Scalars['String'];
};
/** The specific details for a one-off governance transfer */
export type OneOffGovernanceTransfer = {
__typename?: 'OneOffGovernanceTransfer';
/** An optional time when the transfer should be delivered */
deliverOn?: Maybe<Scalars['Timestamp']>;
};
/** The specific details for a one-off transfer */
export type OneOffTransfer = {
__typename?: 'OneOffTransfer';
@@ -2374,8 +2293,6 @@ export type Order = {
createdAt: Scalars['Timestamp'];
/** Expiration time of this order (ISO-8601 RFC3339+Nano formatted date) */
expiresAt?: Maybe<Scalars['Timestamp']>;
/** Details of an iceberg order */
icebergOrder?: Maybe<IcebergOrder>;
/** Hash of the order data */
id: Scalars['ID'];
/** The liquidity provision this order was created from */
@@ -2620,35 +2537,6 @@ export enum OrderStatus {
STATUS_STOPPED = 'STATUS_STOPPED'
}
/** Details of the order that will be submitted when the stop order is triggered. */
export type OrderSubmission = {
__typename?: 'OrderSubmission';
/** Expiration time of this order (ISO-8601 RFC3339+Nano formatted date) */
expiresAt: Scalars['Timestamp'];
/** Details of an iceberg order */
icebergOrder?: Maybe<IcebergOrder>;
/** Market the order is for. */
marketId: Scalars['ID'];
/** PeggedOrder contains the details about a pegged order */
peggedOrder?: Maybe<PeggedOrder>;
/** Is this a post only order */
postOnly?: Maybe<Scalars['Boolean']>;
/** The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64) */
price: Scalars['String'];
/** Is this a reduce only order */
reduceOnly?: Maybe<Scalars['Boolean']>;
/** The external reference (if available) for the order */
reference?: Maybe<Scalars['String']>;
/** Whether the order is to buy or sell */
side: Side;
/** Total number of units that may be bought or sold (immutable) (uint64) */
size: Scalars['String'];
/** The timeInForce of order (determines how and if it executes, and whether it persists on the book) */
timeInForce: OrderTimeInForce;
/** The order type */
type: OrderType;
};
/** Valid order types, these determine what happens when an order is added to the book */
export enum OrderTimeInForce {
/** Fill or Kill: The order either trades completely (remainingSize == 0 after adding) or not at all, does not remain on the book if it doesn't trade */
@@ -2688,8 +2576,6 @@ export type OrderUpdate = {
createdAt: Scalars['Timestamp'];
/** Expiration time of this order (ISO-8601 RFC3339+Nano formatted date) */
expiresAt?: Maybe<Scalars['Timestamp']>;
/** Details of an iceberg order */
icebergOrder?: Maybe<IcebergOrder>;
/** Hash of the order data */
id: Scalars['ID'];
/** The liquidity provision this order was created from */
@@ -3203,7 +3089,7 @@ export type Proposal = {
votes: ProposalVotes;
};
export type ProposalChange = CancelTransfer | NewAsset | NewFreeform | NewMarket | NewTransfer | UpdateAsset | UpdateMarket | UpdateNetworkParameter;
export type ProposalChange = NewAsset | NewFreeform | NewMarket | UpdateAsset | UpdateMarket | UpdateNetworkParameter;
export type ProposalDetail = {
__typename?: 'ProposalDetail';
@@ -3274,12 +3160,6 @@ export enum ProposalRejectionReason {
PROPOSAL_ERROR_ENACT_TIME_TOO_SOON = 'PROPOSAL_ERROR_ENACT_TIME_TOO_SOON',
/** The ERC-20 address specified by this proposal is already in use by another asset */
PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE = 'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE',
/** The proposal for cancellation of an active governance transfer has failed */
PROPOSAL_ERROR_GOVERNANCE_CANCEL_TRANSFER_PROPOSAL_INVALID = 'PROPOSAL_ERROR_GOVERNANCE_CANCEL_TRANSFER_PROPOSAL_INVALID',
/** The governance transfer proposal has failed */
PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_FAILED = 'PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_FAILED',
/** The governance transfer proposal is invalid */
PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID = 'PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID',
/** Proposal terms timestamps are not compatible (Validation < Closing < Enactment) */
PROPOSAL_ERROR_INCOMPATIBLE_TIMESTAMPS = 'PROPOSAL_ERROR_INCOMPATIBLE_TIMESTAMPS',
/** The proposal is rejected because the party does not have enough equity like share in the market */
@@ -3304,10 +3184,6 @@ export enum ProposalRejectionReason {
PROPOSAL_ERROR_INVALID_RISK_PARAMETER = 'PROPOSAL_ERROR_INVALID_RISK_PARAMETER',
/** Market proposal has one or more invalid liquidity shapes */
PROPOSAL_ERROR_INVALID_SHAPE = 'PROPOSAL_ERROR_INVALID_SHAPE',
/** Validation of spot market proposal failed */
PROPOSAL_ERROR_INVALID_SPOT = 'PROPOSAL_ERROR_INVALID_SPOT',
/** Validation of successor market has failed */
PROPOSAL_ERROR_INVALID_SUCCESSOR_MARKET = 'PROPOSAL_ERROR_INVALID_SUCCESSOR_MARKET',
/** Proposal declined because the majority threshold was not reached */
PROPOSAL_ERROR_MAJORITY_THRESHOLD_NOT_REACHED = 'PROPOSAL_ERROR_MAJORITY_THRESHOLD_NOT_REACHED',
/** Market proposal is missing a liquidity commitment */
@@ -3623,12 +3499,6 @@ export type Query = {
protocolUpgradeStatus?: Maybe<ProtocolUpgradeStatus>;
/** Get statistics about the Vega node */
statistics: Statistics;
/** Get stop order by ID */
stopOrder?: Maybe<StopOrder>;
/** Get a list of stop orders. If provided, the filter will be applied to the list of stop orders to restrict the results. */
stopOrders?: Maybe<StopOrderConnection>;
/** List markets in a succession line */
successorMarkets?: Maybe<SuccessorMarketConnection>;
/** Get a list of all trades and apply any given filters to the results */
trades?: Maybe<TradeConnection>;
/** Get a list of all transfers for a public key */
@@ -3688,7 +3558,6 @@ export type QueryentitiesArgs = {
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QueryepochArgs = {
block?: InputMaybe<Scalars['String']>;
id?: InputMaybe<Scalars['ID']>;
};
@@ -3934,27 +3803,6 @@ export type QueryprotocolUpgradeProposalsArgs = {
};
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QuerystopOrderArgs = {
id: Scalars['ID'];
};
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QuerystopOrdersArgs = {
filter?: InputMaybe<StopOrderFilter>;
pagination?: InputMaybe<Pagination>;
};
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QuerysuccessorMarketsArgs = {
fullHistory?: InputMaybe<Scalars['Boolean']>;
marketId: Scalars['ID'];
pagination?: InputMaybe<Pagination>;
};
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QuerytradesArgs = {
dateRange?: InputMaybe<DateRange>;
@@ -3999,15 +3847,6 @@ export type RankingScore = {
votingPower: Scalars['String'];
};
/** The specific details for a recurring governance transfer */
export type RecurringGovernanceTransfer = {
__typename?: 'RecurringGovernanceTransfer';
/** An optional epoch at which this transfer will stop */
endEpoch?: Maybe<Scalars['Int']>;
/** The epoch at which this recurring transfer will start */
startEpoch: Scalars['Int'];
};
/** The specific details for a recurring transfer */
export type RecurringTransfer = {
__typename?: 'RecurringTransfer';
@@ -4343,117 +4182,6 @@ export type Statistics = {
vegaTime: Scalars['Timestamp'];
};
/** A stop order in Vega */
export type StopOrder = {
__typename?: 'StopOrder';
/** Time the stop order was created. */
createdAt: Scalars['Timestamp'];
/** Time at which the order will expire if an expiry time is set. */
expiresAt?: Maybe<Scalars['Timestamp']>;
/** If an expiry is set, what should the stop order do when it expires. */
expiryStrategy?: Maybe<StopOrderExpiryStrategy>;
/** Hash of the stop order data */
id: Scalars['ID'];
/** Market the stop order is for. */
marketId: Scalars['ID'];
/** If OCO (one-cancels-other) order, the ID of the associated order. */
ocoLinkId?: Maybe<Scalars['ID']>;
/** Party that submitted the stop order. */
partyId: Scalars['ID'];
/** Status of the stop order */
status: StopOrderStatus;
/** Order to submit when the stop order is triggered. */
submission: OrderSubmission;
/** Price movement that will trigger the stop order */
trigger?: Maybe<StopOrderTrigger>;
/** Direction the price is moving to trigger the stop order. */
triggerDirection: StopOrderTriggerDirection;
/** Time the stop order was last updated. */
updatedAt?: Maybe<Scalars['Timestamp']>;
};
/** Connection type for retrieving cursory-based paginated stop order information */
export type StopOrderConnection = {
__typename?: 'StopOrderConnection';
/** The stop orders in this connection */
edges?: Maybe<Array<StopOrderEdge>>;
/** The pagination information */
pageInfo?: Maybe<PageInfo>;
};
/** Edge type containing the stop order and cursor information returned by a StopOrderConnection */
export type StopOrderEdge = {
__typename?: 'StopOrderEdge';
/** The cursor for this stop order */
cursor?: Maybe<Scalars['String']>;
/** The stop order */
node?: Maybe<StopOrder>;
};
/** Valid stop order expiry strategies. The expiry strategy determines what happens to a stop order when it expires. */
export enum StopOrderExpiryStrategy {
/** The stop order will be cancelled when it expires. */
EXPIRY_STRATEGY_CANCELS = 'EXPIRY_STRATEGY_CANCELS',
/** The stop order will be submitted when the expiry time is reached. */
EXPIRY_STRATEGY_SUBMIT = 'EXPIRY_STRATEGY_SUBMIT',
/** The stop order expiry strategy has not been specified by the trader. */
EXPIRY_STRATEGY_UNSPECIFIED = 'EXPIRY_STRATEGY_UNSPECIFIED'
}
/** Filter to be applied when querying a list of stop orders. If multiple criteria are specified, e.g. parties and markets, then the filter is applied as an AND. */
export type StopOrderFilter = {
/** Date range to retrieve order from/to. Start and end time should be expressed as an integer value of nano-seconds past the Unix epoch */
dateRange?: InputMaybe<DateRange>;
/** Zero or more expiry strategies to filter by */
expiryStrategy?: InputMaybe<Array<StopOrderExpiryStrategy>>;
/** Zero or more market IDs to filter by */
markets?: InputMaybe<Array<Scalars['ID']>>;
/** Zero or more party IDs to filter by */
parties?: InputMaybe<Array<Scalars['ID']>>;
/** Zero or more order status to filter by */
status?: InputMaybe<Array<StopOrderStatus>>;
};
/** Price at which a stop order will trigger */
export type StopOrderPrice = {
__typename?: 'StopOrderPrice';
price: Scalars['String'];
};
/** Valid stop order statuses, these determine several states for a stop order that cannot be expressed with other fields in StopOrder. */
export enum StopOrderStatus {
/** Stop order has been cancelled. This could be by the trader or by the network. */
STATUS_CANCELLED = 'STATUS_CANCELLED',
/** Stop order has expired. This means the trigger conditions have not been met and the stop order has expired. */
STATUS_EXPIRED = 'STATUS_EXPIRED',
/** Stop order is pending. This means the stop order has been accepted in the network, but the trigger conditions have not been met. */
STATUS_PENDING = 'STATUS_PENDING',
/** Stop order has been rejected. This means the stop order was not accepted by the network. */
STATUS_REJECTED = 'STATUS_REJECTED',
/** Stop order has been stopped. This means the trigger conditions have been met, but the stop order was not executed, and stopped. */
STATUS_STOPPED = 'STATUS_STOPPED',
/** Stop order has been triggered. This means the trigger conditions have been met, and the stop order was executed. */
STATUS_TRIGGERED = 'STATUS_TRIGGERED',
/** Stop order has been submitted to the network but does not have a status yet */
STATUS_UNSPECIFIED = 'STATUS_UNSPECIFIED'
}
/** Percentage movement in the price at which a stop order will trigger. */
export type StopOrderTrailingPercentOffset = {
__typename?: 'StopOrderTrailingPercentOffset';
trailingPercentOffset: Scalars['String'];
};
export type StopOrderTrigger = StopOrderPrice | StopOrderTrailingPercentOffset;
/** Valid stop order trigger direction. The trigger direction determines whether the price should rise above or fall below the stop order trigger. */
export enum StopOrderTriggerDirection {
/** The price should fall below the trigger. */
TRIGGER_DIRECTION_FALLS_BELOW = 'TRIGGER_DIRECTION_FALLS_BELOW',
/** The price should rise above the trigger. */
TRIGGER_DIRECTION_RISES_ABOVE = 'TRIGGER_DIRECTION_RISES_ABOVE'
}
/** Subscriptions allow a caller to receive new information as it is available from the Vega network. */
export type Subscription = {
__typename?: 'Subscription';
@@ -4586,40 +4314,6 @@ export type SubscriptionvotesArgs = {
proposalId?: InputMaybe<Scalars['ID']>;
};
export type SuccessorConfiguration = {
__typename?: 'SuccessorConfiguration';
/** Decimal value between 0 and 1, specifying the fraction of the insurance pool balance is carried over from the parent market to the successor. */
insurancePoolFraction: Scalars['String'];
/** ID of the market this proposal will succeed */
parentMarketId: Scalars['String'];
};
export type SuccessorMarket = {
__typename?: 'SuccessorMarket';
/** The market */
market: Market;
/** Proposals for child markets */
proposals?: Maybe<Array<Maybe<Proposal>>>;
};
/** Connection type for retrieving cursor-based paginated market information */
export type SuccessorMarketConnection = {
__typename?: 'SuccessorMarketConnection';
/** The markets in this connection */
edges: Array<SuccessorMarketEdge>;
/** The pagination information */
pageInfo: PageInfo;
};
/** Edge type containing the market and cursor information returned by a MarketConnection */
export type SuccessorMarketEdge = {
__typename?: 'SuccessorMarketEdge';
/** The cursor for this market */
cursor: Scalars['String'];
/** The market */
node: SuccessorMarket;
};
/** TargetStakeParameters contains parameters used in target stake calculation */
export type TargetStakeParameters = {
__typename?: 'TargetStakeParameters';
@@ -4852,7 +4546,7 @@ export type TransferEdge = {
node: Transfer;
};
export type TransferKind = OneOffGovernanceTransfer | OneOffTransfer | RecurringGovernanceTransfer | RecurringTransfer;
export type TransferKind = OneOffTransfer | RecurringTransfer;
export type TransferResponse = {
__typename?: 'TransferResponse';
@@ -4899,10 +4593,6 @@ export enum TransferType {
TRANSFER_TYPE_CLEAR_ACCOUNT = 'TRANSFER_TYPE_CLEAR_ACCOUNT',
/** Funds deposited to general account */
TRANSFER_TYPE_DEPOSIT = 'TRANSFER_TYPE_DEPOSIT',
/** An internal instruction to transfer a quantity corresponding to an active spot order from a general account into a party holding account */
TRANSFER_TYPE_HOLDING_LOCK = 'TRANSFER_TYPE_HOLDING_LOCK',
/** An internal instruction to transfer an excess quantity corresponding to an active spot order from a holding account into a party general account */
TRANSFER_TYPE_HOLDING_RELEASE = 'TRANSFER_TYPE_HOLDING_RELEASE',
/** Infrastructure fee received into general account */
TRANSFER_TYPE_INFRASTRUCTURE_FEE_DISTRIBUTE = 'TRANSFER_TYPE_INFRASTRUCTURE_FEE_DISTRIBUTE',
/** Infrastructure fee paid from general account */
@@ -4929,8 +4619,6 @@ export enum TransferType {
TRANSFER_TYPE_MTM_WIN = 'TRANSFER_TYPE_MTM_WIN',
/** Reward payout received */
TRANSFER_TYPE_REWARD_PAYOUT = 'TRANSFER_TYPE_REWARD_PAYOUT',
/** Spot trade delivery */
TRANSFER_TYPE_SPOT = 'TRANSFER_TYPE_SPOT',
/** A network internal instruction for the collateral engine to move funds from the pending transfers pool account into the destination account */
TRANSFER_TYPE_TRANSFER_FUNDS_DISTRIBUTE = 'TRANSFER_TYPE_TRANSFER_FUNDS_DISTRIBUTE',
/** A network internal instruction for the collateral engine to move funds from a user's general account into the pending transfers pool */
-15
View File
@@ -321,15 +321,6 @@ export const ProposalRejectionReasonMapping: {
PROPOSAL_ERROR_UNSUPPORTED_TRADING_MODE: 'Unsupported trading mode',
PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE:
'ERC20 address already in use by an existing asset',
PROPOSAL_ERROR_GOVERNANCE_CANCEL_TRANSFER_PROPOSAL_INVALID:
'PROPOSAL_ERROR_GOVERNANCE_CANCEL_TRANSFER_PROPOSAL_INVALID',
PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_FAILED:
'PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_FAILED',
PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID:
'PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID',
PROPOSAL_ERROR_INVALID_SPOT: 'PROPOSAL_ERROR_INVALID_SPOT',
PROPOSAL_ERROR_INVALID_SUCCESSOR_MARKET:
'PROPOSAL_ERROR_INVALID_SUCCESSOR_MARKET',
};
/**
@@ -428,9 +419,6 @@ export const TransferTypeMapping: TransferTypeMap = {
TRANSFER_TYPE_TRANSFER_FUNDS_DISTRIBUTE: 'Transfer received',
TRANSFER_TYPE_CLEAR_ACCOUNT: 'Market accounts cleared',
TRANSFER_TYPE_CHECKPOINT_BALANCE_RESTORE: 'Balances restored',
TRANSFER_TYPE_HOLDING_LOCK: 'TRANSFER_TYPE_HOLDING_LOCK',
TRANSFER_TYPE_HOLDING_RELEASE: 'TRANSFER_TYPE_HOLDING_RELEASE',
TRANSFER_TYPE_SPOT: 'TRANSFER_TYPE_SPOT',
};
export const DescriptionTransferTypeMapping: TransferTypeMap = {
@@ -458,9 +446,6 @@ export const DescriptionTransferTypeMapping: TransferTypeMap = {
TRANSFER_TYPE_CLEAR_ACCOUNT: `Market-related accounts emptied, and balances moved, because the market has closed`,
TRANSFER_TYPE_UNSPECIFIED: 'Default value, always invalid',
TRANSFER_TYPE_CHECKPOINT_BALANCE_RESTORE: `Balances are being restored to the user's account following a checkpoint restart of the network`,
TRANSFER_TYPE_HOLDING_LOCK: '-',
TRANSFER_TYPE_HOLDING_RELEASE: '-',
TRANSFER_TYPE_SPOT: '-',
};
type DispatchMetricLabel = {
@@ -45,7 +45,7 @@ export function Dialog({
'dark:bg-black bg-white dark:text-white',
getIntentBorder(intent),
{
'w-[520px]': size === 'small',
'w-[620px]': size === 'small',
'w-[720px] lg:w-[940px]': size === 'medium',
}
);
@@ -77,7 +77,7 @@ export function Dialog({
className="absolute p-2 top-0 right-0 md:top-2 md:right-2"
data-testid="dialog-close"
>
<VegaIcon name={VegaIconNames.CROSS} size={24} />
<VegaIcon name={VegaIconNames.CROSS} />
</DialogPrimitives.Close>
)}
<div className="flex gap-4 max-w-full">
@@ -74,11 +74,11 @@ export const DropdownMenuContent = forwardRef<
React.ComponentProps<typeof DropdownMenuPrimitive.Content>
>(({ className, ...contentProps }, forwardedRef) => (
<DropdownMenuPrimitive.Content
{...contentProps}
ref={forwardedRef}
sideOffset={10}
className="min-w-[290px] bg-vega-light-100 dark:bg-vega-dark-100 p-2 rounded z-20 text-black dark:text-white border border-vega-light-200 dark:border-vega-dark-200"
align="start"
{...contentProps}
sideOffset={10}
/>
));
@@ -1,7 +0,0 @@
export const IconChevronLeft = ({ size = 16 }: { size: number }) => {
return (
<svg width={size} height={size} viewBox="0 0 16 16">
<path d="M10.38 1.62L11.13 2.38L5.5 8L11.13 13.62L10.38 14.38L4 8L10.38 1.62Z" />
</svg>
);
};
@@ -2,7 +2,6 @@ import { IconArrowDown } from './svg-icons/icon-arrow-down';
import { IconArrowRight } from './svg-icons/icon-arrow-right';
import { IconBreakdown } from './svg-icons/icon-breakdown';
import { IconChevronDown } from './svg-icons/icon-chevron-down';
import { IconChevronLeft } from './svg-icons/icon-chevron-left';
import { IconChevronUp } from './svg-icons/icon-chevron-up';
import { IconCopy } from './svg-icons/icon-copy';
import { IconCross } from './svg-icons/icon-cross';
@@ -27,7 +26,6 @@ export enum VegaIconNames {
ARROW_RIGHT = 'arrow-right',
BREAKDOWN = 'breakdown',
CHEVRON_DOWN = 'chevron-down',
CHEVRON_LEFT = 'chevron-left',
CHEVRON_UP = 'chevron-up',
COPY = 'copy',
CROSS = 'cross',
@@ -55,7 +53,6 @@ export const VegaIconNameMap: Record<
'arrow-down': IconArrowDown,
'arrow-right': IconArrowRight,
'chevron-down': IconChevronDown,
'chevron-left': IconChevronLeft,
'chevron-up': IconChevronUp,
'open-external': IconOpenExternal,
'question-mark': IconQuestionMark,
@@ -1,10 +1,7 @@
import { DocsLinks, ExternalLinks } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { Link } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import type { VegaConnector } from '../connectors';
import { RestConnector } from '../connectors';
export const ConnectDialogTitle = ({ children }: { children: ReactNode }) => {
return (
@@ -21,35 +18,11 @@ export const ConnectDialogContent = ({ children }: { children: ReactNode }) => {
return <div>{children}</div>;
};
export const ConnectDialogFooter = ({
connector,
}: {
connector: VegaConnector | undefined;
}) => {
const wrapperClasses = classNames(
'flex justify-center gap-4',
'px-4 md:px-8 pt-4 md:pt-6',
'border-t border-vega-light-200 dark:border-vega-dark-200',
'text-vega-light-400 dark:text-vega-dark-400'
);
const isHostedWalletSelected = connector instanceof RestConnector;
export const ConnectDialogFooter = ({ children }: { children?: ReactNode }) => {
return (
<footer className={wrapperClasses}>
{isHostedWalletSelected ? (
<p className="text-center">
{t('For demo purposes get a ')}
<Link
href={ExternalLinks.VEGA_WALLET_HOSTED_URL}
target="_blank"
rel="noreferrer"
>
{t('hosted wallet')}
</Link>
{t(', or for the real experience create a wallet in the ')}
<Link href={ExternalLinks.VEGA_WALLET_URL}>
{t('Vega wallet app')}
</Link>
</p>
<footer className="flex justify-center gap-4 px-4 md:px-8 pt-4 md:pt-6 -mx-4 md:-mx-8 border-t border-neutral-500 text-neutral-500 dark:text-neutral-400 mt-6">
{children ? (
children
) : (
<>
<Link href={ExternalLinks.VEGA_WALLET_URL}>
@@ -16,7 +16,6 @@ import {
import type { VegaConnectDialogProps } from '..';
import {
ClientErrors,
InjectedConnector,
JsonRpcConnector,
RestConnector,
ViewConnector,
@@ -25,12 +24,6 @@ import {
import { useEnvironment } from '@vegaprotocol/environment';
import type { ChainIdQuery } from './__generated__/ChainId';
import { ChainIdDocument } from './__generated__/ChainId';
import {
mockBrowserWallet,
clearBrowserWallet,
delayedReject,
delayedResolve,
} from '../test-helpers';
const mockUpdateDialogOpen = jest.fn();
const mockCloseVegaDialog = jest.fn();
@@ -56,12 +49,10 @@ const INITIAL_KEY = 'some-key';
const rest = new RestConnector();
const jsonRpc = new JsonRpcConnector();
const view = new ViewConnector(INITIAL_KEY);
const injected = new InjectedConnector();
const connectors = {
rest,
jsonRpc,
view,
injected,
};
beforeEach(() => {
jest.clearAllMocks();
@@ -114,7 +105,7 @@ describe('VegaConnectDialog', () => {
expect(screen.getByTestId('connector-jsonRpc')).toHaveTextContent(
'Connect Vega wallet'
);
expect(screen.getByTestId('connector-rest')).toHaveTextContent(
expect(screen.getByTestId('connector-hosted')).toHaveTextContent(
'Hosted Fairground wallet'
);
expect(screen.getByTestId('connector-view')).toHaveTextContent(
@@ -122,17 +113,6 @@ describe('VegaConnectDialog', () => {
);
});
it('displays browser wallet option if detected on window object', async () => {
mockBrowserWallet();
render(generateJSX());
const list = await screen.findByTestId('connectors-list');
expect(list.children).toHaveLength(4);
expect(screen.getByTestId('connector-injected')).toHaveTextContent(
'Connect Web wallet'
);
clearBrowserWallet();
});
describe('RestConnector', () => {
it('connects', async () => {
const spy = jest
@@ -249,19 +229,17 @@ describe('VegaConnectDialog', () => {
beforeEach(() => {
spyOnCheckCompat = jest
.spyOn(connectors.jsonRpc, 'checkCompat')
.mockImplementation(() => delayedResolve(true, delay));
.mockImplementation(() => delayedResolve(true));
spyOnGetChainId = jest
.spyOn(connectors.jsonRpc, 'getChainId')
.mockImplementation(() =>
delayedResolve({ chainID: mockChainId }, delay)
);
.mockImplementation(() => delayedResolve({ chainID: mockChainId }));
spyOnConnectWallet = jest
.spyOn(connectors.jsonRpc, 'connectWallet')
.mockImplementation(() => delayedResolve(null, delay));
.mockImplementation(() => delayedResolve(null));
spyOnConnect = jest
.spyOn(connectors.jsonRpc, 'connect')
.mockImplementation(() =>
delayedResolve([{ publicKey: 'pubkey', name: 'test key 1' }], delay)
delayedResolve([{ publicKey: 'pubkey', name: 'test key 1' }])
);
});
@@ -373,6 +351,18 @@ describe('VegaConnectDialog', () => {
expect(screen.getByText('An unknown error occurred')).toBeInTheDocument();
});
function delayedResolve<T>(result: T): Promise<T> {
return new Promise((resolve) => {
setTimeout(() => resolve(result), delay);
});
}
function delayedReject<T>(result: T): Promise<T> {
return new Promise((_, reject) => {
setTimeout(() => reject(result), delay);
});
}
async function selectJsonRpc() {
expect(await screen.findByRole('dialog')).toBeInTheDocument();
fireEvent.click(await screen.findByTestId('connector-jsonRpc'));
@@ -449,109 +439,4 @@ describe('VegaConnectDialog', () => {
});
});
});
describe('InjectedConnector', () => {
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
localStorage.clear();
});
afterEach(() => {
clearBrowserWallet();
});
it('connects', async () => {
const delay = 100;
const vegaWindow = {
getChainId: jest.fn(() =>
delayedResolve({ chainID: mockChainId }, delay)
),
connectWallet: jest.fn(() => delayedResolve(null, delay)),
disconnectWallet: jest.fn(() => delayedResolve(undefined, delay)),
listKeys: jest.fn(() =>
delayedResolve(
{
keys: [{ name: 'test key', publicKey: '0x123' }],
},
100
)
),
};
mockBrowserWallet(vegaWindow);
render(generateJSX());
await selectInjected();
// Chain check
expect(screen.getByText('Verifying chain')).toBeInTheDocument();
expect(vegaWindow.getChainId).toHaveBeenCalled();
await act(async () => {
jest.advanceTimersByTime(delay);
});
// Await user connect
expect(screen.getByText('Connecting...')).toBeInTheDocument();
expect(vegaWindow.connectWallet).toHaveBeenCalled();
await act(async () => {
jest.advanceTimersByTime(delay);
});
// Connect (list keys)
expect(vegaWindow.listKeys).toHaveBeenCalled();
await act(async () => {
jest.advanceTimersByTime(delay);
});
expect(screen.getByText('Successfully connected')).toBeInTheDocument();
await act(async () => {
jest.advanceTimersByTime(CLOSE_DELAY);
});
expect(mockCloseVegaDialog).toHaveBeenCalledWith();
});
it('handles invalid chain', async () => {
const delay = 100;
const invalidChain = 'invalid chain';
const vegaWindow = {
getChainId: jest.fn(() =>
delayedResolve({ chainID: invalidChain }, delay)
),
connectWallet: jest.fn(() => delayedResolve(null, delay)),
disconnectWallet: jest.fn(() => delayedResolve(undefined, delay)),
listKeys: jest.fn(() =>
delayedResolve(
{
keys: [{ name: 'test key', publicKey: '0x123' }],
},
100
)
),
};
mockBrowserWallet(vegaWindow);
render(generateJSX());
await selectInjected();
// Chain check
expect(screen.getByText('Verifying chain')).toBeInTheDocument();
expect(vegaWindow.getChainId).toHaveBeenCalled();
await act(async () => {
jest.advanceTimersByTime(delay);
});
expect(screen.getByText('Wrong network')).toBeInTheDocument();
expect(
screen.getByText(
new RegExp(`set your wallet network in your app to "${mockChainId}"`)
)
).toBeInTheDocument();
});
async function selectInjected() {
expect(await screen.findByRole('dialog')).toBeInTheDocument();
fireEvent.click(await screen.findByTestId('connector-injected'));
}
});
});
+187 -150
View File
@@ -3,50 +3,45 @@ import {
Button,
Dialog,
FormGroup,
Icon,
Input,
VegaIcon,
VegaIconNames,
Link,
Loader,
} from '@vegaprotocol/ui-toolkit';
import { useCallback, useState } from 'react';
import type { WalletClientError } from '@vegaprotocol/wallet-client';
import { t } from '@vegaprotocol/i18n';
import type { VegaConnector } from '../connectors';
import { InjectedConnector } from '../connectors';
import { ViewConnector } from '../connectors';
import { JsonRpcConnector, RestConnector } from '../connectors';
import { RestConnectorForm } from './rest-connector-form';
import { JsonRpcConnectorForm } from './json-rpc-connector-form';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import {
Networks,
useEnvironment,
ExternalLinks,
} from '@vegaprotocol/environment';
import {
ConnectDialogContent,
ConnectDialogFooter,
ConnectDialogTitle,
} from './connect-dialog-elements';
import type { Status as JsonRpcStatus } from '../use-json-rpc-connect';
import type { Status as InjectedStatus } from '../use-injected-connector';
import type { Status } from '../use-json-rpc-connect';
import { useJsonRpcConnect } from '../use-json-rpc-connect';
import { ViewConnectorForm } from './view-connector-form';
import { useChainIdQuery } from './__generated__/ChainId';
import { useVegaWallet } from '../use-vega-wallet';
import { useInjectedConnector } from '../use-injected-connector';
import { InjectedConnectorForm } from './injected-connector-form';
export const CLOSE_DELAY = 1700;
type Connectors = { [key: string]: VegaConnector };
export type WalletType = 'injected' | 'jsonRpc' | 'rest' | 'view';
type WalletType = 'jsonRpc' | 'hosted' | 'view';
export interface VegaConnectDialogProps {
connectors: Connectors;
onChangeOpen?: (open: boolean) => void;
riskMessage?: React.ReactNode;
}
export interface VegaWalletDialogStore {
vegaWalletDialogOpen: boolean;
updateVegaWalletDialog: (open: boolean) => void;
openVegaWalletDialog: () => void;
closeVegaWalletDialog: () => void;
}
export const useVegaWalletDialogStore = create<VegaWalletDialogStore>()(
(set) => ({
vegaWalletDialogOpen: false,
@@ -57,20 +52,32 @@ export const useVegaWalletDialogStore = create<VegaWalletDialogStore>()(
})
);
export interface VegaWalletDialogStore {
vegaWalletDialogOpen: boolean;
updateVegaWalletDialog: (open: boolean) => void;
openVegaWalletDialog: () => void;
closeVegaWalletDialog: () => void;
}
export const VegaConnectDialog = ({
connectors,
onChangeOpen,
riskMessage,
}: VegaConnectDialogProps) => {
const { disconnect, acknowledgeNeeded } = useVegaWallet();
const vegaWalletDialogOpen = useVegaWalletDialogStore(
(store) => store.vegaWalletDialogOpen
);
const updateVegaWalletDialog = useVegaWalletDialogStore(
(store) => (open: boolean) => {
store.updateVegaWalletDialog(open);
onChangeOpen?.(open);
}
);
const closeVegaWalletDialog = useVegaWalletDialogStore((store) => () => {
store.closeVegaWalletDialog();
onChangeOpen?.(false);
});
const { disconnect, acknowledgeNeeded } = useVegaWallet();
const onVegaWalletDialogChange = useCallback(
(open: boolean) => {
updateVegaWalletDialog(open);
@@ -81,9 +88,41 @@ export const VegaConnectDialog = ({
[updateVegaWalletDialog, acknowledgeNeeded, disconnect]
);
// Ensure we have a chain Id so we can compare with wallet chain id.
// This value will already be in the cache, if it failed the app wont render
const { data } = useChainIdQuery();
const { data, error, loading } = useChainIdQuery();
const renderContent = () => {
if (error) {
return (
<ConnectDialogContent>
<ConnectDialogTitle>
{t('Could not retrieve chain id')}
</ConnectDialogTitle>
<ConnectDialogFooter />
</ConnectDialogContent>
);
}
if (loading || !data) {
return (
<ConnectDialogContent>
<ConnectDialogTitle>{t('Fetching chain ID')}</ConnectDialogTitle>
<div className="flex justify-center items-center my-6">
<Loader />
</div>
<ConnectDialogFooter />
</ConnectDialogContent>
);
}
return (
<ConnectDialogContainer
connectors={connectors}
closeDialog={closeVegaWalletDialog}
appChainId={data.statistics.chainId}
riskMessage={riskMessage}
/>
);
};
return (
<Dialog
@@ -91,35 +130,29 @@ export const VegaConnectDialog = ({
size="small"
onChange={onVegaWalletDialogChange}
>
{data && (
<ConnectDialogContainer
connectors={connectors}
appChainId={data.statistics.chainId}
riskMessage={riskMessage}
/>
)}
{renderContent()}
</Dialog>
);
};
const ConnectDialogContainer = ({
connectors,
closeDialog,
appChainId,
riskMessage,
}: {
connectors: Connectors;
closeDialog: () => void;
appChainId: string;
riskMessage?: React.ReactNode;
}) => {
const { VEGA_WALLET_URL, VEGA_ENV, HOSTED_WALLET_URL } = useEnvironment();
const closeDialog = useVegaWalletDialogStore(
(store) => store.closeVegaWalletDialog
);
const [selectedConnector, setSelectedConnector] = useState<VegaConnector>();
const [walletUrl, setWalletUrl] = useState(VEGA_WALLET_URL || '');
const [walletType, setWalletType] = useState<WalletType>();
const reset = useCallback(() => {
setSelectedConnector(undefined);
setWalletType(undefined);
}, []);
const delayedOnConnect = useCallback(() => {
@@ -128,59 +161,52 @@ const ConnectDialogContainer = ({
}, CLOSE_DELAY);
}, [closeDialog]);
const { connect: jsonRpcConnect, ...jsonRpcState } =
useJsonRpcConnect(delayedOnConnect);
const { connect: injectedConnect, ...injectedState } =
useInjectedConnector(delayedOnConnect);
const { connect, ...jsonRpcState } = useJsonRpcConnect(delayedOnConnect);
const handleSelect = (type: WalletType) => {
const connector = connectors[type];
const handleSelect = (type: WalletType, isHosted = false) => {
let connector;
// If type is rest user has selected the hosted wallet option. So here
// we ensure that we are connecting to https://vega-hosted-wallet.on.fleek.co/
// otherwise use walletUrl which defaults to the localhost:1789
connector.url = type === 'rest' ? HOSTED_WALLET_URL : walletUrl;
if (isHosted) {
// If the user has selected hosted wallet ensure that we are connecting to https://vega-hosted-wallet.on.fleek.co/
// otherwise use the default walletUrl or what has been put in the input
connector = connectors['rest'];
connector.url = HOSTED_WALLET_URL || walletUrl;
} else {
connector = connectors[type];
connector.url = walletUrl;
}
if (!connector) {
// we should never get here unless connectors are not configured correctly
throw new Error(`Connector type: ${type} not configured`);
}
setSelectedConnector(connector);
setWalletType(type);
// Immediately connect on selection if jsonRpc is selected, we can't do this
// for rest because we need to show an authentication form
if (connector instanceof JsonRpcConnector) {
jsonRpcConnect(connector, appChainId);
} else if (connector instanceof InjectedConnector) {
injectedConnect(connector, appChainId);
connect(connector, appChainId);
}
};
return (
<>
<ConnectDialogContent>
{selectedConnector !== undefined ? (
<SelectedForm
connector={selectedConnector}
jsonRpcState={jsonRpcState}
injectedState={injectedState}
onConnect={closeDialog}
appChainId={appChainId}
reset={reset}
riskMessage={riskMessage}
/>
) : (
<ConnectorList
walletUrl={walletUrl}
setWalletUrl={setWalletUrl}
onSelect={handleSelect}
isMainnet={VEGA_ENV === Networks.MAINNET}
/>
)}
</ConnectDialogContent>
<ConnectDialogFooter connector={selectedConnector} />
</>
return selectedConnector !== undefined && walletType !== undefined ? (
<SelectedForm
type={walletType}
connector={selectedConnector}
jsonRpcState={jsonRpcState}
onConnect={closeDialog}
appChainId={appChainId}
reset={reset}
riskMessage={riskMessage}
/>
) : (
<ConnectorList
walletUrl={walletUrl}
setWalletUrl={setWalletUrl}
onSelect={handleSelect}
isMainnet={VEGA_ENV === Networks.MAINNET}
/>
);
};
@@ -190,129 +216,136 @@ const ConnectorList = ({
setWalletUrl,
isMainnet,
}: {
onSelect: (type: WalletType) => void;
onSelect: (type: WalletType, isHosted?: boolean) => void;
walletUrl: string;
setWalletUrl: (value: string) => void;
isMainnet: boolean;
}) => {
return (
<>
<ConnectDialogTitle>{t('Connect')}</ConnectDialogTitle>
<CustomUrlInput walletUrl={walletUrl} setWalletUrl={setWalletUrl} />
<ul data-testid="connectors-list" className="mb-6">
<li className="mb-4 last:mb-0">
<ConnectionOption
type="jsonRpc"
text={t('Connect Vega wallet')}
onClick={() => onSelect('jsonRpc')}
/>
</li>
{'vega' in window && (
<ConnectDialogContent>
<ConnectDialogTitle>{t('Connect')}</ConnectDialogTitle>
<CustomUrlInput walletUrl={walletUrl} setWalletUrl={setWalletUrl} />
<ul data-testid="connectors-list" className="mb-6">
<li className="mb-4 last:mb-0">
<ConnectionOption
type="injected"
text={t('Connect Web wallet')}
onClick={() => onSelect('injected')}
type="jsonRpc"
text={t('Connect Vega wallet')}
onClick={() => onSelect('jsonRpc')}
/>
</li>
)}
{!isMainnet && (
{!isMainnet && (
<li className="mb-4 last:mb-0">
<ConnectionOption
type="hosted"
text={t('Hosted Fairground wallet')}
onClick={() => onSelect('hosted', true)}
/>
</li>
)}
<li className="mb-4 last:mb-0">
<div className="my-4 text-center text-vega-dark-400">{t('OR')}</div>
<ConnectionOption
type="rest"
text={t('Hosted Fairground wallet')}
onClick={() => onSelect('rest')}
type="view"
text={t('View as vega user')}
onClick={() => onSelect('view')}
/>
</li>
)}
<li className="mb-4 last:mb-0">
<div className="my-4 text-center">{t('OR')}</div>
<ConnectionOption
type="view"
text={t('View as vega user')}
onClick={() => onSelect('view')}
/>
</li>
</ul>
</ul>
</ConnectDialogContent>
<ConnectDialogFooter />
</>
);
};
const SelectedForm = ({
type,
connector,
appChainId,
jsonRpcState,
injectedState,
reset,
onConnect,
riskMessage,
}: {
type: WalletType;
connector: VegaConnector;
appChainId: string;
jsonRpcState: {
status: JsonRpcStatus;
status: Status;
error: WalletClientError | null;
};
injectedState: {
status: InjectedStatus;
error: Error | null;
};
reset: () => void;
onConnect: () => void;
riskMessage?: React.ReactNode;
}) => {
if (connector instanceof InjectedConnector) {
return (
<InjectedConnectorForm
status={injectedState.status}
error={injectedState.error}
onConnect={onConnect}
appChainId={appChainId}
reset={reset}
riskMessage={riskMessage}
/>
);
}
if (connector instanceof RestConnector) {
return (
<>
<button
onClick={reset}
className="absolute p-2 top-0 left-0 md:top-2 md:left-2"
data-testid="back-button"
>
<VegaIcon name={VegaIconNames.CHEVRON_LEFT} />
</button>
<ConnectDialogTitle>{t('Connect')}</ConnectDialogTitle>
<div className="mb-2">
<RestConnectorForm connector={connector} onConnect={onConnect} />
</div>
<ConnectDialogContent>
<button
onClick={reset}
className="absolute p-2 top-0 left-0 md:top-2 md:left-2"
data-testid="back-button"
>
<Icon name={'chevron-left'} ariaLabel="back" size={4} />
</button>
<ConnectDialogTitle>{t('Connect')}</ConnectDialogTitle>
<div className="mb-2">
<RestConnectorForm connector={connector} onConnect={onConnect} />
</div>
</ConnectDialogContent>
{type === 'hosted' ? (
<ConnectDialogFooter>
<p className="text-center">
{t('For demo purposes get a ')}
<Link
href={ExternalLinks.VEGA_WALLET_HOSTED_URL}
target="_blank"
rel="noreferrer"
>
{t('hosted wallet')}
</Link>
{t(', or for the real experience create a wallet in the ')}
<Link href={ExternalLinks.VEGA_WALLET_URL}>
{t('Vega wallet app')}
</Link>
</p>
</ConnectDialogFooter>
) : (
<ConnectDialogFooter />
)}
</>
);
}
if (connector instanceof JsonRpcConnector) {
return (
<JsonRpcConnectorForm
connector={connector}
status={jsonRpcState.status}
error={jsonRpcState.error}
onConnect={onConnect}
appChainId={appChainId}
reset={reset}
riskMessage={riskMessage}
/>
<ConnectDialogContent>
<JsonRpcConnectorForm
connector={connector}
status={jsonRpcState.status}
error={jsonRpcState.error}
onConnect={onConnect}
appChainId={appChainId}
reset={reset}
riskMessage={riskMessage}
/>
</ConnectDialogContent>
);
}
if (connector instanceof ViewConnector) {
return (
<ViewConnectorForm
connector={connector}
onConnect={onConnect}
reset={reset}
/>
<>
<ConnectDialogContent>
<ViewConnectorForm
connector={connector}
onConnect={onConnect}
reset={reset}
/>
</ConnectDialogContent>
<ConnectDialogFooter />
</>
);
}
@@ -333,12 +366,12 @@ const ConnectionOption = ({
onClick={onClick}
size="lg"
fill={true}
variant={['rest', 'view'].includes(type) ? 'default' : 'primary'}
variant={['hosted', 'view'].includes(type) ? 'default' : 'primary'}
data-testid={`connector-${type}`}
>
<span className="-mx-10 flex text-left justify-between items-center">
<span className="-mx-6 flex text-left justify-between items-center">
{text}
<VegaIcon name={VegaIconNames.ARROW_RIGHT} />
<Icon name="chevron-right" />
</span>
</Button>
);
@@ -354,7 +387,9 @@ const CustomUrlInput = ({
const [urlInputExpanded, setUrlInputExpanded] = useState(false);
return urlInputExpanded ? (
<>
<p className="mb-2">{t('Custom wallet location')}</p>
<p className="mb-2 text-neutral-600 dark:text-neutral-400">
{t('Custom wallet location')}
</p>
<FormGroup
labelFor="wallet-url"
label={t('Custom wallet location')}
@@ -366,10 +401,12 @@ const CustomUrlInput = ({
name="wallet-url"
/>
</FormGroup>
<p className="mb-2">{t('Choose wallet app to connect')}</p>
<p className="mb-2 text-neutral-600 dark:text-neutral-400">
{t('Choose wallet app to connect')}
</p>
</>
) : (
<p className="mb-6">
<p className="mb-6 text-neutral-600 dark:text-neutral-400">
{t(
'Choose wallet app to connect, or to change port or server URL enter a '
)}
@@ -1,153 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { Status } from '../use-injected-connector';
import { ConnectDialogTitle } from './connect-dialog-elements';
import type { ReactNode } from 'react';
import {
Button,
ButtonLink,
Diamond,
Loader,
Tick,
} from '@vegaprotocol/ui-toolkit';
import { setAcknowledged } from '../storage';
import { useVegaWallet } from '../use-vega-wallet';
export const InjectedConnectorForm = ({
status,
onConnect,
riskMessage,
appChainId,
reset,
error,
}: {
// connector: JsonRpcConnector;
appChainId: string;
status: Status;
error: Error | null;
onConnect: () => void;
reset: () => void;
riskMessage?: React.ReactNode;
}) => {
const { disconnect } = useVegaWallet();
if (status === Status.Idle) {
return null;
}
if (status === Status.Error) {
return <Error error={error} appChainId={appChainId} onTryAgain={reset} />;
}
if (status === Status.GettingChainId) {
return (
<>
<ConnectDialogTitle>{t('Verifying chain')}</ConnectDialogTitle>
<Center>
<Loader />
</Center>
</>
);
}
if (status === Status.Connected) {
return (
<>
<ConnectDialogTitle>{t('Successfully connected')}</ConnectDialogTitle>
<Center>
<Tick />
</Center>
</>
);
}
if (status === Status.Connecting) {
return (
<>
<ConnectDialogTitle>{t('Connecting...')}</ConnectDialogTitle>
<Center>
<Diamond />
</Center>
<p className="text-center">
{t(
"Approve the connection from your Vega wallet app. If you have multiple wallets you'll need to choose which to connect with."
)}
</p>
</>
);
}
if (status === Status.AcknowledgeNeeded) {
const setConnection = () => {
setAcknowledged();
onConnect();
};
const handleDisagree = () => {
disconnect();
onConnect(); // this is dialog closing
};
return (
<>
<ConnectDialogTitle>{t('Understand the risk')}</ConnectDialogTitle>
{riskMessage}
<div className="grid grid-cols-2 gap-5">
<div>
<Button onClick={handleDisagree} fill>
{t('Cancel')}
</Button>
</div>
<div>
<Button onClick={setConnection} variant="primary" fill>
{t('I agree')}
</Button>
</div>
</div>
</>
);
}
return null;
};
const Center = ({ children }: { children: ReactNode }) => {
return (
<div className="flex justify-center items-center my-6">{children}</div>
);
};
const Error = ({
error,
appChainId,
onTryAgain,
}: {
error: Error | null;
appChainId: string;
onTryAgain: () => void;
}) => {
let title = t('Something went wrong');
let text: ReactNode | undefined = t('An unknown error occurred');
const tryAgain: ReactNode | null = (
<p className="text-center">
<ButtonLink onClick={onTryAgain}>{t('Try again')}</ButtonLink>
</p>
);
if (error) {
if (error.message === 'Invalid chain') {
title = t('Wrong network');
text = t(
'To complete your wallet connection, set your wallet network in your app to "%s".',
appChainId
);
} else if (error.message === 'window.vega not found') {
title = t('No wallet detected');
text = t('Vega browser extension not installed');
}
}
return (
<>
<ConnectDialogTitle>{title}</ConnectDialogTitle>
<p className="text-center mb-2 first-letter:uppercase">{text}</p>
{tryAgain}
</>
);
};
@@ -1,83 +1,21 @@
import { clearConfig, setConfig } from '../storage';
import type { Transaction, VegaConnector } from './vega-connector';
declare global {
interface Vega {
getChainId: () => Promise<{ chainID: string }>;
connectWallet: () => Promise<null>;
disconnectWallet: () => Promise<void>;
listKeys: () => Promise<{
keys: Array<{ name: string; publicKey: string }>;
}>;
sendTransaction: (params: {
publicKey: string;
transaction: Transaction;
sendingMode: 'TYPE_SYNC';
}) => Promise<{
receivedAt: string;
sentAt: string;
transaction: {
from: {
pubKey: string;
};
inputData: string;
pow: {
tid: string;
nonce: string;
};
signature: {
algo: string;
value: string;
version: number;
};
version: number;
};
transactionHash: string;
}>;
}
interface Window {
vega: Vega;
}
}
import type { VegaConnector } from './vega-connector';
/**
* Dummy injected connector that we may use when browser wallet is implemented
*/
export class InjectedConnector implements VegaConnector {
description = 'Connects using the Vega wallet browser extension';
async getChainId() {
return window.vega.getChainId();
}
connectWallet() {
return window.vega.connectWallet();
}
async connect() {
const res = await window.vega.listKeys();
setConfig({
connector: 'injected',
token: null, // no token required for injected
url: null, // no url for injected
});
return res.keys;
return [{ publicKey: '0x123', name: 'text key' }];
}
disconnect() {
clearConfig();
return window.vega.disconnectWallet();
async disconnect() {
return;
}
async sendTx(pubKey: string, transaction: Transaction) {
const result = await window.vega.sendTransaction({
publicKey: pubKey,
transaction,
sendingMode: 'TYPE_SYNC' as const,
});
return {
transactionHash: result.transactionHash,
receivedAt: result.receivedAt,
sentAt: result.sentAt,
signature: result.transaction.signature.value,
};
// @ts-ignore injected connector is not implemented
sendTx() {
throw new Error('Not implemented');
}
}
+1 -1
View File
@@ -411,7 +411,7 @@ export interface PubKey {
}
export interface VegaConnector {
url?: string | null;
url: string | null;
/** Connect to wallet and return keys */
connect(): Promise<PubKey[] | null>;
+1
View File
@@ -106,6 +106,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
if (!connector.current) {
throw new Error('No connector');
}
return connector.current.sendTx(pubkey, transaction);
}, []);
+1 -1
View File
@@ -2,7 +2,7 @@ import { LocalStorage } from '@vegaprotocol/utils';
interface ConnectorConfig {
token: string | null;
connector: 'injected' | 'rest' | 'jsonRpc' | 'view';
connector: 'rest' | 'jsonRpc' | 'view';
url: string | null;
}
-39
View File
@@ -1,39 +0,0 @@
export function mockBrowserWallet(overrides?: Partial<Vega>) {
const vega: Vega = {
getChainId: jest.fn().mockReturnValue(Promise.resolve({ chainID: '1' })),
connectWallet: jest.fn().mockReturnValue(Promise.resolve(null)),
disconnectWallet: jest.fn().mockReturnValue(Promise.resolve()),
listKeys: jest
.fn()
.mockReturnValue({ keys: [{ name: 'test key', publicKey: '0x123' }] }),
sendTransaction: jest.fn().mockReturnValue({
code: 1,
data: '',
height: '1',
log: '',
success: true,
txHash: '0x123',
}),
...overrides,
};
// @ts-ignore globalThis has no index signature
globalThis.vega = vega;
return vega;
}
export function clearBrowserWallet() {
// @ts-ignore no index signature on globalThis
delete globalThis['vega'];
}
export function delayedResolve<T>(result: T, delay = 0): Promise<T> {
return new Promise((resolve) => {
setTimeout(() => resolve(result), delay);
});
}
export function delayedReject<T>(result: T, delay = 0): Promise<T> {
return new Promise((_, reject) => {
setTimeout(() => reject(result), delay);
});
}
@@ -1,104 +0,0 @@
import { act, renderHook, waitFor } from '@testing-library/react';
import { Status, useInjectedConnector } from './use-injected-connector';
import type { ReactNode } from 'react';
import { VegaWalletProvider } from './provider';
import { InjectedConnector } from './connectors';
import { mockBrowserWallet } from './test-helpers';
import { useEnvironment } from '@vegaprotocol/environment';
import { Networks } from '@vegaprotocol/environment';
jest.mock('@vegaprotocol/environment');
const setup = (callback = jest.fn()) => {
const wrapper = ({ children }: { children: ReactNode }) => (
<VegaWalletProvider>{children}</VegaWalletProvider>
);
return renderHook(() => useInjectedConnector(callback), { wrapper });
};
const injected = new InjectedConnector();
describe('useInjectedConnector', () => {
beforeEach(() => {
// @ts-ignore useEnvironment has been mocked
useEnvironment.mockImplementation(() => ({ VEGA_ENV: Networks.TESTNET }));
});
it('attempts connection', async () => {
const { result } = setup();
expect(typeof result.current.connect).toBe('function');
expect(result.current.status).toBe(Status.Idle);
expect(result.current.error).toBe(null);
});
it('errors if vega not injected', async () => {
const { result } = setup();
await act(async () => {
result.current.connect(injected, '1');
});
expect(result.current.error?.message).toBe('window.vega not found');
expect(result.current.status).toBe(Status.Error);
});
it('errors if chain ids dont match', async () => {
mockBrowserWallet();
const { result } = setup();
await act(async () => {
result.current.connect(injected, '2'); // default mock chainId is '1'
});
expect(result.current.error?.message).toBe('Invalid chain');
expect(result.current.status).toBe(Status.Error);
});
it('errors if connection throws', async () => {
const callback = jest.fn();
mockBrowserWallet({
getChainId: () => Promise.reject('failed'),
});
const { result } = setup(callback);
await act(async () => {
result.current.connect(injected, '1'); // default mock chainId is '1'
});
expect(result.current.status).toBe(Status.Error);
expect(result.current.error?.message).toBe('injected connection failed');
});
it('connects', async () => {
const callback = jest.fn();
const vega = mockBrowserWallet();
const { result } = setup(callback);
act(() => {
result.current.connect(injected, '1'); // default mock chainId is '1'
});
expect(result.current.status).toBe(Status.GettingChainId);
await waitFor(() => {
expect(vega.connectWallet).toHaveBeenCalled();
expect(vega.listKeys).toHaveBeenCalled();
});
expect(result.current.status).toBe(Status.Connected);
expect(callback).toHaveBeenCalled();
});
it('connects when aknowledgement required', async () => {
const callback = jest.fn();
// @ts-ignore useEnvironment has been mocked
useEnvironment.mockImplementation(() => ({ VEGA_ENV: Networks.MAINNET }));
const vega = mockBrowserWallet();
const { result } = setup(callback);
act(() => {
result.current.connect(injected, '1'); // default mock chainId is '1'
});
await waitFor(() => {
expect(vega.listKeys).toHaveBeenCalled();
});
expect(result.current.status).toBe(Status.AcknowledgeNeeded);
expect(callback).not.toHaveBeenCalled();
});
});
-61
View File
@@ -1,61 +0,0 @@
import { useCallback, useState } from 'react';
import type { InjectedConnector } from './connectors';
import { useVegaWallet } from './use-vega-wallet';
export enum Status {
Idle = 'Idle',
GettingChainId = 'GettingChainId',
Connecting = 'Connecting',
Connected = 'Connected',
Error = 'Error',
AcknowledgeNeeded = 'AcknowledgeNeeded',
}
export const useInjectedConnector = (onConnect: () => void) => {
const { connect, acknowledgeNeeded } = useVegaWallet();
const [status, setStatus] = useState(Status.Idle);
const [error, setError] = useState<Error | null>(null);
const attemptConnect = useCallback(
async (connector: InjectedConnector, appChainId: string) => {
try {
if (!('vega' in window)) {
throw new Error('window.vega not found');
}
setStatus(Status.GettingChainId);
const { chainID } = await connector.getChainId();
if (chainID !== appChainId) {
throw new Error('Invalid chain');
}
setStatus(Status.Connecting);
await connector.connectWallet(); // authorize wallet
await connect(connector); // connect with keys
if (acknowledgeNeeded) {
setStatus(Status.AcknowledgeNeeded);
} else {
setStatus(Status.Connected);
onConnect();
}
} catch (err) {
if (err instanceof Error) {
setError(err);
} else {
setError(new Error('injected connection failed'));
}
setStatus(Status.Error);
}
},
[acknowledgeNeeded, connect, onConnect]
);
return {
status,
error,
connect: attemptConnect,
};
};
+1
View File
@@ -11,6 +11,7 @@ export enum Status {
GettingChainId = 'GettingChainId',
Connecting = 'Connecting',
GettingPerms = 'GettingPerms',
ListingKeys = 'ListingKeys',
Connected = 'Connected',
Error = 'Error',
AcknowledgeNeeded = 'AcknowledgeNeeded',