Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d718093313 | ||
|
|
7059d06ddf | ||
|
|
1055d5792d | ||
|
|
12abcfc598 | ||
|
|
6e70576a11 | ||
|
|
e335a51903 | ||
|
|
e3cfacd508 | ||
|
|
c22daa0636 | ||
|
|
bcfb958454 | ||
|
|
cf71af8f9d | ||
|
|
d44392bebf | ||
|
|
bd679957e2 | ||
|
|
e30d48555e | ||
|
|
b4b2416780 | ||
|
|
dc7832ac81 | ||
|
|
91207d31ee | ||
|
|
58efb460a6 | ||
|
|
044f98777b | ||
|
|
00fdc5e81a | ||
|
|
adfad3bafb | ||
|
|
e6ae88905c | ||
|
|
3953ed9953 | ||
|
|
b48ab58e6a | ||
|
|
e44cf1ff53 | ||
|
|
bded1d32ba | ||
|
|
76ddf45f4c | ||
|
|
6a2c9004d8 | ||
|
|
77ee88b43e |
@@ -119,7 +119,7 @@ jobs:
|
||||
run: |
|
||||
echo "Check ipfs-hash"
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matirx.app }}-ipfs-hash
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat /ipfs-hash > ${{ matrix.app }}-ipfs-hash
|
||||
echo "List html directory"
|
||||
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local sh -c 'apk add --update tree; tree /usr/share/nginx/html'
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
.PHONY: latest-release
|
||||
latest-release:
|
||||
gh release list | head -n 1 | awk '{print $1}'
|
||||
gh release list | head -n 1 | awk '{print $1}'
|
||||
|
||||
.PHONY: show-latest-release
|
||||
show-latest-release:
|
||||
gh release view `gh release list | head -n 1 | awk '{print $1}'`
|
||||
gh release view `gh release list | head -n 1 | awk '{print $1}'`
|
||||
|
||||
.PHONY: recalculate-ipfs
|
||||
recalculate-ipfs:
|
||||
echo "ipfs hash inside the image"
|
||||
docker run --rm ${TAG} cat /ipfs-hash
|
||||
echo "recalculating ipfs hash"
|
||||
docker run --rm ${TAG} ipfs add -rw /usr/share/nginx/html
|
||||
echo "ipfs hash inside the image"
|
||||
docker run --rm ${TAG} cat /ipfs-hash
|
||||
echo "recalculating ipfs hash"
|
||||
docker run --rm ${TAG} ipfs add -rw /usr/share/nginx/html
|
||||
|
||||
.PHONY: eject-ipfs-hash
|
||||
unpack:
|
||||
docker create --name=dist ${TAG}
|
||||
docker cp dist:/usr/share/nginx/html dist
|
||||
docker rm dist
|
||||
docker create --name=dist ${TAG}
|
||||
docker cp dist:/usr/share/nginx/html dist
|
||||
docker rm dist
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_URL=https://api.vega.community/graphql
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_BLOCK_EXPLORER=https://be.vega.community/rest/
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { NetworkLoader, useInitializeEnv } from '@vegaprotocol/environment';
|
||||
import {
|
||||
AppFailure,
|
||||
NetworkLoader,
|
||||
NodeGuard,
|
||||
NodeSwitcherDialog,
|
||||
useEnvironment,
|
||||
useInitializeEnv,
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
|
||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
import { router } from './routes/router-config';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
const splashLoading = (
|
||||
<Splash>
|
||||
@@ -12,10 +21,23 @@ const splashLoading = (
|
||||
);
|
||||
|
||||
function App() {
|
||||
const { VEGA_URL } = useEnvironment();
|
||||
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useNodeSwitcherStore(
|
||||
(store) => [store.dialogOpen, store.setDialogOpen]
|
||||
);
|
||||
return (
|
||||
<TendermintWebsocketProvider>
|
||||
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
<NodeGuard
|
||||
skeleton={<div>{t('Loading')}</div>}
|
||||
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||
>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
</NodeGuard>
|
||||
<NodeSwitcherDialog
|
||||
open={nodeSwitcherOpen}
|
||||
setOpen={setNodeSwitcherOpen}
|
||||
/>
|
||||
</NetworkLoader>
|
||||
</TendermintWebsocketProvider>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
useEnvironment,
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { ENV } from '../../config/env';
|
||||
|
||||
export const Footer = () => {
|
||||
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
|
||||
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
|
||||
const setNodeSwitcherOpen = useNodeSwitcherStore(
|
||||
(store) => store.setDialogOpen
|
||||
);
|
||||
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const showFullFeedbackLabel = useMemo(
|
||||
() => ['md', 'lg', 'xl', 'xxl', 'xxxl'].includes(screenSize),
|
||||
@@ -15,46 +21,40 @@ export const Footer = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||
<div className="flex justify-between gap-2 align-middle">
|
||||
{GIT_COMMIT_HASH && (
|
||||
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||
<p data-testid="git-commit-hash">
|
||||
{t('Version')}:{' '}
|
||||
<Link
|
||||
href={
|
||||
GIT_ORIGIN_URL
|
||||
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
|
||||
: undefined
|
||||
}
|
||||
target={GIT_ORIGIN_URL ? '_blank' : undefined}
|
||||
>
|
||||
{GIT_COMMIT_HASH}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
|
||||
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
|
||||
{t('Change')}
|
||||
</Link>
|
||||
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||
<div className="flex justify-between gap-2 align-middle">
|
||||
{GIT_COMMIT_HASH && (
|
||||
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||
<p data-testid="git-commit-hash">
|
||||
{t('Version')}:{' '}
|
||||
<Link
|
||||
href={
|
||||
GIT_ORIGIN_URL
|
||||
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
|
||||
: undefined
|
||||
}
|
||||
target={GIT_ORIGIN_URL ? '_blank' : undefined}
|
||||
>
|
||||
{GIT_COMMIT_HASH}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex pl-2 content-center">
|
||||
<ExternalLink href={ENV.addresses.feedback}>
|
||||
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
|
||||
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
|
||||
{t('Change')}
|
||||
</Link>
|
||||
</div>
|
||||
</footer>
|
||||
<NodeSwitcherDialog
|
||||
open={nodeSwitcherOpen}
|
||||
setOpen={setNodeSwitcherOpen}
|
||||
/>
|
||||
</>
|
||||
|
||||
<div className="flex pl-2 content-center">
|
||||
<ExternalLink href={ENV.addresses.feedback}>
|
||||
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
waitForSpinner,
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
clickOnValidatorFromList,
|
||||
@@ -56,6 +57,7 @@ context(
|
||||
// 2001-STKE-002, 2001-STKE-032
|
||||
before('visit staking tab and connect vega wallet', function () {
|
||||
cy.visit('/');
|
||||
cy.validatorsSelfDelegate();
|
||||
ethereumWalletConnect();
|
||||
// this is a workaround for #2422 which can be removed once issue is resolved
|
||||
cy.associateTokensToVegaWallet('4');
|
||||
@@ -67,6 +69,7 @@ context(
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -252,10 +255,10 @@ context(
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId(stakeValidatorListStakePercentage).should(
|
||||
'have.text',
|
||||
'100%'
|
||||
'50.02%'
|
||||
);
|
||||
navigateTo(navigation.validators);
|
||||
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
|
||||
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -29,7 +29,10 @@ const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
|
||||
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
|
||||
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
|
||||
const epochCountDown = '[data-testid="epoch-countdown"]';
|
||||
const stakeNumberRegex = /^\d*\.?\d*$/;
|
||||
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
|
||||
|
||||
// If running locally, validators need to have self-stake to be displayed
|
||||
// Run cy.validatorsSelfDelegate() in before hook
|
||||
|
||||
context('Validators Page - verify elements on page', function () {
|
||||
before('navigate to validators page', function () {
|
||||
@@ -84,13 +87,13 @@ context('Validators Page - verify elements on page', function () {
|
||||
|
||||
cy.get(stakedByOperatorToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by operator: 0.00');
|
||||
.should('contain', 'Staked by operator: 3,000.00');
|
||||
cy.get(stakedByDelegatesToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by delegates: 0.00');
|
||||
cy.get(totalStakedToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total stake: 0.00');
|
||||
.should('contain', 'Total stake: 3,000.00');
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power', function () {
|
||||
@@ -106,10 +109,10 @@ context('Validators Page - verify elements on page', function () {
|
||||
|
||||
cy.get(unnormalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Unnormalised voting power: 0.00%');
|
||||
.should('contain', 'Unnormalised voting power: 20.00%');
|
||||
cy.get(normalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Normalised voting power: 0.10%');
|
||||
.should('contain', 'Normalised voting power: 50.00%');
|
||||
});
|
||||
|
||||
// 2002-SINC-018
|
||||
@@ -126,13 +129,13 @@ context('Validators Page - verify elements on page', function () {
|
||||
|
||||
cy.get(performancePenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Performance penalty: 100.00%');
|
||||
.should('contain', 'Performance penalty: 0.00%');
|
||||
cy.get(overstakedPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
|
||||
.should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
|
||||
cy.get(totalPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total penalties: 0.00%');
|
||||
.should('contain', 'Total penalties: 60.00%');
|
||||
});
|
||||
|
||||
it('Should be able to see validator pending stake', function () {
|
||||
|
||||
+37
-14
@@ -37,6 +37,10 @@ import {
|
||||
useEnvironment,
|
||||
NetworkLoader,
|
||||
useInitializeEnv,
|
||||
NodeGuard,
|
||||
AppFailure,
|
||||
NodeSwitcherDialog,
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
@@ -48,6 +52,7 @@ import {
|
||||
TELEMETRY_ON,
|
||||
} from './components/telemetry-dialog/telemetry-dialog';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const cache: InMemoryCacheConfig = {
|
||||
typePolicies: {
|
||||
@@ -181,9 +186,19 @@ const ScrollToTop = () => {
|
||||
|
||||
const AppContainer = () => {
|
||||
const { config, loading, error } = useEthereumConfig();
|
||||
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH, ETHEREUM_PROVIDER_URL } =
|
||||
useEnvironment();
|
||||
const {
|
||||
VEGA_ENV,
|
||||
VEGA_URL,
|
||||
GIT_COMMIT_HASH,
|
||||
GIT_BRANCH,
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
} = useEnvironment();
|
||||
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||
const { t } = useTranslation();
|
||||
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
||||
store.dialogOpen,
|
||||
store.setDialogOpen,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ENV.dsn && telemetryOn) {
|
||||
@@ -219,21 +234,29 @@ const AppContainer = () => {
|
||||
<ScrollToTop />
|
||||
<AppStateProvider>
|
||||
<div className="grid min-h-full text-white">
|
||||
<AsyncRenderer<EthereumConfig | null>
|
||||
loading={loading}
|
||||
data={config}
|
||||
error={error}
|
||||
render={(cnf) =>
|
||||
cnf && (
|
||||
<Web3Container
|
||||
chainId={Number(cnf.chain_id)}
|
||||
providerUrl={ETHEREUM_PROVIDER_URL}
|
||||
/>
|
||||
)
|
||||
<NodeGuard
|
||||
skeleton={<div>{t('Loading')}</div>}
|
||||
failure={
|
||||
<AppFailure title={t('NodeUnsuitable', { url: VEGA_URL })} />
|
||||
}
|
||||
/>
|
||||
>
|
||||
<AsyncRenderer<EthereumConfig | null>
|
||||
loading={loading}
|
||||
data={config}
|
||||
error={error}
|
||||
render={(cnf) =>
|
||||
cnf && (
|
||||
<Web3Container
|
||||
chainId={Number(cnf.chain_id)}
|
||||
providerUrl={ETHEREUM_PROVIDER_URL}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</NodeGuard>
|
||||
</div>
|
||||
</AppStateProvider>
|
||||
<NodeSwitcherDialog open={nodeSwitcherOpen} setOpen={setNodeSwitcher} />
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -811,5 +811,6 @@
|
||||
"OptOutOfTelemetry": "You can opt out any time via settings",
|
||||
"NoThanks": "No thanks",
|
||||
"ShareData": "Share data",
|
||||
"ContinueSharingData": "Continue sharing data"
|
||||
"ContinueSharingData": "Continue sharing data",
|
||||
"NodeUnsuitable": "Node: {{url}} is unsuitable"
|
||||
}
|
||||
|
||||
+1
-1
@@ -216,7 +216,7 @@ export const ConsensusValidatorsTable = ({
|
||||
: undefined,
|
||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare))
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare), 2)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
"tranche_start": "2023-04-20T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-20T00:00:00.000Z",
|
||||
"total_added": "19242.125",
|
||||
"total_removed": "1523.8177488329475",
|
||||
"locked_amount": "6891.9471601080244395475",
|
||||
"total_removed": "1979.64045368475",
|
||||
"locked_amount": "5608.9383879726076446",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "188",
|
||||
@@ -228,6 +228,16 @@
|
||||
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
|
||||
"tx": "0x2b3571c143ecebddf91fb62f402d516d51110edfe37b13200a6e5cf682dc5bb0"
|
||||
},
|
||||
{
|
||||
"amount": "336.4580555509875",
|
||||
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
|
||||
"tx": "0x7af5942634e236f5f9c580f4ed042794ed309e83885f652a55ab37793ea2e85c"
|
||||
},
|
||||
{
|
||||
"amount": "119.364649300815",
|
||||
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
|
||||
"tx": "0x47f5bf2c758c5270dd1b6519ac649ddabef8199f8a0bae319e36f8ac5c9c142e"
|
||||
},
|
||||
{
|
||||
"amount": "202.093666077975",
|
||||
"user": "0x1447Efc62d5077d7AbB20492dF831Dd6c9Eb1756",
|
||||
@@ -362,6 +372,12 @@
|
||||
"tranche_id": 56,
|
||||
"tx": "0x2b3571c143ecebddf91fb62f402d516d51110edfe37b13200a6e5cf682dc5bb0"
|
||||
},
|
||||
{
|
||||
"amount": "119.364649300815",
|
||||
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
|
||||
"tranche_id": 56,
|
||||
"tx": "0x47f5bf2c758c5270dd1b6519ac649ddabef8199f8a0bae319e36f8ac5c9c142e"
|
||||
},
|
||||
{
|
||||
"amount": "195.89040769089",
|
||||
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
|
||||
@@ -370,8 +386,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "914.25",
|
||||
"withdrawn_tokens": "504.71220630471",
|
||||
"remaining_tokens": "409.53779369529"
|
||||
"withdrawn_tokens": "624.076855605525",
|
||||
"remaining_tokens": "290.173144394475"
|
||||
},
|
||||
{
|
||||
"address": "0x9573BDF7FfC5519912d293e4D1f750eab2E471E7",
|
||||
@@ -705,6 +721,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "336.4580555509875",
|
||||
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
|
||||
"tranche_id": 56,
|
||||
"tx": "0x7af5942634e236f5f9c580f4ed042794ed309e83885f652a55ab37793ea2e85c"
|
||||
},
|
||||
{
|
||||
"amount": "422.1034736680125",
|
||||
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
|
||||
@@ -713,8 +735,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "1121.25",
|
||||
"withdrawn_tokens": "422.1034736680125",
|
||||
"remaining_tokens": "699.1465263319875"
|
||||
"withdrawn_tokens": "758.561529219",
|
||||
"remaining_tokens": "362.688470781"
|
||||
},
|
||||
{
|
||||
"address": "0x237D23FcA6d7B2530C7614a9cB921CF27924911E",
|
||||
@@ -877,7 +899,7 @@
|
||||
"tranche_start": "2023-04-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-06T00:00:00.000Z",
|
||||
"total_added": "14610",
|
||||
"total_removed": "6141.45090157707",
|
||||
"total_removed": "6675.45090157707",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -1077,6 +1099,11 @@
|
||||
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
|
||||
"tx": "0x1dbcf713b48965a82aa2e17cb3e7db9a491668d859d408a39c8a74b0ea860b6b"
|
||||
},
|
||||
{
|
||||
"amount": "534",
|
||||
"user": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
|
||||
"tx": "0x5aa922056cad64f97a7dfa750da57d63abfdaea4499192d4d57958bfb4fba2ea"
|
||||
},
|
||||
{
|
||||
"amount": "106.53500000286",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
@@ -1200,10 +1227,17 @@
|
||||
"tx": "0xf970ea0ce3e36fa0014d24bb830dd2ea0dbea06f6e53af486de5ee7e1c63e540"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "534",
|
||||
"user": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
|
||||
"tranche_id": 54,
|
||||
"tx": "0x5aa922056cad64f97a7dfa750da57d63abfdaea4499192d4d57958bfb4fba2ea"
|
||||
}
|
||||
],
|
||||
"total_tokens": "534",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "534"
|
||||
"withdrawn_tokens": "534",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xBf1AaB792D729fA125e6D7122D4b916a1E1C44B1",
|
||||
@@ -1826,7 +1860,7 @@
|
||||
"tranche_start": "2023-03-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-06T00:00:00.000Z",
|
||||
"total_added": "14099",
|
||||
"total_removed": "3722.49002352036",
|
||||
"total_removed": "3785.49002352036",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -2591,6 +2625,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "63",
|
||||
"user": "0x2a65Ae527C6Ff4665e048B0E0883c486A7BA4DBc",
|
||||
"tx": "0xb0edcc25e422bc3db3ad8027dfdfc0928abd7a8af8957a1699e4cf2dc8cee8f7"
|
||||
},
|
||||
{
|
||||
"amount": "30",
|
||||
"user": "0xBe9F912Ad481C61B653463E8F1D2b2b310D49861",
|
||||
@@ -3629,10 +3668,17 @@
|
||||
"tx": "0xd4a269b070cbaaff7e29a99f6b3997117d765f69512bb760e28bade799fcbba4"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "63",
|
||||
"user": "0x2a65Ae527C6Ff4665e048B0E0883c486A7BA4DBc",
|
||||
"tranche_id": 53,
|
||||
"tx": "0xb0edcc25e422bc3db3ad8027dfdfc0928abd7a8af8957a1699e4cf2dc8cee8f7"
|
||||
}
|
||||
],
|
||||
"total_tokens": "63",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "63"
|
||||
"withdrawn_tokens": "63",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xc3B1eB0feE837Db0A3ded5bf16A050726955195B",
|
||||
@@ -4861,7 +4907,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "49802.2753121154213651374",
|
||||
"locked_amount": "49327.3172923405910546417",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -4927,7 +4973,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "312.4325905575906",
|
||||
"locked_amount": "284.955770502645375",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -4960,7 +5006,7 @@
|
||||
"tranche_end": "2023-11-01T00:00:00.000Z",
|
||||
"total_added": "15000.000000000000015",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "14327.0455917874395143270455917874395",
|
||||
"locked_amount": "14163.9766379830905141639766379830905",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1.5e-14",
|
||||
@@ -5048,7 +5094,7 @@
|
||||
"tranche_end": "2023-09-01T00:00:00.000Z",
|
||||
"total_added": "17500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "10913.25608896940375",
|
||||
"locked_amount": "10723.00897619766325",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -5314,8 +5360,8 @@
|
||||
"tranche_start": "2023-02-01T00:00:00.000Z",
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "18077.0118744",
|
||||
"locked_amount": "17350.5026089625535",
|
||||
"total_removed": "18302.01762945",
|
||||
"locked_amount": "16936.07322360343725",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -5329,6 +5375,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "225.00575505",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x0a46fa3cc0154727ae4c7abca97bd7e26e6835018f96cf3a3860a9fcc791b9a4"
|
||||
},
|
||||
{
|
||||
"amount": "164.727209925",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -5507,6 +5558,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "225.00575505",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 34,
|
||||
"tx": "0x0a46fa3cc0154727ae4c7abca97bd7e26e6835018f96cf3a3860a9fcc791b9a4"
|
||||
},
|
||||
{
|
||||
"amount": "164.727209925",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -5689,8 +5746,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "3867.2532036",
|
||||
"remaining_tokens": "3632.7467964"
|
||||
"withdrawn_tokens": "4092.25895865",
|
||||
"remaining_tokens": "3407.74104135"
|
||||
},
|
||||
{
|
||||
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
|
||||
@@ -5734,7 +5791,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "49756.837002255673748325",
|
||||
"locked_amount": "49282.312321912380332265",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -5767,7 +5824,7 @@
|
||||
"tranche_end": "2024-04-01T00:00:00.000Z",
|
||||
"total_added": "54144.7663",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "48485.46842731635829207292",
|
||||
"locked_amount": "48189.54915726316779685587",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "54144.7663",
|
||||
@@ -5800,7 +5857,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "20022.5830035514954",
|
||||
"locked_amount": "19679.51570903094634",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -5993,7 +6050,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1791.028665651953",
|
||||
"locked_amount": "1763.627124556063",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -7062,7 +7119,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "1709370.7872515768348",
|
||||
"locked_amount": "126202.1342632826843379468",
|
||||
"locked_amount": "115570.728817751808474185",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -40934,7 +40991,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "715655.108029600523393",
|
||||
"locked_amount": "218430.066175628697078272948",
|
||||
"locked_amount": "202093.2974680882797136777084",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -42326,8 +42383,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "871680.07831804700259352",
|
||||
"locked_amount": "6074226.5757489411253544394273819693295485",
|
||||
"total_removed": "873375.18460711694221852",
|
||||
"locked_amount": "6016297.4428328087189762607645230040375777",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -42836,6 +42893,16 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13"
|
||||
},
|
||||
{
|
||||
"amount": "955.820117175268125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x4301fcb38d16c1f6aa4a93a18a277a4e77eb3b4565bf98ca6218f6598b78b90b"
|
||||
},
|
||||
{
|
||||
"amount": "739.2861718946715",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x989d22d5ba66353cb132b5643100a99e78133e5d40aef1ac91ed6ab0a2290299"
|
||||
},
|
||||
{
|
||||
"amount": "10150.87581603206683",
|
||||
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
|
||||
@@ -45010,6 +45077,18 @@
|
||||
"tranche_id": 2,
|
||||
"tx": "0x625e0c8fb8d5e2dd7fef0359bb396571f7d8aed05a601ddf0f6e6577edb26a13"
|
||||
},
|
||||
{
|
||||
"amount": "955.820117175268125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x4301fcb38d16c1f6aa4a93a18a277a4e77eb3b4565bf98ca6218f6598b78b90b"
|
||||
},
|
||||
{
|
||||
"amount": "739.2861718946715",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x989d22d5ba66353cb132b5643100a99e78133e5d40aef1ac91ed6ab0a2290299"
|
||||
},
|
||||
{
|
||||
"amount": "913.910324501590625",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -46566,8 +46645,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "259998.8875",
|
||||
"withdrawn_tokens": "159590.522104386423875",
|
||||
"remaining_tokens": "100408.365395613576125"
|
||||
"withdrawn_tokens": "161285.6283934563635",
|
||||
"remaining_tokens": "98713.2591065436365"
|
||||
},
|
||||
{
|
||||
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
|
||||
@@ -48948,7 +49027,7 @@
|
||||
"tranche_start": "2021-11-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-05T00:00:00.000Z",
|
||||
"total_added": "14597706.0446472999",
|
||||
"total_removed": "6170228.182342542771392682",
|
||||
"total_removed": "6246751.954791069035514289",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -49158,6 +49237,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "76523.772448526264121607",
|
||||
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
|
||||
"tx": "0x5c4a7a8fbab9c7c5899835a06397ea9f9dfbc434376dd0515482b8ba83944d67"
|
||||
},
|
||||
{
|
||||
"amount": "1257.7360413991436375",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -56357,6 +56441,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "76523.772448526264121607",
|
||||
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x5c4a7a8fbab9c7c5899835a06397ea9f9dfbc434376dd0515482b8ba83944d67"
|
||||
},
|
||||
{
|
||||
"amount": "466184.354416277260082874",
|
||||
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
|
||||
@@ -56371,8 +56461,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "575536.79731735",
|
||||
"withdrawn_tokens": "499013.024868823735878393",
|
||||
"remaining_tokens": "76523.772448526264121607"
|
||||
"withdrawn_tokens": "575536.79731735",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0xaAeD573103e3f981867C4cd31c6674DE1c8a13c3",
|
||||
@@ -56864,7 +56954,7 @@
|
||||
"tranche_start": "2021-10-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "5778205.3912159303",
|
||||
"total_removed": "3480901.398845846498418769",
|
||||
"total_removed": "3492469.400132244009296769",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -57009,6 +57099,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "11568.001286397510878",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tx": "0xd4191eb657ad26e08faddde326514247ae4a88e8aefcc7592590f368010c4046"
|
||||
},
|
||||
{
|
||||
"amount": "58898.87608135458795",
|
||||
"user": "0x83b1a48376E045D26420200345414e6b93066396",
|
||||
@@ -57979,6 +58074,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "11568.001286397510878",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tranche_id": 4,
|
||||
"tx": "0xd4191eb657ad26e08faddde326514247ae4a88e8aefcc7592590f368010c4046"
|
||||
},
|
||||
{
|
||||
"amount": "209431.056913602489122",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
@@ -57987,8 +58088,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "220999.0582",
|
||||
"withdrawn_tokens": "209431.056913602489122",
|
||||
"remaining_tokens": "11568.001286397510878"
|
||||
"withdrawn_tokens": "220999.0582",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x9058e12e2F32cB1cD4D3123359963D77786477FC",
|
||||
@@ -59069,8 +59170,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "44479.0535455583416",
|
||||
"locked_amount": "34611.49258501772999518148756976",
|
||||
"total_removed": "44544.1737890903416",
|
||||
"locked_amount": "32022.838198356764871382775241",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@@ -65694,6 +65795,11 @@
|
||||
"user": "0x0436E2E4EA467f9958403dc56635404EE1bb1E94",
|
||||
"tx": "0x97f5b5ec5ac839a8d182b9267b5d052cb70f9a18b648874337e3d5360b21a5fb"
|
||||
},
|
||||
{
|
||||
"amount": "65.120243532",
|
||||
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
|
||||
"tx": "0x4697229c7f272bd3fc06ecdc1b15143d28e20083e15f1b3cadb3fc80e80af180"
|
||||
},
|
||||
{
|
||||
"amount": "182.662252664",
|
||||
"user": "0xb1169C6daAc76bAcaf0D8f87641Fc38fbabe569F",
|
||||
@@ -86287,6 +86393,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "65.120243532",
|
||||
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x4697229c7f272bd3fc06ecdc1b15143d28e20083e15f1b3cadb3fc80e80af180"
|
||||
},
|
||||
{
|
||||
"amount": "120.24984145",
|
||||
"user": "0x3F9E884B459a6AaC3f66b72555611AdE68a7F472",
|
||||
@@ -86295,8 +86407,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "200",
|
||||
"withdrawn_tokens": "120.24984145",
|
||||
"remaining_tokens": "79.75015855"
|
||||
"withdrawn_tokens": "185.370084982",
|
||||
"remaining_tokens": "14.629915018"
|
||||
},
|
||||
{
|
||||
"address": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46",
|
||||
@@ -144401,6 +144513,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tx": "0x7b20f640f6947ca553bb2ffb572e316f155781b051b198911b234c6c40e79eaa"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
@@ -144696,6 +144813,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
"tranche_id": 9,
|
||||
"tx": "0x7b20f640f6947ca553bb2ffb572e316f155781b051b198911b234c6c40e79eaa"
|
||||
},
|
||||
{
|
||||
"amount": "0",
|
||||
"user": "0x759C9ABABA492500c4c730bEB568B5b851Dec2c7",
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
createMarketsDataFragment,
|
||||
assetQuery,
|
||||
networkParamsQuery,
|
||||
nodeGuardQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
@@ -158,6 +159,7 @@ describe('Closed markets', { tags: '@smoke' }, () => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
|
||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
|
||||
@@ -6,6 +6,7 @@ const row = 'key-value-table-row';
|
||||
const marketTitle = 'accordion-title';
|
||||
const externalLink = 'external-link';
|
||||
const accordionContent = 'accordion-content';
|
||||
const providerName = 'provider-name';
|
||||
|
||||
describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
@@ -181,9 +182,20 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId(marketTitle).contains('Oracle').click();
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('provider-name')
|
||||
.getByTestId(providerName)
|
||||
.and('contain', 'Another oracle');
|
||||
|
||||
cy.getByTestId(providerName).should('be.visible').click();
|
||||
|
||||
cy.getByTestId('dialog-content')
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.getByTestId('block-explorer-link').contains('Block explorer');
|
||||
cy.getByTestId('github-link').contains('Oracle repository');
|
||||
cy.getByTestId('verified-accounts').contains('0 proofs of ownership');
|
||||
});
|
||||
cy.getByTestId('dialog-close').click();
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('verified-proofs')
|
||||
.and('contain', '1');
|
||||
|
||||
@@ -16,6 +16,10 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
});
|
||||
|
||||
describe('limit order', () => {
|
||||
before(() => {
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
@@ -98,7 +102,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
'have.text',
|
||||
'Total margin available'
|
||||
);
|
||||
cy.get('.text-neutral-500').should('have.text', '~100,000.01 tDAI');
|
||||
cy.get('.text-neutral-500').should('have.text', '100,000.01 tDAI');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import {
|
||||
accountsQuery,
|
||||
amendGeneralAccountBalance,
|
||||
estimateOrderQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import { accountsQuery, amendGeneralAccountBalance } from '@vegaprotocol/mock';
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { createOrder } from '../support/create-order';
|
||||
|
||||
@@ -44,13 +40,10 @@ describe(
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
const accounts = accountsQuery();
|
||||
amendGeneralAccountBalance(accounts, 'market-0', '100000000');
|
||||
amendGeneralAccountBalance(accounts, 'market-0', '1');
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Accounts', accounts);
|
||||
});
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'EstimateOrder', estimateOrderQuery());
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
@@ -66,7 +59,7 @@ describe(
|
||||
);
|
||||
cy.getByTestId('dealticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'You may not have enough margin available to open this position. 2,354.72283 tDAI is currently required. You have only 1,000.01 tDAI available.'
|
||||
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
|
||||
cy.getByTestId('dialog-content')
|
||||
|
||||
@@ -17,7 +17,6 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
it('can connect', () => {
|
||||
// 0004-EWAL-001
|
||||
|
||||
cy.wait('@NetworkParams');
|
||||
cy.getByTestId('Deposits').click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
cy.getByTestId('connect-eth-wallet-btn').click();
|
||||
@@ -30,7 +29,6 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
it('should see QR code modal for WalletConnect', () => {
|
||||
// 0004-EWAL-003
|
||||
|
||||
cy.wait('@NetworkParams');
|
||||
cy.getByTestId('Deposits').click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
cy.getByTestId('connect-eth-wallet-btn').click();
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
chainIdQuery,
|
||||
chartQuery,
|
||||
depositsQuery,
|
||||
estimateOrderQuery,
|
||||
estimateFeesQuery,
|
||||
marginsQuery,
|
||||
marketCandlesQuery,
|
||||
marketDataQuery,
|
||||
@@ -20,12 +20,16 @@ import {
|
||||
marketsDataQuery,
|
||||
marketsQuery,
|
||||
networkParamsQuery,
|
||||
nodeGuardQuery,
|
||||
ordersQuery,
|
||||
estimatePositionQuery,
|
||||
positionsQuery,
|
||||
proposalListQuery,
|
||||
statisticsQuery,
|
||||
tradesQuery,
|
||||
withdrawalsQuery,
|
||||
protocolUpgradeProposalsQuery,
|
||||
blockStatisticsQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/market-list';
|
||||
@@ -82,6 +86,7 @@ const mockTradingPage = (
|
||||
) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'Markets',
|
||||
@@ -155,9 +160,16 @@ const mockTradingPage = (
|
||||
aliasGQLQuery(req, 'Candles', candlesQuery());
|
||||
aliasGQLQuery(req, 'Withdrawals', withdrawalsQuery());
|
||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
||||
aliasGQLQuery(req, 'EstimateOrder', estimateOrderQuery());
|
||||
aliasGQLQuery(req, 'EstimateFees', estimateFeesQuery());
|
||||
aliasGQLQuery(req, 'EstimatePosition', estimatePositionQuery());
|
||||
aliasGQLQuery(req, 'ProposalsList', proposalListQuery());
|
||||
aliasGQLQuery(req, 'Deposits', depositsQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'ProtocolUpgradeProposals',
|
||||
protocolUpgradeProposalsQuery()
|
||||
);
|
||||
aliasGQLQuery(req, 'BlockStatistics', blockStatisticsQuery());
|
||||
};
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import {
|
||||
AppFailure,
|
||||
NetworkLoader,
|
||||
NodeGuard,
|
||||
useEnvironment,
|
||||
@@ -9,7 +10,6 @@ import { MaintenancePage } from '@vegaprotocol/ui-toolkit';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import dynamic from 'next/dynamic';
|
||||
import type { ReactNode } from 'react';
|
||||
import { AppFailure } from './app-failure';
|
||||
import { Web3Provider } from './web3-provider';
|
||||
|
||||
export const DynamicLoader = dynamic(() => import('../preloader/preloader'), {
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
export * from './app-failure';
|
||||
export * from './app-loader';
|
||||
export * from './web3-provider';
|
||||
|
||||
@@ -4,18 +4,15 @@ import { NodeHealth, NodeUrl, HealthIndicator } from './footer';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
const mockSetNodeSwitcher = jest.fn();
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
useEnvironment: jest.fn().mockImplementation(() => ({
|
||||
VEGA_URL: 'https://vega-url.wtf',
|
||||
VEGA_INCIDENT_URL: 'https://blog.vega.community',
|
||||
})),
|
||||
}));
|
||||
|
||||
const mockSetNodeSwitcher = jest.fn();
|
||||
jest.mock('../../stores', () => ({
|
||||
...jest.requireActual('../../stores'),
|
||||
useGlobalStore: () => mockSetNodeSwitcher,
|
||||
useNodeSwitcherStore: jest.fn(() => mockSetNodeSwitcher),
|
||||
}));
|
||||
|
||||
describe('NodeHealth', () => {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useEnvironment, useNodeHealth } from '@vegaprotocol/environment';
|
||||
import {
|
||||
useEnvironment,
|
||||
useNodeHealth,
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { Indicator, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
import type { ButtonHTMLAttributes, ReactNode } from 'react';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export const Footer = () => {
|
||||
return (
|
||||
@@ -20,9 +23,7 @@ export const Footer = () => {
|
||||
|
||||
export const NodeHealth = () => {
|
||||
const { VEGA_URL, VEGA_INCIDENT_URL } = useEnvironment();
|
||||
const setNodeSwitcher = useGlobalStore(
|
||||
(store) => (open: boolean) => store.update({ nodeSwitcherDialog: open })
|
||||
);
|
||||
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
|
||||
const { datanodeBlockHeight, text, intent } = useNodeHealth();
|
||||
const onClick = useCallback(() => {
|
||||
setNodeSwitcher(true);
|
||||
|
||||
@@ -21,9 +21,10 @@ import {
|
||||
NodeSwitcherDialog,
|
||||
useEnvironment,
|
||||
useInitializeEnv,
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
import './styles.css';
|
||||
import { useGlobalStore, usePageTitleStore } from '../stores';
|
||||
import { usePageTitleStore } from '../stores';
|
||||
import { Footer } from '../components/footer';
|
||||
import DialogsContainer from './dialogs-container';
|
||||
import ToastsManager from './toasts-manager';
|
||||
@@ -115,11 +116,10 @@ function AppBody({ Component }: AppProps) {
|
||||
|
||||
function VegaTradingApp(props: AppProps) {
|
||||
const status = useEnvironment((store) => store.status);
|
||||
const { nodeSwitcherOpen, setNodeSwitcher } = useGlobalStore((store) => ({
|
||||
nodeSwitcherOpen: store.nodeSwitcherDialog,
|
||||
setNodeSwitcher: (open: boolean) =>
|
||||
store.update({ nodeSwitcherDialog: open }),
|
||||
}));
|
||||
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
||||
store.dialogOpen,
|
||||
store.setDialogOpen,
|
||||
]);
|
||||
|
||||
useInitializeEnv();
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { create } from 'zustand';
|
||||
import produce from 'immer';
|
||||
|
||||
interface GlobalStore {
|
||||
nodeSwitcherDialog: boolean;
|
||||
marketId: string | null;
|
||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||
shouldDisplayWelcomeDialog: boolean;
|
||||
@@ -15,7 +14,6 @@ interface PageTitleStore {
|
||||
}
|
||||
|
||||
export const useGlobalStore = create<GlobalStore>()((set) => ({
|
||||
nodeSwitcherDialog: false,
|
||||
marketId: LocalStorage.getItem('marketId') || null,
|
||||
shouldDisplayWelcomeDialog: false,
|
||||
update: (newState) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash -e
|
||||
yarn --pure-lockfile
|
||||
app={$1:-trading}
|
||||
flags="--env=${$2:-mainnet}"
|
||||
app=${1:-trading}
|
||||
flags="--env=${2:-mainnet}"
|
||||
yarn install
|
||||
if [ "${app}" = "trading" ]; then
|
||||
yarn nx export trading $flags
|
||||
|
||||
@@ -33,9 +33,9 @@ export const useAccountBalance = (assetId?: string) => {
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
accountBalance,
|
||||
accountDecimals,
|
||||
accountBalance: pubKey ? accountBalance : '',
|
||||
accountDecimals: pubKey ? accountDecimals : null,
|
||||
}),
|
||||
[accountBalance, accountDecimals]
|
||||
[accountBalance, accountDecimals, pubKey]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -32,9 +32,9 @@ export const useMarketAccountBalance = (marketId: string) => {
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
accountBalance,
|
||||
accountDecimals,
|
||||
accountBalance: pubKey ? accountBalance : '',
|
||||
accountDecimals: pubKey ? accountDecimals : null,
|
||||
}),
|
||||
[accountBalance, accountDecimals]
|
||||
[accountBalance, accountDecimals, pubKey]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
import { VegaDataSource } from './data-source';
|
||||
import type { ApolloClient } from '@apollo/client';
|
||||
import { Interval } from 'pennant';
|
||||
import type {
|
||||
CandleFieldsFragment,
|
||||
CandlesQuery,
|
||||
} from './__generated__/Candles';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const returnDataMocks = (nodes: CandleFieldsFragment[]): CandlesQuery => {
|
||||
return {
|
||||
data: {
|
||||
market: {
|
||||
decimalPlaces: 1,
|
||||
positionDecimalPlaces: 1,
|
||||
candlesConnection: {
|
||||
edges: nodes.map((node) => ({ node })),
|
||||
},
|
||||
},
|
||||
},
|
||||
} as CandlesQuery;
|
||||
};
|
||||
|
||||
const dataMocks: { [key in Schema.Interval]: Partial<CandleFieldsFragment>[] } =
|
||||
{
|
||||
[Schema.Interval.INTERVAL_I1M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:05:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I5M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:25:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I15M]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T13:15:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I1H]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T17:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I6H]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T12:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-11T18:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_I1D]: [
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-10T00:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '10',
|
||||
volume: '1',
|
||||
},
|
||||
{
|
||||
__typename: 'Candle',
|
||||
periodStart: '2023-05-15T00:00:00Z',
|
||||
lastUpdateInPeriod: '',
|
||||
close: '5',
|
||||
volume: '2',
|
||||
},
|
||||
],
|
||||
[Schema.Interval.INTERVAL_BLOCK]: [],
|
||||
};
|
||||
|
||||
describe('VegaDataSource', () => {
|
||||
const marketId = 'marketId';
|
||||
const partyId = 'partyId';
|
||||
const client = {
|
||||
query: jest.fn().mockImplementation(({ variables: { interval } }) => {
|
||||
return returnDataMocks(
|
||||
dataMocks[interval as Schema.Interval] as CandleFieldsFragment[]
|
||||
);
|
||||
}),
|
||||
} as unknown as ApolloClient<object>;
|
||||
|
||||
it('should be properly initialized', () => {
|
||||
const dataSource = new VegaDataSource(client, marketId, partyId);
|
||||
expect(dataSource).toBeInstanceOf(VegaDataSource);
|
||||
expect(dataSource.onReady).toBeDefined();
|
||||
expect(dataSource.query).toBeDefined();
|
||||
expect(dataSource.subscribeData).toBeDefined();
|
||||
expect(dataSource.unsubscribeData).toBeDefined();
|
||||
expect(dataSource.decimalPlaces).toBeDefined();
|
||||
expect(dataSource.positionDecimalPlaces).toBeDefined();
|
||||
});
|
||||
|
||||
describe('query should return continuous data', () => {
|
||||
it('when interval is I1M', async () => {
|
||||
const dataSource = new VegaDataSource(client, marketId, partyId);
|
||||
const data = await dataSource.query(Interval.I1M, '');
|
||||
expect(data).toHaveLength(6);
|
||||
expect(data[1]).toStrictEqual({
|
||||
date: new Date('2023-05-10T12:01:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
expect(data[2]).toStrictEqual({
|
||||
date: new Date('2023-05-10T12:02:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('when interval is I5M', async () => {
|
||||
const dataSource = new VegaDataSource(client, marketId, partyId);
|
||||
const data = await dataSource.query(Interval.I5M, '');
|
||||
expect(data).toHaveLength(6);
|
||||
expect(data[1]).toStrictEqual({
|
||||
date: new Date('2023-05-10T12:05:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
expect(data[2]).toStrictEqual({
|
||||
date: new Date('2023-05-10T12:10:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('when interval is I15M', async () => {
|
||||
const dataSource = new VegaDataSource(client, marketId, partyId);
|
||||
const data = await dataSource.query(Interval.I15M, '');
|
||||
expect(data).toHaveLength(6);
|
||||
expect(data[1]).toStrictEqual({
|
||||
date: new Date('2023-05-10T12:15:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
expect(data[2]).toStrictEqual({
|
||||
date: new Date('2023-05-10T12:30:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('when interval is I1H', async () => {
|
||||
const dataSource = new VegaDataSource(client, marketId, partyId);
|
||||
const data = await dataSource.query(Interval.I1H, '');
|
||||
expect(data).toHaveLength(6);
|
||||
expect(data[1]).toStrictEqual({
|
||||
date: new Date('2023-05-10T13:00:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
expect(data[2]).toStrictEqual({
|
||||
date: new Date('2023-05-10T14:00:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('when interval is I6H', async () => {
|
||||
const dataSource = new VegaDataSource(client, marketId, partyId);
|
||||
const data = await dataSource.query(Interval.I6H, '');
|
||||
expect(data).toHaveLength(6);
|
||||
expect(data[1]).toStrictEqual({
|
||||
date: new Date('2023-05-10T18:00:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
expect(data[2]).toStrictEqual({
|
||||
date: new Date('2023-05-11T00:00:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('when interval is I1D', async () => {
|
||||
const dataSource = new VegaDataSource(client, marketId, partyId);
|
||||
const data = await dataSource.query(Interval.I1D, '');
|
||||
expect(data).toHaveLength(6);
|
||||
expect(data[1]).toStrictEqual({
|
||||
date: new Date('2023-05-11T00:00:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
expect(data[2]).toStrictEqual({
|
||||
date: new Date('2023-05-12T00:00:00Z'),
|
||||
high: 1,
|
||||
low: 1,
|
||||
open: 1,
|
||||
close: 1,
|
||||
volume: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,11 @@
|
||||
import type { ApolloClient } from '@apollo/client';
|
||||
import type { Duration } from 'date-fns';
|
||||
import {
|
||||
add,
|
||||
differenceInDays,
|
||||
differenceInHours,
|
||||
differenceInMinutes,
|
||||
} from 'date-fns';
|
||||
import type { Candle, DataSource } from 'pennant';
|
||||
import { Interval as PennantInterval } from 'pennant';
|
||||
|
||||
@@ -153,7 +160,6 @@ export class VegaDataSource implements DataSource {
|
||||
},
|
||||
fetchPolicy: 'no-cache',
|
||||
});
|
||||
|
||||
if (data?.market?.candlesConnection?.edges) {
|
||||
const decimalPlaces = data.market.decimalPlaces;
|
||||
const positionDecimalPlaces = data.market.positionDecimalPlaces;
|
||||
@@ -163,8 +169,8 @@ export class VegaDataSource implements DataSource {
|
||||
.filter((node): node is CandleFieldsFragment => !!node)
|
||||
.map((node) =>
|
||||
parseCandle(node, decimalPlaces, positionDecimalPlaces)
|
||||
);
|
||||
|
||||
)
|
||||
.reduce(checkGranulationContinuity(interval), []);
|
||||
return candles;
|
||||
} else {
|
||||
return [];
|
||||
@@ -213,6 +219,85 @@ export class VegaDataSource implements DataSource {
|
||||
}
|
||||
}
|
||||
|
||||
const getDuration = (
|
||||
interval: PennantInterval,
|
||||
multiplier: number
|
||||
): Duration => {
|
||||
switch (interval) {
|
||||
case 'I1D':
|
||||
return {
|
||||
days: 1 * multiplier,
|
||||
};
|
||||
case 'I1H':
|
||||
return {
|
||||
hours: 1 * multiplier,
|
||||
};
|
||||
case 'I1M':
|
||||
return {
|
||||
minutes: 1 * multiplier,
|
||||
};
|
||||
case 'I5M':
|
||||
return {
|
||||
minutes: 5 * multiplier,
|
||||
};
|
||||
case 'I6H':
|
||||
return {
|
||||
hours: 6 * multiplier,
|
||||
};
|
||||
case 'I15M':
|
||||
return {
|
||||
minutes: 15 * multiplier,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const getDifference = (
|
||||
interval: PennantInterval,
|
||||
dateLeft: Date,
|
||||
dateRight: Date
|
||||
): number => {
|
||||
switch (interval) {
|
||||
case 'I1D':
|
||||
return differenceInDays(dateRight, dateLeft);
|
||||
case 'I6H':
|
||||
return differenceInHours(dateRight, dateLeft) / 6;
|
||||
case 'I1H':
|
||||
return differenceInHours(dateRight, dateLeft);
|
||||
case 'I15M':
|
||||
return differenceInMinutes(dateRight, dateLeft) / 15;
|
||||
case 'I5M':
|
||||
return differenceInMinutes(dateRight, dateLeft) / 5;
|
||||
case 'I1M':
|
||||
return differenceInMinutes(dateRight, dateLeft);
|
||||
}
|
||||
};
|
||||
|
||||
const checkGranulationContinuity =
|
||||
(interval: PennantInterval) =>
|
||||
(agg: Candle[], candle: Candle, i: number): Candle[] => {
|
||||
if (agg.length && i) {
|
||||
const previous = agg[agg.length - 1];
|
||||
const difference = getDifference(interval, previous.date, candle.date);
|
||||
if (difference > 1) {
|
||||
for (let j = 1; j < difference; j++) {
|
||||
const duration = getDuration(interval, j);
|
||||
const newStartDate = add(previous.date, duration);
|
||||
const newParsedCandle: Candle = {
|
||||
date: newStartDate,
|
||||
high: previous.close,
|
||||
low: previous.close,
|
||||
open: previous.close,
|
||||
close: previous.close,
|
||||
volume: 0,
|
||||
};
|
||||
agg.push(newParsedCandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
agg.push(candle);
|
||||
return agg;
|
||||
};
|
||||
|
||||
function parseCandle(
|
||||
candle: CandleFieldsFragment,
|
||||
decimalPlaces: number,
|
||||
|
||||
@@ -7,6 +7,7 @@ export * from '../candles-chart/src/lib/chart.mock';
|
||||
export * from '../deal-ticket/src/hooks/estimate-order.mock';
|
||||
export * from '../deposits/src/lib/deposit.mock';
|
||||
export * from '../environment/src/utils/node.mock';
|
||||
export * from '../environment/src/components/node-guard/node-guard.mock';
|
||||
export * from '../fills/src/lib/fills.mock';
|
||||
export * from '../proposals/src/lib/proposals-data-provider/proposals.mock';
|
||||
export * from '../ledger/src/lib/ledger-entries.mock';
|
||||
@@ -22,5 +23,8 @@ export * from '../orders/src/lib/components/order-data-provider/orders.mock';
|
||||
export * from '../positions/src/lib/positions.mock';
|
||||
export * from '../network-parameters/src/network-params.mock';
|
||||
export * from '../wallet/src/connect-dialog/chain-id.mock';
|
||||
export * from '../positions/src/lib/estimate-position.mock';
|
||||
export * from '../trades/src/lib/trades.mock';
|
||||
export * from '../withdraws/src/lib/withdrawal.mock';
|
||||
export * from '../proposals/src/lib/protocol-upgrade-proposals/protocol-statistics-proposals.mock';
|
||||
export * from '../proposals/src/lib/protocol-upgrade-proposals/block-statistics.mock';
|
||||
|
||||
@@ -17,7 +17,12 @@ const hasOperationName = (
|
||||
operationName: string
|
||||
) => {
|
||||
const { body } = req;
|
||||
return 'operationName' in body && body.operationName === operationName;
|
||||
return (
|
||||
typeof body === 'object' &&
|
||||
body !== null &&
|
||||
'operationName' in body &&
|
||||
body.operationName === operationName
|
||||
);
|
||||
};
|
||||
|
||||
export function addMockGQLCommand() {
|
||||
|
||||
@@ -20,8 +20,12 @@ const mockSocketServer = Cypress.env('VEGA_URL')
|
||||
: null;
|
||||
|
||||
// DO NOT REMOVE: PASSTHROUGH for walletconnect
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const relayServer = new Server('wss://relay.walletconnect.com', {
|
||||
new Server('wss://relay.walletconnect.com', {
|
||||
mock: false,
|
||||
});
|
||||
|
||||
// DO NOT REMOVE: PASSTHROUGH for hot module reload
|
||||
new Server('ws://localhost:4200/_next/webpack-hmr', {
|
||||
mock: false,
|
||||
});
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export const useColumnSizes = ({
|
||||
const setSizes = useCallback(
|
||||
(apiEvent: GridReadyEvent | GridSizeChangedEvent) => {
|
||||
if (!storeKey || !Object.keys(sizes).length || !widthRef.current) {
|
||||
apiEvent.api.sizeColumnsToFit();
|
||||
apiEvent?.api.sizeColumnsToFit();
|
||||
} else {
|
||||
const recalculatedSizes = recalculateSizes(sizes);
|
||||
const newSizes = Object.entries(recalculatedSizes).map(
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
import React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Icon, Tooltip, TrafficLight } from '@vegaprotocol/ui-toolkit';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import * as constants from '../constants';
|
||||
|
||||
interface DealTicketEstimatesProps {
|
||||
quoteName?: string;
|
||||
price?: string;
|
||||
estCloseOut?: string;
|
||||
estMargin?: string;
|
||||
fees?: string;
|
||||
notionalSize?: string;
|
||||
size?: string;
|
||||
slippage?: string;
|
||||
}
|
||||
|
||||
export const DealTicketEstimates = ({
|
||||
price,
|
||||
quoteName,
|
||||
estCloseOut,
|
||||
estMargin,
|
||||
fees,
|
||||
notionalSize,
|
||||
size,
|
||||
slippage,
|
||||
}: DealTicketEstimatesProps) => (
|
||||
<dl className="text-black dark:text-white">
|
||||
{size && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Contracts')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={size}
|
||||
description={constants.CONTRACTS_MARGIN_TOOLTIP_TEXT}
|
||||
id="contracts_tooltip_trigger"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{price && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Est. Price')}</DataTitle>
|
||||
<dd>{price}</dd>
|
||||
</div>
|
||||
)}
|
||||
{notionalSize && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Position Size')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={notionalSize}
|
||||
description={constants.NOTIONAL_SIZE_TOOLTIP_TEXT(quoteName || '')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{fees && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Fees')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={fees}
|
||||
description={constants.EST_FEES_TOOLTIP_TEXT}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{estMargin && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Margin')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={estMargin}
|
||||
description={constants.EST_MARGIN_TOOLTIP_TEXT(quoteName || '')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{estCloseOut && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle quoteName={quoteName}>{t('Est. Close out')}</DataTitle>
|
||||
<ValueTooltipRow
|
||||
value={estCloseOut}
|
||||
description={constants.EST_CLOSEOUT_TOOLTIP_TEXT(quoteName || '')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{slippage && (
|
||||
<div className="flex justify-between mb-2">
|
||||
<DataTitle>{t('Est. Price Impact / Slippage')}</DataTitle>
|
||||
<ValueTooltipRow description={constants.EST_SLIPPAGE}>
|
||||
<TrafficLight value={parseFloat(slippage)} q1={1} q2={5}>
|
||||
{slippage}%
|
||||
</TrafficLight>
|
||||
</ValueTooltipRow>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
);
|
||||
|
||||
interface DataTitleProps {
|
||||
children: ReactNode;
|
||||
quoteName?: string;
|
||||
}
|
||||
|
||||
export const DataTitle = ({ children, quoteName = '' }: DataTitleProps) => (
|
||||
<dt>
|
||||
{children}
|
||||
{quoteName && <small> ({quoteName})</small>}
|
||||
</dt>
|
||||
);
|
||||
|
||||
interface ValueTooltipProps {
|
||||
value?: string;
|
||||
children?: ReactNode;
|
||||
description: string;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export const ValueTooltipRow = ({
|
||||
value,
|
||||
children,
|
||||
description,
|
||||
id,
|
||||
}: ValueTooltipProps) => (
|
||||
<dd className="flex gap-x-2 items-center">
|
||||
{value || children}
|
||||
<Tooltip align="center" description={description}>
|
||||
<div className="cursor-help" id={id || ''} tabIndex={-1}>
|
||||
<Icon
|
||||
name={IconNames.ISSUE}
|
||||
className="block rotate-180"
|
||||
ariaLabel={description}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</dd>
|
||||
);
|
||||
@@ -1,24 +1,8 @@
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import classnames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||
import type { Market, MarketData } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
getFeeDetailsValues,
|
||||
useFeeDealTicketDetails,
|
||||
} from '../../hooks/use-fee-deal-ticket-details';
|
||||
|
||||
interface DealTicketFeeDetailsProps {
|
||||
order: OrderSubmissionBody['orderSubmission'];
|
||||
market: Market;
|
||||
marketData: MarketData;
|
||||
currentInitialMargin?: string;
|
||||
currentMaintenanceMargin?: string;
|
||||
estimatedInitialMargin: string;
|
||||
estimatedTotalInitialMargin: string;
|
||||
marginAccountBalance: string;
|
||||
generalAccountBalance: string;
|
||||
}
|
||||
import { getFeeDetailsValues } from '../../hooks/use-fee-deal-ticket-details';
|
||||
import type { FeeDetails } from '../../hooks/use-fee-deal-ticket-details';
|
||||
|
||||
export interface DealTicketFeeDetailProps {
|
||||
label: string;
|
||||
@@ -45,17 +29,8 @@ export const DealTicketFeeDetail = ({
|
||||
</div>
|
||||
);
|
||||
|
||||
export const DealTicketFeeDetails = ({
|
||||
order,
|
||||
market,
|
||||
marketData,
|
||||
...args
|
||||
}: DealTicketFeeDetailsProps) => {
|
||||
const feeDetails = useFeeDealTicketDetails(order, market, marketData);
|
||||
const details = getFeeDetailsValues({
|
||||
...feeDetails,
|
||||
...args,
|
||||
});
|
||||
export const DealTicketFeeDetails = (props: FeeDetails) => {
|
||||
const details = getFeeDetailsValues(props);
|
||||
return (
|
||||
<div>
|
||||
{details.map(({ label, value, labelDescription, symbol, indent }) => (
|
||||
|
||||
@@ -25,6 +25,16 @@ import {
|
||||
TinyScroll,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import {
|
||||
useEstimatePositionQuery,
|
||||
useOpenVolume,
|
||||
} from '@vegaprotocol/positions';
|
||||
import { toBigNum, removeDecimal } from '@vegaprotocol/utils';
|
||||
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||
import { useEstimateFees } from '../../hooks/use-fee-deal-ticket-details';
|
||||
import { getDerivedPrice } from '../../utils/get-price';
|
||||
import type { OrderInfo } from '@vegaprotocol/types';
|
||||
|
||||
import {
|
||||
validateExpiration,
|
||||
validateMarketState,
|
||||
@@ -34,7 +44,6 @@ import {
|
||||
} from '../../utils';
|
||||
import { ZeroBalanceError } from '../deal-ticket-validation/zero-balance-error';
|
||||
import { SummaryValidationType } from '../../constants';
|
||||
import { useInitialMargin } from '../../hooks/use-initial-margin';
|
||||
import type { Market, MarketData } from '@vegaprotocol/market-list';
|
||||
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
|
||||
import {
|
||||
@@ -104,7 +113,67 @@ export const DealTicket = ({
|
||||
market.positionDecimalPlaces
|
||||
);
|
||||
|
||||
const { margin, totalMargin } = useInitialMargin(market.id, normalizedOrder);
|
||||
const price = useMemo(() => {
|
||||
return normalizedOrder && getDerivedPrice(normalizedOrder, marketData);
|
||||
}, [normalizedOrder, marketData]);
|
||||
|
||||
const notionalSize = useMemo(() => {
|
||||
if (price && normalizedOrder?.size) {
|
||||
return removeDecimal(
|
||||
toBigNum(
|
||||
normalizedOrder.size,
|
||||
market.positionDecimalPlaces
|
||||
).multipliedBy(toBigNum(price, market.decimalPlaces)),
|
||||
asset.decimals
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}, [
|
||||
price,
|
||||
normalizedOrder?.size,
|
||||
market.decimalPlaces,
|
||||
market.positionDecimalPlaces,
|
||||
asset.decimals,
|
||||
]);
|
||||
|
||||
const feeEstimate = useEstimateFees(
|
||||
normalizedOrder && { ...normalizedOrder, price }
|
||||
);
|
||||
const { data: activeOrders } = useDataProvider({
|
||||
dataProvider: activeOrdersProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const openVolume = useOpenVolume(pubKey, market.id) ?? '0';
|
||||
const orders = activeOrders
|
||||
? activeOrders.map<OrderInfo>(({ node: order }) => ({
|
||||
isMarketOrder: order.type === OrderType.TYPE_MARKET,
|
||||
price: order.price,
|
||||
remaining: order.remaining,
|
||||
side: order.side,
|
||||
}))
|
||||
: [];
|
||||
if (normalizedOrder) {
|
||||
orders.push({
|
||||
isMarketOrder: normalizedOrder.type === OrderType.TYPE_MARKET,
|
||||
price: normalizedOrder.price ?? '0',
|
||||
remaining: normalizedOrder.size,
|
||||
side: normalizedOrder.side,
|
||||
});
|
||||
}
|
||||
const { data: positionEstimate } = useEstimatePositionQuery({
|
||||
variables: {
|
||||
marketId: market.id,
|
||||
openVolume,
|
||||
orders,
|
||||
collateralAvailable:
|
||||
marginAccountBalance || generalAccountBalance ? balance : undefined,
|
||||
},
|
||||
skip: !normalizedOrder,
|
||||
});
|
||||
|
||||
const assetSymbol =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
|
||||
const { data: currentMargins } = useDataProvider({
|
||||
dataProvider: marketMarginDataProvider,
|
||||
@@ -401,7 +470,10 @@ export const DealTicket = ({
|
||||
asset={asset}
|
||||
marketTradingMode={marketData.marketTradingMode}
|
||||
balance={balance}
|
||||
margin={totalMargin}
|
||||
margin={
|
||||
positionEstimate?.estimatePosition?.margin.bestCase.initialLevel ||
|
||||
'0'
|
||||
}
|
||||
isReadOnly={isReadOnly}
|
||||
pubKey={pubKey}
|
||||
onClickCollateral={onClickCollateral}
|
||||
@@ -413,15 +485,15 @@ export const DealTicket = ({
|
||||
}
|
||||
/>
|
||||
<DealTicketFeeDetails
|
||||
order={normalizedOrder}
|
||||
market={market}
|
||||
marketData={marketData}
|
||||
estimatedInitialMargin={margin}
|
||||
estimatedTotalInitialMargin={totalMargin}
|
||||
currentInitialMargin={currentMargins?.initialLevel}
|
||||
currentMaintenanceMargin={currentMargins?.maintenanceLevel}
|
||||
feeEstimate={feeEstimate}
|
||||
notionalSize={notionalSize}
|
||||
assetSymbol={assetSymbol}
|
||||
marginAccountBalance={marginAccountBalance}
|
||||
generalAccountBalance={generalAccountBalance}
|
||||
positionEstimate={positionEstimate?.estimatePosition}
|
||||
market={market}
|
||||
currentInitialMargin={currentMargins?.initialLevel}
|
||||
currentMaintenanceMargin={currentMargins?.maintenanceLevel}
|
||||
/>
|
||||
</form>
|
||||
</TinyScroll>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './deal-ticket';
|
||||
export * from './deal-ticket-validation';
|
||||
export * from './trading-mode-tooltip';
|
||||
export * from './deal-ticket-estimates';
|
||||
|
||||
@@ -59,6 +59,10 @@ export const EST_FEES_TOOLTIP_TEXT = t(
|
||||
'When you execute a new buy or sell order, you must pay a small amount of commission to the network for doing so. This fee is used to provide income to the node operates of the network and market makers who make prices on the futures market you are trading.'
|
||||
);
|
||||
|
||||
export const LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT = t(
|
||||
'This is a approximation to the liquidation price for that particular contract position, assuming nothing else changes, which may affect your margin and collateral balances.'
|
||||
);
|
||||
|
||||
export const EST_SLIPPAGE = t(
|
||||
'When you execute a trade on Vega, the price obtained in the market may differ from the best available price displayed at the time of placing the trade. The estimated slippage shows the difference between the best available price and the estimated execution price, determined by market liquidity and your chosen order size.'
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
query EstimateOrder(
|
||||
query EstimateFees(
|
||||
$marketId: ID!
|
||||
$partyId: ID!
|
||||
$price: String
|
||||
@@ -8,7 +8,7 @@ query EstimateOrder(
|
||||
$expiration: Timestamp
|
||||
$type: OrderType!
|
||||
) {
|
||||
estimateOrder(
|
||||
estimateFees(
|
||||
marketId: $marketId
|
||||
partyId: $partyId
|
||||
price: $price
|
||||
@@ -18,14 +18,11 @@ query EstimateOrder(
|
||||
expiration: $expiration
|
||||
type: $type
|
||||
) {
|
||||
fee {
|
||||
fees {
|
||||
makerFee
|
||||
infrastructureFee
|
||||
liquidityFee
|
||||
}
|
||||
marginLevels {
|
||||
initialLevel
|
||||
}
|
||||
totalFeeAmount
|
||||
}
|
||||
}
|
||||
|
||||
+17
-20
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type EstimateOrderQueryVariables = Types.Exact<{
|
||||
export type EstimateFeesQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
partyId: Types.Scalars['ID'];
|
||||
price?: Types.InputMaybe<Types.Scalars['String']>;
|
||||
@@ -15,12 +15,12 @@ export type EstimateOrderQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type EstimateOrderQuery = { __typename?: 'Query', estimateOrder: { __typename?: 'OrderEstimate', totalFeeAmount: string, fee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, marginLevels: { __typename?: 'MarginLevels', initialLevel: string } } };
|
||||
export type EstimateFeesQuery = { __typename?: 'Query', estimateFees: { __typename?: 'FeeEstimate', totalFeeAmount: string, fees: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string } } };
|
||||
|
||||
|
||||
export const EstimateOrderDocument = gql`
|
||||
query EstimateOrder($marketId: ID!, $partyId: ID!, $price: String, $size: String!, $side: Side!, $timeInForce: OrderTimeInForce!, $expiration: Timestamp, $type: OrderType!) {
|
||||
estimateOrder(
|
||||
export const EstimateFeesDocument = gql`
|
||||
query EstimateFees($marketId: ID!, $partyId: ID!, $price: String, $size: String!, $side: Side!, $timeInForce: OrderTimeInForce!, $expiration: Timestamp, $type: OrderType!) {
|
||||
estimateFees(
|
||||
marketId: $marketId
|
||||
partyId: $partyId
|
||||
price: $price
|
||||
@@ -30,30 +30,27 @@ export const EstimateOrderDocument = gql`
|
||||
expiration: $expiration
|
||||
type: $type
|
||||
) {
|
||||
fee {
|
||||
fees {
|
||||
makerFee
|
||||
infrastructureFee
|
||||
liquidityFee
|
||||
}
|
||||
marginLevels {
|
||||
initialLevel
|
||||
}
|
||||
totalFeeAmount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useEstimateOrderQuery__
|
||||
* __useEstimateFeesQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useEstimateOrderQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useEstimateOrderQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* To run a query within a React component, call `useEstimateFeesQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useEstimateFeesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useEstimateOrderQuery({
|
||||
* const { data, loading, error } = useEstimateFeesQuery({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* partyId: // value for 'partyId'
|
||||
@@ -66,14 +63,14 @@ export const EstimateOrderDocument = gql`
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useEstimateOrderQuery(baseOptions: Apollo.QueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
|
||||
export function useEstimateFeesQuery(baseOptions: Apollo.QueryHookOptions<EstimateFeesQuery, EstimateFeesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
|
||||
return Apollo.useQuery<EstimateFeesQuery, EstimateFeesQueryVariables>(EstimateFeesDocument, options);
|
||||
}
|
||||
export function useEstimateOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
|
||||
export function useEstimateFeesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimateFeesQuery, EstimateFeesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
|
||||
return Apollo.useLazyQuery<EstimateFeesQuery, EstimateFeesQueryVariables>(EstimateFeesDocument, options);
|
||||
}
|
||||
export type EstimateOrderQueryHookResult = ReturnType<typeof useEstimateOrderQuery>;
|
||||
export type EstimateOrderLazyQueryHookResult = ReturnType<typeof useEstimateOrderLazyQuery>;
|
||||
export type EstimateOrderQueryResult = Apollo.QueryResult<EstimateOrderQuery, EstimateOrderQueryVariables>;
|
||||
export type EstimateFeesQueryHookResult = ReturnType<typeof useEstimateFeesQuery>;
|
||||
export type EstimateFeesLazyQueryHookResult = ReturnType<typeof useEstimateFeesLazyQuery>;
|
||||
export type EstimateFeesQueryResult = Apollo.QueryResult<EstimateFeesQuery, EstimateFeesQueryVariables>;
|
||||
@@ -1,21 +1,20 @@
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import merge from 'lodash/merge';
|
||||
import type { EstimateOrderQuery } from './__generated__/EstimateOrder';
|
||||
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
|
||||
|
||||
export const estimateOrderQuery = (
|
||||
override?: PartialDeep<EstimateOrderQuery>
|
||||
): EstimateOrderQuery => {
|
||||
const defaultResult: EstimateOrderQuery = {
|
||||
estimateOrder: {
|
||||
__typename: 'OrderEstimate',
|
||||
export const estimateFeesQuery = (
|
||||
override?: PartialDeep<EstimateFeesQuery>
|
||||
): EstimateFeesQuery => {
|
||||
const defaultResult: EstimateFeesQuery = {
|
||||
estimateFees: {
|
||||
__typename: 'FeeEstimate',
|
||||
totalFeeAmount: '0.0006',
|
||||
fee: {
|
||||
fees: {
|
||||
__typename: 'TradeFee',
|
||||
makerFee: '100000',
|
||||
infrastructureFee: '100000',
|
||||
liquidityFee: '100000',
|
||||
},
|
||||
marginLevels: { __typename: 'MarginLevels', initialLevel: '1' },
|
||||
},
|
||||
};
|
||||
return merge(defaultResult, override);
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
import { FeesBreakdown } from '@vegaprotocol/market-info';
|
||||
import {
|
||||
addDecimal,
|
||||
addDecimalsFormatNumber,
|
||||
formatNumber,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useMemo } from 'react';
|
||||
import type { Market, MarketData } from '@vegaprotocol/market-list';
|
||||
import type { Market } from '@vegaprotocol/market-list';
|
||||
import type { EstimatePositionQuery } from '@vegaprotocol/positions';
|
||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
EST_TOTAL_MARGIN_TOOLTIP_TEXT,
|
||||
@@ -17,58 +12,30 @@ import {
|
||||
MARGIN_DIFF_TOOLTIP_TEXT,
|
||||
DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT,
|
||||
TOTAL_MARGIN_AVAILABLE,
|
||||
LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT,
|
||||
} from '../constants';
|
||||
import { useMarketAccountBalance } from '@vegaprotocol/accounts';
|
||||
import { getDerivedPrice } from '../utils/get-price';
|
||||
import { useEstimateOrderQuery } from './__generated__/EstimateOrder';
|
||||
import type { EstimateOrderQuery } from './__generated__/EstimateOrder';
|
||||
|
||||
export const useFeeDealTicketDetails = (
|
||||
order: OrderSubmissionBody['orderSubmission'],
|
||||
market: Market,
|
||||
marketData: MarketData
|
||||
import { useEstimateFeesQuery } from './__generated__/EstimateOrder';
|
||||
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
|
||||
|
||||
export const useEstimateFees = (
|
||||
order?: OrderSubmissionBody['orderSubmission']
|
||||
) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { accountBalance } = useMarketAccountBalance(market.id);
|
||||
|
||||
const price = useMemo(() => {
|
||||
return getDerivedPrice(order, marketData);
|
||||
}, [order, marketData]);
|
||||
|
||||
const { data: estMargin } = useEstimateOrderQuery({
|
||||
variables: {
|
||||
marketId: market.id,
|
||||
const { data } = useEstimateFeesQuery({
|
||||
variables: order && {
|
||||
marketId: order.marketId,
|
||||
partyId: pubKey || '',
|
||||
price,
|
||||
price: order.price,
|
||||
size: order.size,
|
||||
side: order.side,
|
||||
timeInForce: order.timeInForce,
|
||||
type: order.type,
|
||||
},
|
||||
skip: !pubKey || !market || !order.size || !price,
|
||||
skip: !pubKey || !order?.size || !order?.price,
|
||||
});
|
||||
|
||||
const notionalSize = useMemo(() => {
|
||||
if (price && order.size) {
|
||||
return toBigNum(order.size, market.positionDecimalPlaces)
|
||||
.multipliedBy(addDecimal(price, market.decimalPlaces))
|
||||
.toString();
|
||||
}
|
||||
return null;
|
||||
}, [price, order.size, market.decimalPlaces, market.positionDecimalPlaces]);
|
||||
|
||||
const assetSymbol =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
market,
|
||||
assetSymbol,
|
||||
notionalSize,
|
||||
accountBalance,
|
||||
estimateOrder: estMargin?.estimateOrder,
|
||||
};
|
||||
}, [market, assetSymbol, notionalSize, accountBalance, estMargin]);
|
||||
return data?.estimateFees;
|
||||
};
|
||||
|
||||
export interface FeeDetails {
|
||||
@@ -77,42 +44,54 @@ export interface FeeDetails {
|
||||
market: Market;
|
||||
assetSymbol: string;
|
||||
notionalSize: string | null;
|
||||
estimateOrder: EstimateOrderQuery['estimateOrder'] | undefined;
|
||||
estimatedInitialMargin: string;
|
||||
estimatedTotalInitialMargin: string;
|
||||
feeEstimate: EstimateFeesQuery['estimateFees'] | undefined;
|
||||
currentInitialMargin?: string;
|
||||
currentMaintenanceMargin?: string;
|
||||
positionEstimate: EstimatePositionQuery['estimatePosition'];
|
||||
}
|
||||
|
||||
const emptyValue = '-';
|
||||
const formatValue = (
|
||||
value: string | number | null | undefined,
|
||||
formatDecimals: number
|
||||
): string => {
|
||||
return isNumeric(value)
|
||||
? addDecimalsFormatNumber(value, formatDecimals)
|
||||
: emptyValue;
|
||||
};
|
||||
const formatRange = (
|
||||
min: string | number | null | undefined,
|
||||
max: string | number | null | undefined,
|
||||
formatDecimals: number
|
||||
) => {
|
||||
const minFormatted = formatValue(min, formatDecimals);
|
||||
const maxFormatted = formatValue(max, formatDecimals);
|
||||
if (minFormatted !== maxFormatted) {
|
||||
return `${minFormatted} - ${maxFormatted}`;
|
||||
}
|
||||
if (minFormatted !== emptyValue) {
|
||||
return minFormatted;
|
||||
}
|
||||
return maxFormatted;
|
||||
};
|
||||
|
||||
export const getFeeDetailsValues = ({
|
||||
marginAccountBalance,
|
||||
generalAccountBalance,
|
||||
assetSymbol,
|
||||
estimateOrder,
|
||||
feeEstimate,
|
||||
market,
|
||||
notionalSize,
|
||||
estimatedTotalInitialMargin,
|
||||
currentInitialMargin,
|
||||
currentMaintenanceMargin,
|
||||
positionEstimate,
|
||||
}: FeeDetails) => {
|
||||
const liquidationEstimate = positionEstimate?.liquidation;
|
||||
const marginEstimate = positionEstimate?.margin;
|
||||
const totalBalance =
|
||||
BigInt(generalAccountBalance || '0') + BigInt(marginAccountBalance || '0');
|
||||
const assetDecimals =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.decimals;
|
||||
const formatValueWithMarketDp = (
|
||||
value: string | number | null | undefined
|
||||
): string => {
|
||||
return value && !isNaN(Number(value))
|
||||
? formatNumber(value, market.decimalPlaces)
|
||||
: '-';
|
||||
};
|
||||
const formatValueWithAssetDp = (
|
||||
value: string | number | null | undefined
|
||||
): string => {
|
||||
return value && !isNaN(Number(value))
|
||||
? addDecimalsFormatNumber(value, assetDecimals)
|
||||
: '-';
|
||||
};
|
||||
const details: {
|
||||
label: string;
|
||||
value?: string | null;
|
||||
@@ -122,15 +101,15 @@ export const getFeeDetailsValues = ({
|
||||
}[] = [
|
||||
{
|
||||
label: t('Notional'),
|
||||
value: formatValueWithMarketDp(notionalSize),
|
||||
value: formatValue(notionalSize, assetDecimals),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: NOTIONAL_SIZE_TOOLTIP_TEXT(assetSymbol),
|
||||
},
|
||||
{
|
||||
label: t('Fees'),
|
||||
value:
|
||||
estimateOrder?.totalFeeAmount &&
|
||||
`~${formatValueWithAssetDp(estimateOrder?.totalFeeAmount)}`,
|
||||
feeEstimate?.totalFeeAmount &&
|
||||
`~${formatValue(feeEstimate?.totalFeeAmount, assetDecimals)}`,
|
||||
labelDescription: (
|
||||
<>
|
||||
<span>
|
||||
@@ -139,7 +118,7 @@ export const getFeeDetailsValues = ({
|
||||
)}
|
||||
</span>
|
||||
<FeesBreakdown
|
||||
fees={estimateOrder?.fee}
|
||||
fees={feeEstimate?.fees}
|
||||
feeFactors={market.fees.factors}
|
||||
symbol={assetSymbol}
|
||||
decimals={assetDecimals}
|
||||
@@ -148,66 +127,147 @@ export const getFeeDetailsValues = ({
|
||||
),
|
||||
symbol: assetSymbol,
|
||||
},
|
||||
{
|
||||
label: t('Margin required'),
|
||||
value: `~${formatValueWithAssetDp(
|
||||
currentInitialMargin
|
||||
? (
|
||||
BigInt(estimatedTotalInitialMargin) - BigInt(currentInitialMargin)
|
||||
).toString()
|
||||
: estimatedTotalInitialMargin
|
||||
)}`,
|
||||
symbol: assetSymbol,
|
||||
labelDescription: MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol),
|
||||
},
|
||||
];
|
||||
if (totalBalance) {
|
||||
const totalMarginAvailable = (
|
||||
currentMaintenanceMargin
|
||||
? totalBalance - BigInt(currentMaintenanceMargin)
|
||||
: totalBalance
|
||||
).toString();
|
||||
let marginRequiredBestCase: string | undefined = undefined;
|
||||
let marginRequiredWorstCase: string | undefined = undefined;
|
||||
if (marginEstimate) {
|
||||
if (currentInitialMargin) {
|
||||
marginRequiredBestCase = (
|
||||
BigInt(marginEstimate.bestCase.initialLevel) -
|
||||
BigInt(currentInitialMargin)
|
||||
).toString();
|
||||
if (marginRequiredBestCase.startsWith('-')) {
|
||||
marginRequiredBestCase = '0';
|
||||
}
|
||||
marginRequiredWorstCase = (
|
||||
BigInt(marginEstimate.worstCase.initialLevel) -
|
||||
BigInt(currentInitialMargin)
|
||||
).toString();
|
||||
if (marginRequiredWorstCase.startsWith('-')) {
|
||||
marginRequiredWorstCase = '0';
|
||||
}
|
||||
} else {
|
||||
marginRequiredBestCase = marginEstimate.bestCase.initialLevel;
|
||||
marginRequiredWorstCase = marginEstimate.worstCase.initialLevel;
|
||||
}
|
||||
}
|
||||
details.push({
|
||||
label: t('Margin required'),
|
||||
value: formatRange(
|
||||
marginRequiredBestCase,
|
||||
marginRequiredWorstCase,
|
||||
assetDecimals
|
||||
),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol),
|
||||
});
|
||||
|
||||
const totalMarginAvailable = (
|
||||
currentMaintenanceMargin
|
||||
? totalBalance - BigInt(currentMaintenanceMargin)
|
||||
: totalBalance
|
||||
).toString();
|
||||
|
||||
details.push({
|
||||
indent: true,
|
||||
label: t('Total margin available'),
|
||||
value: formatValue(totalMarginAvailable, assetDecimals),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: TOTAL_MARGIN_AVAILABLE(
|
||||
formatValue(generalAccountBalance, assetDecimals),
|
||||
formatValue(marginAccountBalance, assetDecimals),
|
||||
formatValue(currentMaintenanceMargin, assetDecimals),
|
||||
assetSymbol
|
||||
),
|
||||
});
|
||||
|
||||
if (marginAccountBalance) {
|
||||
const deductionFromCollateralBestCase =
|
||||
BigInt(marginEstimate?.bestCase.initialLevel ?? 0) -
|
||||
BigInt(marginAccountBalance);
|
||||
|
||||
const deductionFromCollateralWorstCase =
|
||||
BigInt(marginEstimate?.worstCase.initialLevel ?? 0) -
|
||||
BigInt(marginAccountBalance);
|
||||
|
||||
details.push({
|
||||
indent: true,
|
||||
label: t('Total margin available'),
|
||||
value: `~${formatValueWithAssetDp(totalMarginAvailable)}`,
|
||||
symbol: assetSymbol,
|
||||
labelDescription: TOTAL_MARGIN_AVAILABLE(
|
||||
formatValueWithAssetDp(generalAccountBalance),
|
||||
formatValueWithAssetDp(marginAccountBalance),
|
||||
formatValueWithAssetDp(currentMaintenanceMargin),
|
||||
assetSymbol
|
||||
label: t('Deduction from collateral'),
|
||||
value: formatRange(
|
||||
deductionFromCollateralBestCase > 0
|
||||
? deductionFromCollateralBestCase.toString()
|
||||
: '0',
|
||||
deductionFromCollateralWorstCase > 0
|
||||
? deductionFromCollateralWorstCase.toString()
|
||||
: '0',
|
||||
assetDecimals
|
||||
),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT(assetSymbol),
|
||||
});
|
||||
|
||||
if (marginAccountBalance) {
|
||||
const deductionFromCollateral =
|
||||
BigInt(estimatedTotalInitialMargin) - BigInt(marginAccountBalance);
|
||||
|
||||
details.push({
|
||||
indent: true,
|
||||
label: t('Deduction from collateral'),
|
||||
value: `~${formatValueWithAssetDp(
|
||||
deductionFromCollateral > 0 ? deductionFromCollateral.toString() : '0'
|
||||
)}`,
|
||||
symbol: assetSymbol,
|
||||
labelDescription: DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT(assetSymbol),
|
||||
});
|
||||
}
|
||||
|
||||
details.push({
|
||||
label: t('Projected margin'),
|
||||
value: `~${formatValueWithAssetDp(estimatedTotalInitialMargin)}`,
|
||||
value: formatRange(
|
||||
marginEstimate?.bestCase.initialLevel,
|
||||
marginEstimate?.worstCase.initialLevel,
|
||||
assetDecimals
|
||||
),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: EST_TOTAL_MARGIN_TOOLTIP_TEXT,
|
||||
});
|
||||
}
|
||||
details.push({
|
||||
label: t('Current margin allocation'),
|
||||
value: `${formatValueWithAssetDp(marginAccountBalance)}`,
|
||||
value: formatValue(marginAccountBalance, assetDecimals),
|
||||
symbol: assetSymbol,
|
||||
labelDescription: MARGIN_ACCOUNT_TOOLTIP_TEXT,
|
||||
});
|
||||
|
||||
let liquidationPriceEstimate = emptyValue;
|
||||
|
||||
if (liquidationEstimate) {
|
||||
const liquidationEstimateBestCaseIncludingBuyOrders = BigInt(
|
||||
liquidationEstimate.bestCase.including_buy_orders.replace(/\..*/, '')
|
||||
);
|
||||
const liquidationEstimateBestCaseIncludingSellOrders = BigInt(
|
||||
liquidationEstimate.bestCase.including_sell_orders.replace(/\..*/, '')
|
||||
);
|
||||
const liquidationEstimateBestCase =
|
||||
liquidationEstimateBestCaseIncludingBuyOrders >
|
||||
liquidationEstimateBestCaseIncludingSellOrders
|
||||
? liquidationEstimateBestCaseIncludingBuyOrders
|
||||
: liquidationEstimateBestCaseIncludingSellOrders;
|
||||
|
||||
const liquidationEstimateWorstCaseIncludingBuyOrders = BigInt(
|
||||
liquidationEstimate.worstCase.including_buy_orders.replace(/\..*/, '')
|
||||
);
|
||||
const liquidationEstimateWorstCaseIncludingSellOrders = BigInt(
|
||||
liquidationEstimate.worstCase.including_sell_orders.replace(/\..*/, '')
|
||||
);
|
||||
const liquidationEstimateWorstCase =
|
||||
liquidationEstimateWorstCaseIncludingBuyOrders >
|
||||
liquidationEstimateWorstCaseIncludingSellOrders
|
||||
? liquidationEstimateWorstCaseIncludingBuyOrders
|
||||
: liquidationEstimateWorstCaseIncludingSellOrders;
|
||||
liquidationPriceEstimate = formatRange(
|
||||
(liquidationEstimateBestCase < liquidationEstimateWorstCase
|
||||
? liquidationEstimateBestCase
|
||||
: liquidationEstimateWorstCase
|
||||
).toString(),
|
||||
(liquidationEstimateBestCase > liquidationEstimateWorstCase
|
||||
? liquidationEstimateBestCase
|
||||
: liquidationEstimateWorstCase
|
||||
).toString(),
|
||||
assetDecimals
|
||||
);
|
||||
}
|
||||
|
||||
details.push({
|
||||
label: t('Liquidation price estimate'),
|
||||
value: liquidationPriceEstimate,
|
||||
symbol: assetSymbol,
|
||||
labelDescription: LIQUIDATION_PRICE_ESTIMATE_TOOLTIP_TEXT,
|
||||
});
|
||||
return details;
|
||||
};
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
calculateMargins,
|
||||
// getDerivedPrice,
|
||||
volumeAndMarginProvider,
|
||||
} from '@vegaprotocol/positions';
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||
import { marketInfoProvider } from '@vegaprotocol/market-info';
|
||||
|
||||
export const useInitialMargin = (
|
||||
marketId: OrderSubmissionBody['orderSubmission']['marketId'],
|
||||
order?: OrderSubmissionBody['orderSubmission']
|
||||
) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { data: marketData } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables: { marketId },
|
||||
});
|
||||
const { data: activeVolumeAndMargin } = useDataProvider({
|
||||
dataProvider: volumeAndMarginProvider,
|
||||
variables: { marketId, partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const { data: marketInfo } = useDataProvider({
|
||||
dataProvider: marketInfoProvider,
|
||||
variables: { marketId },
|
||||
});
|
||||
let totalMargin = '0';
|
||||
let margin = '0';
|
||||
if (marketInfo?.riskFactors && marketData && order) {
|
||||
const {
|
||||
positionDecimalPlaces,
|
||||
decimalPlaces,
|
||||
tradableInstrument,
|
||||
riskFactors,
|
||||
} = marketInfo;
|
||||
const { marginCalculator, instrument } = tradableInstrument;
|
||||
const { decimals } = instrument.product.settlementAsset;
|
||||
margin = totalMargin = calculateMargins({
|
||||
side: order.side,
|
||||
size: order.size,
|
||||
price: marketData.markPrice, // getDerivedPrice(order, marketData), same in positions-data-providers
|
||||
positionDecimalPlaces,
|
||||
decimalPlaces,
|
||||
decimals,
|
||||
scalingFactors: marginCalculator?.scalingFactors,
|
||||
riskFactors,
|
||||
}).initialMargin;
|
||||
}
|
||||
|
||||
if (activeVolumeAndMargin) {
|
||||
let sellMargin = BigInt(activeVolumeAndMargin.sellInitialMargin);
|
||||
let buyMargin = BigInt(activeVolumeAndMargin.buyInitialMargin);
|
||||
if (order?.side === Side.SIDE_SELL) {
|
||||
sellMargin += BigInt(totalMargin);
|
||||
} else {
|
||||
buyMargin += BigInt(totalMargin);
|
||||
}
|
||||
totalMargin =
|
||||
sellMargin > buyMargin ? sellMargin.toString() : buyMargin.toString();
|
||||
}
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
totalMargin,
|
||||
margin,
|
||||
}),
|
||||
[totalMargin, margin]
|
||||
);
|
||||
};
|
||||
+3
-7
@@ -1,6 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useNodeSwitcherStore } from '../../hooks/use-node-switcher-store';
|
||||
|
||||
export const AppFailure = ({
|
||||
title,
|
||||
@@ -9,17 +9,13 @@ export const AppFailure = ({
|
||||
title: string;
|
||||
error?: string | null;
|
||||
}) => {
|
||||
const { setNodeSwitcher } = useGlobalStore((store) => ({
|
||||
nodeSwitcherOpen: store.nodeSwitcherDialog,
|
||||
setNodeSwitcher: (open: boolean) =>
|
||||
store.update({ nodeSwitcherDialog: open }),
|
||||
}));
|
||||
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
|
||||
const nonIdealWrapperClasses =
|
||||
'h-full min-h-screen flex items-center justify-center';
|
||||
return (
|
||||
<div className={nonIdealWrapperClasses}>
|
||||
<div className="text-center">
|
||||
<h1 className="text-xl mb-4">{title}</h1>
|
||||
<p className="text-xl mb-4">{title}</p>
|
||||
{error && <p className="text-sm mb-8">{error}</p>}
|
||||
<Button onClick={() => setNodeSwitcher(true)}>
|
||||
{t('Change node')}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './app-failure';
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './app-failure';
|
||||
export * from './network-loader';
|
||||
export * from './network-switcher';
|
||||
export * from './node-guard';
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
query NodeGuard {
|
||||
lastBlockHeight
|
||||
networkParametersConnection {
|
||||
edges {
|
||||
node {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type NodeGuardQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type NodeGuardQuery = { __typename?: 'Query', lastBlockHeight: string, networkParametersConnection: { __typename?: 'NetworkParametersConnection', edges?: Array<{ __typename?: 'NetworkParameterEdge', node: { __typename?: 'NetworkParameter', key: string, value: string } } | null> | null } };
|
||||
|
||||
|
||||
export const NodeGuardDocument = gql`
|
||||
query NodeGuard {
|
||||
lastBlockHeight
|
||||
networkParametersConnection {
|
||||
edges {
|
||||
node {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useNodeGuardQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useNodeGuardQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useNodeGuardQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useNodeGuardQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useNodeGuardQuery(baseOptions?: Apollo.QueryHookOptions<NodeGuardQuery, NodeGuardQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<NodeGuardQuery, NodeGuardQueryVariables>(NodeGuardDocument, options);
|
||||
}
|
||||
export function useNodeGuardLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<NodeGuardQuery, NodeGuardQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<NodeGuardQuery, NodeGuardQueryVariables>(NodeGuardDocument, options);
|
||||
}
|
||||
export type NodeGuardQueryHookResult = ReturnType<typeof useNodeGuardQuery>;
|
||||
export type NodeGuardLazyQueryHookResult = ReturnType<typeof useNodeGuardLazyQuery>;
|
||||
export type NodeGuardQueryResult = Apollo.QueryResult<NodeGuardQuery, NodeGuardQueryVariables>;
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { NodeGuardQuery } from './__generated__/NodeGuard';
|
||||
import merge from 'lodash/merge';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
export const nodeGuardQuery = (
|
||||
override?: PartialDeep<NodeGuardQuery>
|
||||
): NodeGuardQuery => {
|
||||
const defaultResult: NodeGuardQuery = {
|
||||
lastBlockHeight: '11',
|
||||
networkParametersConnection: {
|
||||
__typename: 'NetworkParametersConnection',
|
||||
edges: [
|
||||
{
|
||||
__typename: 'NetworkParameterEdge',
|
||||
node: {
|
||||
__typename: 'NetworkParameter' as const,
|
||||
key: 'governance.proposal.market.requiredMajority',
|
||||
value: '0.66',
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'NetworkParameterEdge',
|
||||
node: {
|
||||
__typename: 'NetworkParameter' as const,
|
||||
key: 'blockchains.ethereumConfig',
|
||||
value: JSON.stringify({
|
||||
network_id: '3',
|
||||
chain_id: '3',
|
||||
collateral_bridge_contract: {
|
||||
address: '0x7fe27d970bc8Afc3B11Cc8d9737bfB66B1efd799',
|
||||
},
|
||||
multisig_control_contract: {
|
||||
address: '0x6eBc32d66277D94DB8FF2ccF86E36f37F29a52D3',
|
||||
deployment_block_height: 12341882,
|
||||
},
|
||||
staking_bridge_contract: {
|
||||
address: '0xFFb0A0d4806502ceF491aF1141f66669A1Bd0D03',
|
||||
deployment_block_height: 11177313,
|
||||
},
|
||||
token_vesting_contract: {
|
||||
address: '0x680fF88252FA7071CAce7398e77872d54D781d0B',
|
||||
deployment_block_height: 11177353,
|
||||
},
|
||||
confirmations: 3,
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return merge(defaultResult, override);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { useStatisticsQuery } from '../../utils/__generated__/Node';
|
||||
import { useNodeGuardQuery } from './__generated__/NodeGuard';
|
||||
|
||||
export const NodeGuard = ({
|
||||
children,
|
||||
@@ -10,14 +10,19 @@ export const NodeGuard = ({
|
||||
failure: ReactNode;
|
||||
skeleton: ReactNode;
|
||||
}) => {
|
||||
const { error, loading } = useStatisticsQuery();
|
||||
const wrapperClasses = 'h-full min-h-screen flex items-center justify-center';
|
||||
const { data, error, loading } = useNodeGuardQuery();
|
||||
const wrapperClasses =
|
||||
'h-full min-h-screen flex items-center justify-center text-black dark:text-white';
|
||||
|
||||
if (loading) {
|
||||
return <div className={wrapperClasses}>{skeleton}</div>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
// It is possible for nodes to have a functioning datanode, but not return
|
||||
// any net params. The app cannot safely function without net params
|
||||
const netParamEdges = data?.networkParametersConnection.edges;
|
||||
|
||||
if (error || !netParamEdges || !netParamEdges.length) {
|
||||
return <div className={wrapperClasses}>{failure}</div>;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,13 +96,13 @@ describe('RowData', () => {
|
||||
mockHeaders(props.url);
|
||||
render(renderComponent(props, statsQueryMock, subMock));
|
||||
|
||||
// radio should be disabled until query resolves
|
||||
// radio should be enabled until query resolves
|
||||
expect(
|
||||
screen.getByRole('radio', {
|
||||
checked: false,
|
||||
name: props.url,
|
||||
})
|
||||
).toBeDisabled();
|
||||
).toBeEnabled();
|
||||
expect(screen.getByTestId('response-time-cell')).toHaveTextContent(
|
||||
'Checking'
|
||||
);
|
||||
@@ -124,7 +124,7 @@ describe('RowData', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('radio button disabled if query fails', async () => {
|
||||
it('radio button still enabled if query fails', async () => {
|
||||
mockHeaders(props.url, {});
|
||||
|
||||
const failedQueryMock: MockedResponse<StatisticsQuery> = {
|
||||
@@ -149,7 +149,7 @@ describe('RowData', () => {
|
||||
checked: false,
|
||||
name: props.url,
|
||||
})
|
||||
).toBeDisabled();
|
||||
).toBeEnabled();
|
||||
expect(screen.getByTestId('response-time-cell')).toHaveTextContent(
|
||||
'Checking'
|
||||
);
|
||||
@@ -170,7 +170,7 @@ describe('RowData', () => {
|
||||
checked: false,
|
||||
name: props.url,
|
||||
})
|
||||
).toBeDisabled();
|
||||
).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -207,19 +207,6 @@ describe('RowData', () => {
|
||||
expect(screen.getByTestId('block-height-cell')).toHaveClass('text-danger');
|
||||
});
|
||||
|
||||
it('disables radio button if url is invalid', () => {
|
||||
mockHeaders(props.url, { blockHeight: 100 });
|
||||
|
||||
render(renderComponent(props, statsQueryMock, subMock));
|
||||
|
||||
expect(
|
||||
screen.getByRole('radio', {
|
||||
checked: false,
|
||||
name: props.url,
|
||||
})
|
||||
).toBeDisabled();
|
||||
});
|
||||
|
||||
it('doesnt render the radio if its the custom row', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
|
||||
@@ -124,29 +124,6 @@ export const RowData = ({
|
||||
return false;
|
||||
};
|
||||
|
||||
const getIsNodeDisabled = () => {
|
||||
if (!isValidUrl(url)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if still waiting or query errored disable node
|
||||
if (loading || error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (subLoading || subError) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we are still waiting for a header entry for this
|
||||
// url disable the node
|
||||
if (!headers) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const getSubFailed = (
|
||||
subError: ApolloError | undefined,
|
||||
subFailed: boolean
|
||||
@@ -160,12 +137,7 @@ export const RowData = ({
|
||||
<>
|
||||
{id !== CUSTOM_NODE_KEY && (
|
||||
<div className="break-all" data-testid="node">
|
||||
<Radio
|
||||
id={`node-url-${id}`}
|
||||
value={url}
|
||||
label={url}
|
||||
disabled={getIsNodeDisabled()}
|
||||
/>
|
||||
<Radio id={`node-url-${id}`} value={url} label={url} />
|
||||
</div>
|
||||
)}
|
||||
<LayoutCell
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './use-environment';
|
||||
export * from './use-links';
|
||||
export * from './use-node-health';
|
||||
export * from './use-node-switcher-store';
|
||||
export * from './use-vega-releases';
|
||||
export * from './use-vega-release';
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Networks } from '../types';
|
||||
import { compileErrors } from '../utils/compile-errors';
|
||||
import { envSchema } from '../utils/validate-environment';
|
||||
import { tomlConfigSchema } from '../utils/validate-configuration';
|
||||
import uniq from 'lodash/uniq';
|
||||
|
||||
type Client = ReturnType<typeof createClient>;
|
||||
type ClientCollection = {
|
||||
@@ -32,7 +33,8 @@ type Actions = {
|
||||
export type Env = Environment & EnvState;
|
||||
export type EnvStore = Env & Actions;
|
||||
|
||||
export const STORAGE_KEY = 'vega_url';
|
||||
const VERSION = 1;
|
||||
export const STORAGE_KEY = `vega_url_${VERSION}`;
|
||||
const SUBSCRIPTION_TIMEOUT = 3000;
|
||||
|
||||
export const useEnvironment = create<EnvStore>()((set, get) => ({
|
||||
@@ -69,7 +71,10 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({
|
||||
let nodes: string[] | undefined;
|
||||
try {
|
||||
nodes = await fetchConfig(state.VEGA_CONFIG_URL);
|
||||
set({ nodes });
|
||||
const enrichedNodes = uniq(
|
||||
[...nodes, state.VEGA_URL, storedUrl].filter(Boolean) as string[]
|
||||
);
|
||||
set({ nodes: enrichedNodes });
|
||||
} catch (err) {
|
||||
console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export const useNodeHealth = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!('Cypress' in window)) {
|
||||
if (!('Cypress' in window) && window.location.hostname !== 'localhost') {
|
||||
startPolling(POLL_INTERVAL);
|
||||
}
|
||||
}, [error, startPolling, stopPolling]);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
export const useNodeSwitcherStore = create<{
|
||||
dialogOpen: boolean;
|
||||
setDialogOpen: (isOpen: boolean) => void;
|
||||
}>()((set) => ({
|
||||
dialogOpen: false,
|
||||
setDialogOpen: (isOpen) => {
|
||||
set({ dialogOpen: isOpen });
|
||||
},
|
||||
}));
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Fragment, useState } from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Link, Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
useEnvironment,
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
|
||||
const getFeedbackLinks = (gitOriginUrl?: string) =>
|
||||
[
|
||||
@@ -19,64 +22,59 @@ export const NetworkInfo = () => {
|
||||
GITHUB_FEEDBACK_URL,
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
} = useEnvironment();
|
||||
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
|
||||
|
||||
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
|
||||
const feedbackLinks = getFeedbackLinks(GITHUB_FEEDBACK_URL);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div data-testid="git-info">
|
||||
<p data-testid="git-network-data" className="mb-2">
|
||||
{t('Reading network data from')}{' '}
|
||||
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
|
||||
{VEGA_URL}
|
||||
</Lozenge>
|
||||
. <Link onClick={() => setNodeSwitcherOpen(true)}>{t('Edit')}</Link>
|
||||
<div data-testid="git-info">
|
||||
<p data-testid="git-network-data" className="mb-2">
|
||||
{t('Reading network data from')}{' '}
|
||||
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
|
||||
{VEGA_URL}
|
||||
</Lozenge>
|
||||
. <Link onClick={() => setNodeSwitcher(true)}>{t('Edit')}</Link>
|
||||
</p>
|
||||
<p data-testid="git-eth-data" className="mb-2 break-all">
|
||||
{t('Reading Ethereum data from')}{' '}
|
||||
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
|
||||
{ETHEREUM_PROVIDER_URL}
|
||||
</Lozenge>
|
||||
.{' '}
|
||||
</p>
|
||||
{GIT_COMMIT_HASH && (
|
||||
<p data-testid="git-commit-hash" className="mb-2">
|
||||
{t('Version/commit hash')}:{' '}
|
||||
<Link
|
||||
href={
|
||||
GIT_ORIGIN_URL
|
||||
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
|
||||
: undefined
|
||||
}
|
||||
target={GIT_ORIGIN_URL ? '_blank' : undefined}
|
||||
>
|
||||
{GIT_COMMIT_HASH}
|
||||
</Link>
|
||||
</p>
|
||||
<p data-testid="git-eth-data" className="mb-2 break-all">
|
||||
{t('Reading Ethereum data from')}{' '}
|
||||
<Lozenge className="bg-neutral-300 dark:bg-neutral-700">
|
||||
{ETHEREUM_PROVIDER_URL}
|
||||
</Lozenge>
|
||||
.{' '}
|
||||
)}
|
||||
{feedbackLinks.length > 0 && (
|
||||
<p>
|
||||
{t('Known issues and feedback on')}{' '}
|
||||
{feedbackLinks.map(({ name, url }, index) => (
|
||||
<Fragment key={index}>
|
||||
<Link key={index} href={url}>
|
||||
{name}
|
||||
</Link>
|
||||
{feedbackLinks.length > 1 &&
|
||||
index < feedbackLinks.length - 2 &&
|
||||
','}
|
||||
{feedbackLinks.length > 1 &&
|
||||
index === feedbackLinks.length - 1 &&
|
||||
`, ${t('and')} `}
|
||||
</Fragment>
|
||||
))}
|
||||
</p>
|
||||
{GIT_COMMIT_HASH && (
|
||||
<p data-testid="git-commit-hash" className="mb-2">
|
||||
{t('Version/commit hash')}:{' '}
|
||||
<Link
|
||||
href={
|
||||
GIT_ORIGIN_URL
|
||||
? `${GIT_ORIGIN_URL}/commit/${GIT_COMMIT_HASH}`
|
||||
: undefined
|
||||
}
|
||||
target={GIT_ORIGIN_URL ? '_blank' : undefined}
|
||||
>
|
||||
{GIT_COMMIT_HASH}
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
{feedbackLinks.length > 0 && (
|
||||
<p>
|
||||
{t('Known issues and feedback on')}{' '}
|
||||
{feedbackLinks.map(({ name, url }, index) => (
|
||||
<Fragment key={index}>
|
||||
<Link key={index} href={url}>
|
||||
{name}
|
||||
</Link>
|
||||
{feedbackLinks.length > 1 &&
|
||||
index < feedbackLinks.length - 2 &&
|
||||
','}
|
||||
{feedbackLinks.length > 1 &&
|
||||
index === feedbackLinks.length - 1 &&
|
||||
`, ${t('and')} `}
|
||||
</Fragment>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<NodeSwitcherDialog
|
||||
open={nodeSwitcherOpen}
|
||||
setOpen={setNodeSwitcherOpen}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ export * from './lib/__generated__/Positions';
|
||||
export * from './lib/positions-container';
|
||||
export * from './lib/positions-data-providers';
|
||||
export * from './lib/margin-data-provider';
|
||||
export * from './lib/margin-calculator';
|
||||
export * from './lib/positions-table';
|
||||
export * from './lib/use-market-margin';
|
||||
export * from './lib/use-open-volume';
|
||||
|
||||
@@ -75,3 +75,44 @@ subscription MarginsSubscription($partyId: ID!) {
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
|
||||
query EstimatePosition(
|
||||
$marketId: ID!
|
||||
$openVolume: String!
|
||||
$orders: [OrderInfo!]
|
||||
$collateralAvailable: String
|
||||
) {
|
||||
estimatePosition(
|
||||
marketId: $marketId
|
||||
openVolume: $openVolume
|
||||
orders: $orders
|
||||
collateralAvailable: $collateralAvailable
|
||||
) {
|
||||
margin {
|
||||
worstCase {
|
||||
maintenanceLevel
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
}
|
||||
bestCase {
|
||||
maintenanceLevel
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
}
|
||||
}
|
||||
liquidation {
|
||||
worstCase {
|
||||
open_volume_only
|
||||
including_buy_orders
|
||||
including_sell_orders
|
||||
}
|
||||
bestCase {
|
||||
open_volume_only
|
||||
including_buy_orders
|
||||
including_sell_orders
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+79
-1
@@ -35,6 +35,16 @@ export type MarginsSubscriptionSubscriptionVariables = Types.Exact<{
|
||||
|
||||
export type MarginsSubscriptionSubscription = { __typename?: 'Subscription', margins: { __typename?: 'MarginLevelsUpdate', marketId: string, asset: string, partyId: string, maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, timestamp: any } };
|
||||
|
||||
export type EstimatePositionQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
openVolume: Types.Scalars['String'];
|
||||
orders?: Types.InputMaybe<Array<Types.OrderInfo> | Types.OrderInfo>;
|
||||
collateralAvailable?: Types.InputMaybe<Types.Scalars['String']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type EstimatePositionQuery = { __typename?: 'Query', estimatePosition?: { __typename?: 'PositionEstimate', margin: { __typename?: 'MarginEstimate', worstCase: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string }, bestCase: { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string } }, liquidation?: { __typename?: 'LiquidationEstimate', worstCase: { __typename?: 'LiquidationPrice', open_volume_only: string, including_buy_orders: string, including_sell_orders: string }, bestCase: { __typename?: 'LiquidationPrice', open_volume_only: string, including_buy_orders: string, including_sell_orders: string } } | null } | null };
|
||||
|
||||
export const PositionFieldsFragmentDoc = gql`
|
||||
fragment PositionFields on Position {
|
||||
realisedPNL
|
||||
@@ -220,4 +230,72 @@ export function useMarginsSubscriptionSubscription(baseOptions: Apollo.Subscript
|
||||
return Apollo.useSubscription<MarginsSubscriptionSubscription, MarginsSubscriptionSubscriptionVariables>(MarginsSubscriptionDocument, options);
|
||||
}
|
||||
export type MarginsSubscriptionSubscriptionHookResult = ReturnType<typeof useMarginsSubscriptionSubscription>;
|
||||
export type MarginsSubscriptionSubscriptionResult = Apollo.SubscriptionResult<MarginsSubscriptionSubscription>;
|
||||
export type MarginsSubscriptionSubscriptionResult = Apollo.SubscriptionResult<MarginsSubscriptionSubscription>;
|
||||
export const EstimatePositionDocument = gql`
|
||||
query EstimatePosition($marketId: ID!, $openVolume: String!, $orders: [OrderInfo!], $collateralAvailable: String) {
|
||||
estimatePosition(
|
||||
marketId: $marketId
|
||||
openVolume: $openVolume
|
||||
orders: $orders
|
||||
collateralAvailable: $collateralAvailable
|
||||
) {
|
||||
margin {
|
||||
worstCase {
|
||||
maintenanceLevel
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
}
|
||||
bestCase {
|
||||
maintenanceLevel
|
||||
searchLevel
|
||||
initialLevel
|
||||
collateralReleaseLevel
|
||||
}
|
||||
}
|
||||
liquidation {
|
||||
worstCase {
|
||||
open_volume_only
|
||||
including_buy_orders
|
||||
including_sell_orders
|
||||
}
|
||||
bestCase {
|
||||
open_volume_only
|
||||
including_buy_orders
|
||||
including_sell_orders
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useEstimatePositionQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useEstimatePositionQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useEstimatePositionQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useEstimatePositionQuery({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* openVolume: // value for 'openVolume'
|
||||
* orders: // value for 'orders'
|
||||
* collateralAvailable: // value for 'collateralAvailable'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useEstimatePositionQuery(baseOptions: Apollo.QueryHookOptions<EstimatePositionQuery, EstimatePositionQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<EstimatePositionQuery, EstimatePositionQueryVariables>(EstimatePositionDocument, options);
|
||||
}
|
||||
export function useEstimatePositionLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimatePositionQuery, EstimatePositionQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<EstimatePositionQuery, EstimatePositionQueryVariables>(EstimatePositionDocument, options);
|
||||
}
|
||||
export type EstimatePositionQueryHookResult = ReturnType<typeof useEstimatePositionQuery>;
|
||||
export type EstimatePositionLazyQueryHookResult = ReturnType<typeof useEstimatePositionLazyQuery>;
|
||||
export type EstimatePositionQueryResult = Apollo.QueryResult<EstimatePositionQuery, EstimatePositionQueryVariables>;
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import merge from 'lodash/merge';
|
||||
import type { EstimatePositionQuery } from './__generated__/Positions';
|
||||
|
||||
export const estimatePositionQuery = (
|
||||
override?: PartialDeep<EstimatePositionQuery>
|
||||
): EstimatePositionQuery => {
|
||||
const defaultResult: EstimatePositionQuery = {
|
||||
estimatePosition: {
|
||||
__typename: 'PositionEstimate',
|
||||
margin: {
|
||||
bestCase: {
|
||||
collateralReleaseLevel: '1000000',
|
||||
initialLevel: '500000',
|
||||
maintenanceLevel: '200000',
|
||||
searchLevel: '300000',
|
||||
},
|
||||
worstCase: {
|
||||
collateralReleaseLevel: '1100000',
|
||||
initialLevel: '600000',
|
||||
maintenanceLevel: '300000',
|
||||
searchLevel: '400000',
|
||||
},
|
||||
},
|
||||
liquidation: {
|
||||
bestCase: {
|
||||
including_buy_orders: '1',
|
||||
including_sell_orders: '1',
|
||||
open_volume_only: '1',
|
||||
},
|
||||
worstCase: {
|
||||
including_buy_orders: '1',
|
||||
including_sell_orders: '1',
|
||||
open_volume_only: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
return merge(defaultResult, override);
|
||||
};
|
||||
@@ -1,95 +0,0 @@
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { Side, MarketTradingMode, OrderType } from '@vegaprotocol/types';
|
||||
import type { ScalingFactors, RiskFactor } from '@vegaprotocol/types';
|
||||
import type { MarketData } from '@vegaprotocol/market-list';
|
||||
|
||||
export const isMarketInAuction = (marketTradingMode: MarketTradingMode) => {
|
||||
return [
|
||||
MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
|
||||
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
||||
].includes(marketTradingMode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the market price based on market mode (auction or not auction)
|
||||
*/
|
||||
export const getMarketPrice = ({
|
||||
marketTradingMode,
|
||||
indicativePrice,
|
||||
markPrice,
|
||||
}: Pick<MarketData, 'marketTradingMode' | 'indicativePrice' | 'markPrice'>) => {
|
||||
if (isMarketInAuction(marketTradingMode)) {
|
||||
// 0 can never be a valid uncrossing price
|
||||
// as it would require there being orders on the book at that price.
|
||||
if (
|
||||
indicativePrice &&
|
||||
indicativePrice !== '0' &&
|
||||
BigInt(indicativePrice) !== BigInt(0)
|
||||
) {
|
||||
return indicativePrice;
|
||||
}
|
||||
}
|
||||
return markPrice;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the price for an order, order limit this is the user
|
||||
* entered value, for market this will be the mark price or
|
||||
* if in auction the indicative uncrossing price
|
||||
*/
|
||||
export const getDerivedPrice = (
|
||||
order: {
|
||||
type?: OrderType | null;
|
||||
price?: string;
|
||||
},
|
||||
marketData: Pick<
|
||||
MarketData,
|
||||
'marketTradingMode' | 'indicativePrice' | 'markPrice'
|
||||
>
|
||||
) => {
|
||||
// If order type is market we should use either the mark price
|
||||
// or the uncrossing price. If order type is limit use the price
|
||||
// the user has input
|
||||
|
||||
// Use the market price if order is a market order
|
||||
if (order.type === OrderType.TYPE_LIMIT && order.price) {
|
||||
return order.price;
|
||||
}
|
||||
return getMarketPrice(marketData);
|
||||
};
|
||||
|
||||
export const calculateMargins = ({
|
||||
size,
|
||||
side,
|
||||
price,
|
||||
decimals,
|
||||
positionDecimalPlaces,
|
||||
decimalPlaces,
|
||||
scalingFactors,
|
||||
riskFactors,
|
||||
}: {
|
||||
size: string;
|
||||
side: Side;
|
||||
positionDecimalPlaces: number;
|
||||
decimalPlaces: number;
|
||||
decimals: number;
|
||||
price: string;
|
||||
scalingFactors?: ScalingFactors;
|
||||
riskFactors: RiskFactor;
|
||||
}) => {
|
||||
const maintenanceMargin = toBigNum(size, positionDecimalPlaces)
|
||||
.multipliedBy(
|
||||
side === Side.SIDE_SELL ? riskFactors.short : riskFactors.long
|
||||
)
|
||||
.multipliedBy(toBigNum(price, decimalPlaces));
|
||||
return {
|
||||
maintenanceMargin: maintenanceMargin
|
||||
.multipliedBy(Math.pow(10, decimals))
|
||||
.toFixed(0),
|
||||
initialMargin: maintenanceMargin
|
||||
.multipliedBy(scalingFactors?.initialMargin ?? 1)
|
||||
.multipliedBy(Math.pow(10, decimals))
|
||||
.toFixed(0),
|
||||
};
|
||||
};
|
||||
@@ -5,7 +5,6 @@ import sortBy from 'lodash/sortBy';
|
||||
import type { Account } from '@vegaprotocol/accounts';
|
||||
import { accountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { toBigNum, removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import type { Edge } from '@vegaprotocol/data-provider';
|
||||
import {
|
||||
makeDataProvider,
|
||||
makeDerivedDataProvider,
|
||||
@@ -28,14 +27,6 @@ import {
|
||||
PositionsSubscriptionDocument,
|
||||
} from './__generated__/Positions';
|
||||
import { marginsDataProvider } from './margin-data-provider';
|
||||
import { calculateMargins } from './margin-calculator';
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
import { marketInfoProvider } from '@vegaprotocol/market-info';
|
||||
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
|
||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||
import type { MarketData } from '@vegaprotocol/market-list';
|
||||
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||
import type { OrderFieldsFragment } from '@vegaprotocol/orders';
|
||||
import type { PositionStatus } from '@vegaprotocol/types';
|
||||
|
||||
type PositionMarginLevel = Pick<
|
||||
@@ -336,98 +327,3 @@ export const positionsMetricsProvider = makeDerivedDataProvider<
|
||||
return !(previousRow && isEqual(previousRow, row));
|
||||
})
|
||||
);
|
||||
|
||||
export const volumeAndMarginProvider = makeDerivedDataProvider<
|
||||
{
|
||||
buyVolume: string;
|
||||
sellVolume: string;
|
||||
buyInitialMargin: string;
|
||||
sellInitialMargin: string;
|
||||
},
|
||||
never,
|
||||
PositionsQueryVariables & MarketDataQueryVariables
|
||||
>(
|
||||
[
|
||||
(callback, client, { partyId, marketId }) =>
|
||||
activeOrdersProvider(callback, client, {
|
||||
partyId,
|
||||
marketId,
|
||||
}),
|
||||
(callback, client, { marketId }) =>
|
||||
marketDataProvider(callback, client, { marketId }),
|
||||
(callback, client, { marketId }) =>
|
||||
marketInfoProvider(callback, client, { marketId }),
|
||||
openVolumeDataProvider,
|
||||
],
|
||||
(data) => {
|
||||
const orders = data[0] as (Edge<OrderFieldsFragment> | null)[] | null;
|
||||
const marketData = data[1] as MarketData | null;
|
||||
const marketInfo = data[2] as MarketInfoQuery['market'];
|
||||
let openVolume = (data[3] as string | null) || '0';
|
||||
const shortPosition = openVolume?.startsWith('-');
|
||||
if (shortPosition) {
|
||||
openVolume = openVolume.substring(1);
|
||||
}
|
||||
let buyVolume = BigInt(shortPosition ? 0 : openVolume);
|
||||
let sellVolume = BigInt(shortPosition ? openVolume : 0);
|
||||
let buyInitialMargin = BigInt(0);
|
||||
let sellInitialMargin = BigInt(0);
|
||||
if (marketInfo?.riskFactors && marketData) {
|
||||
const {
|
||||
positionDecimalPlaces,
|
||||
decimalPlaces,
|
||||
tradableInstrument,
|
||||
riskFactors,
|
||||
} = marketInfo;
|
||||
const { marginCalculator, instrument } = tradableInstrument;
|
||||
const { decimals } = instrument.product.settlementAsset;
|
||||
const calculatorParams = {
|
||||
positionDecimalPlaces,
|
||||
decimalPlaces,
|
||||
decimals,
|
||||
scalingFactors: marginCalculator?.scalingFactors,
|
||||
riskFactors,
|
||||
};
|
||||
if (openVolume !== '0') {
|
||||
const { initialMargin } = calculateMargins({
|
||||
side: shortPosition ? Side.SIDE_SELL : Side.SIDE_BUY,
|
||||
size: openVolume,
|
||||
price: marketData.markPrice,
|
||||
...calculatorParams,
|
||||
});
|
||||
if (shortPosition) {
|
||||
sellInitialMargin += BigInt(initialMargin);
|
||||
} else {
|
||||
buyInitialMargin += BigInt(initialMargin);
|
||||
}
|
||||
}
|
||||
orders?.forEach((order) => {
|
||||
if (!order) {
|
||||
return;
|
||||
}
|
||||
const { side, remaining: size } = order.node;
|
||||
const initialMargin = BigInt(
|
||||
calculateMargins({
|
||||
side,
|
||||
size,
|
||||
price: marketData.markPrice, //getDerivedPrice(order.node, marketData), same use-initial-margin
|
||||
...calculatorParams,
|
||||
}).initialMargin
|
||||
);
|
||||
if (order.node.side === Side.SIDE_BUY) {
|
||||
buyVolume += BigInt(size);
|
||||
buyInitialMargin += initialMargin;
|
||||
} else {
|
||||
sellVolume += BigInt(size);
|
||||
sellInitialMargin += initialMargin;
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
buyVolume: buyVolume.toString(),
|
||||
sellVolume: sellVolume.toString(),
|
||||
buyInitialMargin: buyInitialMargin.toString(),
|
||||
sellInitialMargin: sellInitialMargin.toString(),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { BlockStatisticsQuery } from './__generated__/BlockStatistics';
|
||||
import merge from 'lodash/merge';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
export const blockStatisticsQuery = (
|
||||
override?: PartialDeep<BlockStatisticsQuery>
|
||||
): BlockStatisticsQuery => {
|
||||
const defaultResult = {
|
||||
statistics: {
|
||||
__typename: 'Statistics',
|
||||
blockHeight: '100',
|
||||
blockDuration: '100',
|
||||
},
|
||||
};
|
||||
|
||||
return merge(defaultResult, override);
|
||||
};
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import type { ProtocolUpgradeProposalsQuery } from './__generated__/ProtocolUpgradeProposals';
|
||||
import merge from 'lodash/merge';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
|
||||
export const protocolUpgradeProposalsQuery = (
|
||||
override?: PartialDeep<ProtocolUpgradeProposalsQuery>
|
||||
): ProtocolUpgradeProposalsQuery => {
|
||||
const defaultResult: ProtocolUpgradeProposalsQuery = {
|
||||
lastBlockHeight: '100',
|
||||
};
|
||||
|
||||
return merge(defaultResult, override);
|
||||
};
|
||||
+22
-11
@@ -1,19 +1,30 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useMemo, useEffect } from 'react';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useProtocolUpgradeProposalsQuery } from './__generated__/ProtocolUpgradeProposals';
|
||||
|
||||
export const useNextProtocolUpgradeProposals = (since?: number) => {
|
||||
const { data, loading, error } = useProtocolUpgradeProposalsQuery({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
inState:
|
||||
Schema.ProtocolUpgradeProposalStatus
|
||||
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
||||
},
|
||||
});
|
||||
const { data, loading, error, startPolling, stopPolling } =
|
||||
useProtocolUpgradeProposalsQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
inState:
|
||||
Schema.ProtocolUpgradeProposalStatus
|
||||
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
stopPolling();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!('Cypress' in window) && window.location.hostname !== 'localhost') {
|
||||
startPolling(5000);
|
||||
}
|
||||
}, [error, startPolling, stopPolling]);
|
||||
|
||||
const nextUpgrades = useMemo(() => {
|
||||
if (!data) return [];
|
||||
|
||||
@@ -8,20 +8,29 @@ const durations = [] as number[];
|
||||
|
||||
const useAverageBlockDuration = (polls = DEFAULT_POLLS) => {
|
||||
const [avg, setAvg] = useState<number | undefined>(undefined);
|
||||
const { data } = useBlockStatisticsQuery({
|
||||
pollInterval: INTERVAL,
|
||||
const { data, startPolling, stopPolling, error } = useBlockStatisticsQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
skip: durations.length === polls,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (error) {
|
||||
stopPolling();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!('Cypress' in window) && window.location.hostname !== 'localhost') {
|
||||
startPolling(INTERVAL);
|
||||
}
|
||||
}, [error, startPolling, stopPolling]);
|
||||
|
||||
useEffect(() => {
|
||||
if (durations.length < polls && data) {
|
||||
durations.push(parseFloat(data.statistics.blockDuration));
|
||||
}
|
||||
if (durations.length === polls) {
|
||||
const averageBlockDuration = sum(durations) / durations.length; // ms
|
||||
console.log('setting avg', averageBlockDuration);
|
||||
setAvg(averageBlockDuration);
|
||||
}
|
||||
}, [data, polls]);
|
||||
|
||||
@@ -32,8 +32,10 @@ export function addDecimal(
|
||||
return toBigNum(value, decimals).toFixed(decimalPrecision);
|
||||
}
|
||||
|
||||
export function removeDecimal(value: string, decimals: number): string {
|
||||
if (!decimals) return value;
|
||||
export function removeDecimal(
|
||||
value: string | BigNumber,
|
||||
decimals: number
|
||||
): string {
|
||||
return new BigNumber(value || 0).times(Math.pow(10, decimals)).toFixed(0);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user