Compare commits

..
Author SHA1 Message Date
sam-keen fda1ef9bbd fix(3690): removed nested ternary 2023-05-10 16:34:38 +01:00
sam-keen 8d82f4873c fix(3690): fixes for tranches data 2023-05-10 16:16:16 +01:00
42 changed files with 713 additions and 1079 deletions
@@ -7,7 +7,6 @@ import {
waitForSpinner,
navigateTo,
navigation,
turnTelemetryOff,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
@@ -57,7 +56,6 @@ 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');
@@ -69,7 +67,6 @@ context(
'teardown wallet & drill into a specific validator',
function () {
cy.clearLocalStorage();
turnTelemetryOff();
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
@@ -255,10 +252,10 @@ context(
waitForBeginningOfEpoch();
cy.getByTestId(stakeValidatorListStakePercentage).should(
'have.text',
'50.02%'
'100%'
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
}
);
@@ -29,10 +29,7 @@ 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{1,3}(,\d{3})*(\.\d+)?$/;
// If running locally, validators need to have self-stake to be displayed
// Run cy.validatorsSelfDelegate() in before hook
const stakeNumberRegex = /^\d*\.?\d*$/;
context('Validators Page - verify elements on page', function () {
before('navigate to validators page', function () {
@@ -87,13 +84,13 @@ context('Validators Page - verify elements on page', function () {
cy.get(stakedByOperatorToolTip)
.invoke('text')
.should('contain', 'Staked by operator: 3,000.00');
.should('contain', 'Staked by operator: 0.00');
cy.get(stakedByDelegatesToolTip)
.invoke('text')
.should('contain', 'Staked by delegates: 0.00');
cy.get(totalStakedToolTip)
.invoke('text')
.should('contain', 'Total stake: 3,000.00');
.should('contain', 'Total stake: 0.00');
});
it('Should be able to see validator normalised voting power', function () {
@@ -109,10 +106,10 @@ context('Validators Page - verify elements on page', function () {
cy.get(unnormalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Unnormalised voting power: 20.00%');
.should('contain', 'Unnormalised voting power: 0.00%');
cy.get(normalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Normalised voting power: 50.00%');
.should('contain', 'Normalised voting power: 0.10%');
});
// 2002-SINC-018
@@ -129,13 +126,13 @@ context('Validators Page - verify elements on page', function () {
cy.get(performancePenaltyToolTip)
.invoke('text')
.should('contain', 'Performance penalty: 0.00%');
.should('contain', 'Performance penalty: 100.00%');
cy.get(overstakedPenaltyToolTip)
.invoke('text')
.should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip)
.invoke('text')
.should('contain', 'Total penalties: 60.00%');
.should('contain', 'Total penalties: 0.00%');
});
it('Should be able to see validator pending stake', function () {
@@ -50,7 +50,22 @@ export const useTranches = create<TranchesStore>()((set) => ({
?.map((t) => {
const tranche_progress =
t.duration !== 0 ? (now - t.cliff_start) / t.duration : 0;
const lockedDecimal = tranche_progress < 0 ? 1 : 1 - tranche_progress;
let lockedDecimal;
if (t.duration !== 0) {
if (tranche_progress < 0) {
lockedDecimal = 1;
} else {
lockedDecimal = 1 - tranche_progress;
}
} else {
if (now < t.cliff_start) {
lockedDecimal = 1;
} else {
lockedDecimal = 0;
}
}
const clampedLockedDecimal = Math.max(0, Math.min(1, lockedDecimal));
return {
tranche_id: t.tranche_id,
tranche_start: secondsToDate(t.cliff_start),
@@ -60,7 +75,7 @@ export const useTranches = create<TranchesStore>()((set) => ({
toBigNum(t.current_balance, decimals)
),
locked_amount: toBigNum(t.initial_balance, decimals).times(
lockedDecimal
clampedLockedDecimal
),
users: t.users,
};
@@ -54,22 +54,16 @@ export const TrancheItem = ({
{formatNumber(total, 2)}
</span>
</div>
<table className="w-full">
<tbody>
<tr>
<td>{t('Starts unlocking')}</td>
<td className="text-right">
{format(tranche.tranche_start, DATE_FORMAT_LONG)}
</td>
</tr>
<tr>
<td>{t('Fully unlocked')}</td>
<td className="text-right">
{format(tranche.tranche_end, DATE_FORMAT_LONG)}
</td>
</tr>
</tbody>
</table>
<div className="grid grid-cols-2 my-2">
<div>
<span>{t('Starts unlocking')}:</span>{' '}
<span>{format(tranche.tranche_start, DATE_FORMAT_LONG)}</span>
</div>
<div className="justify-self-end">
<span>{t('Fully unlocked')}:</span>{' '}
<span>{format(tranche.tranche_end, DATE_FORMAT_LONG)}</span>
</div>
</div>
<LockedProgress
locked={locked}
unlocked={unlocked}
@@ -216,7 +216,7 @@ export const ConsensusValidatorsTable = ({
: undefined,
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
? formatNumberPercentage(new BigNumber(userStakeShare), 2)
? formatNumberPercentage(new BigNumber(userStakeShare))
: undefined,
};
}
+33 -101
View File
@@ -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": "1979.64045368475",
"locked_amount": "5608.9383879726076446",
"total_removed": "1523.8177488329475",
"locked_amount": "6250.46875684799321663875",
"deposits": [
{
"amount": "188",
@@ -228,16 +228,6 @@
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
"tx": "0x2b3571c143ecebddf91fb62f402d516d51110edfe37b13200a6e5cf682dc5bb0"
},
{
"amount": "336.4580555509875",
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
"tx": "0x7af5942634e236f5f9c580f4ed042794ed309e83885f652a55ab37793ea2e85c"
},
{
"amount": "119.364649300815",
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
"tx": "0x47f5bf2c758c5270dd1b6519ac649ddabef8199f8a0bae319e36f8ac5c9c142e"
},
{
"amount": "202.093666077975",
"user": "0x1447Efc62d5077d7AbB20492dF831Dd6c9Eb1756",
@@ -372,12 +362,6 @@
"tranche_id": 56,
"tx": "0x2b3571c143ecebddf91fb62f402d516d51110edfe37b13200a6e5cf682dc5bb0"
},
{
"amount": "119.364649300815",
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
"tranche_id": 56,
"tx": "0x47f5bf2c758c5270dd1b6519ac649ddabef8199f8a0bae319e36f8ac5c9c142e"
},
{
"amount": "195.89040769089",
"user": "0xDd7a98557586ce21f770662319C2047C5a3bD605",
@@ -386,8 +370,8 @@
}
],
"total_tokens": "914.25",
"withdrawn_tokens": "624.076855605525",
"remaining_tokens": "290.173144394475"
"withdrawn_tokens": "504.71220630471",
"remaining_tokens": "409.53779369529"
},
{
"address": "0x9573BDF7FfC5519912d293e4D1f750eab2E471E7",
@@ -721,12 +705,6 @@
}
],
"withdrawals": [
{
"amount": "336.4580555509875",
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
"tranche_id": 56,
"tx": "0x7af5942634e236f5f9c580f4ed042794ed309e83885f652a55ab37793ea2e85c"
},
{
"amount": "422.1034736680125",
"user": "0xf915Da10e5136352Ba049acB0545Deb119054256",
@@ -735,8 +713,8 @@
}
],
"total_tokens": "1121.25",
"withdrawn_tokens": "758.561529219",
"remaining_tokens": "362.688470781"
"withdrawn_tokens": "422.1034736680125",
"remaining_tokens": "699.1465263319875"
},
{
"address": "0x237D23FcA6d7B2530C7614a9cB921CF27924911E",
@@ -899,7 +877,7 @@
"tranche_start": "2023-04-06T00:00:00.000Z",
"tranche_end": "2023-05-06T00:00:00.000Z",
"total_added": "14610",
"total_removed": "6675.45090157707",
"total_removed": "6141.45090157707",
"locked_amount": "0",
"deposits": [
{
@@ -1099,11 +1077,6 @@
"user": "0x9E53F72210479BF46aE7ABF77c48B0d611b3c3dB",
"tx": "0x1dbcf713b48965a82aa2e17cb3e7db9a491668d859d408a39c8a74b0ea860b6b"
},
{
"amount": "534",
"user": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
"tx": "0x5aa922056cad64f97a7dfa750da57d63abfdaea4499192d4d57958bfb4fba2ea"
},
{
"amount": "106.53500000286",
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
@@ -1227,17 +1200,10 @@
"tx": "0xf970ea0ce3e36fa0014d24bb830dd2ea0dbea06f6e53af486de5ee7e1c63e540"
}
],
"withdrawals": [
{
"amount": "534",
"user": "0x2586bA83696a92b5467Aaa0CF9EEC052F28F2c02",
"tranche_id": 54,
"tx": "0x5aa922056cad64f97a7dfa750da57d63abfdaea4499192d4d57958bfb4fba2ea"
}
],
"withdrawals": [],
"total_tokens": "534",
"withdrawn_tokens": "534",
"remaining_tokens": "0"
"withdrawn_tokens": "0",
"remaining_tokens": "534"
},
{
"address": "0xBf1AaB792D729fA125e6D7122D4b916a1E1C44B1",
@@ -1860,7 +1826,7 @@
"tranche_start": "2023-03-06T00:00:00.000Z",
"tranche_end": "2023-04-06T00:00:00.000Z",
"total_added": "14099",
"total_removed": "3785.49002352036",
"total_removed": "3722.49002352036",
"locked_amount": "0",
"deposits": [
{
@@ -2625,11 +2591,6 @@
}
],
"withdrawals": [
{
"amount": "63",
"user": "0x2a65Ae527C6Ff4665e048B0E0883c486A7BA4DBc",
"tx": "0xb0edcc25e422bc3db3ad8027dfdfc0928abd7a8af8957a1699e4cf2dc8cee8f7"
},
{
"amount": "30",
"user": "0xBe9F912Ad481C61B653463E8F1D2b2b310D49861",
@@ -3668,17 +3629,10 @@
"tx": "0xd4a269b070cbaaff7e29a99f6b3997117d765f69512bb760e28bade799fcbba4"
}
],
"withdrawals": [
{
"amount": "63",
"user": "0x2a65Ae527C6Ff4665e048B0E0883c486A7BA4DBc",
"tranche_id": 53,
"tx": "0xb0edcc25e422bc3db3ad8027dfdfc0928abd7a8af8957a1699e4cf2dc8cee8f7"
}
],
"withdrawals": [],
"total_tokens": "63",
"withdrawn_tokens": "63",
"remaining_tokens": "0"
"withdrawn_tokens": "0",
"remaining_tokens": "63"
},
{
"address": "0xc3B1eB0feE837Db0A3ded5bf16A050726955195B",
@@ -4907,7 +4861,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "49327.3172923405910546417",
"locked_amount": "49564.8059208238813573587",
"deposits": [
{
"amount": "86666.297",
@@ -4973,7 +4927,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "284.955770502645375",
"locked_amount": "298.694736975987075",
"deposits": [
{
"amount": "2500",
@@ -5006,7 +4960,7 @@
"tranche_end": "2023-11-01T00:00:00.000Z",
"total_added": "15000.000000000000015",
"total_removed": "0",
"locked_amount": "14163.9766379830905141639766379830905",
"locked_amount": "14245.5144172705305142455144172705305",
"deposits": [
{
"amount": "1.5e-14",
@@ -5094,7 +5048,7 @@
"tranche_end": "2023-09-01T00:00:00.000Z",
"total_added": "17500",
"total_removed": "0",
"locked_amount": "10723.00897619766325",
"locked_amount": "10818.136385366345",
"deposits": [
{
"amount": "12500",
@@ -5360,8 +5314,8 @@
"tranche_start": "2023-02-01T00:00:00.000Z",
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "18302.01762945",
"locked_amount": "16936.07322360343725",
"total_removed": "18077.0118744",
"locked_amount": "17143.2963090853275",
"deposits": [
{
"amount": "7500",
@@ -5375,11 +5329,6 @@
}
],
"withdrawals": [
{
"amount": "225.00575505",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x0a46fa3cc0154727ae4c7abca97bd7e26e6835018f96cf3a3860a9fcc791b9a4"
},
{
"amount": "164.727209925",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -5558,12 +5507,6 @@
}
],
"withdrawals": [
{
"amount": "225.00575505",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 34,
"tx": "0x0a46fa3cc0154727ae4c7abca97bd7e26e6835018f96cf3a3860a9fcc791b9a4"
},
{
"amount": "164.727209925",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -5746,8 +5689,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "4092.25895865",
"remaining_tokens": "3407.74104135"
"withdrawn_tokens": "3867.2532036",
"remaining_tokens": "3632.7467964"
},
{
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
@@ -5791,7 +5734,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "49282.312321912380332265",
"locked_amount": "49519.584271904149936995",
"deposits": [
{
"amount": "129999.45",
@@ -5824,7 +5767,7 @@
"tranche_end": "2024-04-01T00:00:00.000Z",
"total_added": "54144.7663",
"total_removed": "0",
"locked_amount": "48189.54915726316779685587",
"locked_amount": "48337.51478508860464254277",
"deposits": [
{
"amount": "54144.7663",
@@ -5857,7 +5800,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "19679.51570903094634",
"locked_amount": "19851.056303906648696",
"deposits": [
{
"amount": "10000",
@@ -6050,7 +5993,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "1763.627124556063",
"locked_amount": "1777.32845002536775",
"deposits": [
{
"amount": "5000",
@@ -7119,7 +7062,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "1709370.7872515768348",
"locked_amount": "115570.728817751808474185",
"locked_amount": "120886.6468420560097536622",
"deposits": [
{
"amount": "1852091.69",
@@ -40991,7 +40934,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "715655.108029600523393",
"locked_amount": "202093.2974680882797136777084",
"locked_amount": "210262.01266536406332744534",
"deposits": [
{
"amount": "1998.95815",
@@ -42383,8 +42326,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "873375.18460711694221852",
"locked_amount": "6016297.4428328087189762607645230040375777",
"total_removed": "872635.89843522227071852",
"locked_amount": "6045263.1824407030758288827453507606055691",
"deposits": [
{
"amount": "16249.93",
@@ -42898,11 +42841,6 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x4301fcb38d16c1f6aa4a93a18a277a4e77eb3b4565bf98ca6218f6598b78b90b"
},
{
"amount": "739.2861718946715",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x989d22d5ba66353cb132b5643100a99e78133e5d40aef1ac91ed6ab0a2290299"
},
{
"amount": "10150.87581603206683",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -45083,12 +45021,6 @@
"tranche_id": 2,
"tx": "0x4301fcb38d16c1f6aa4a93a18a277a4e77eb3b4565bf98ca6218f6598b78b90b"
},
{
"amount": "739.2861718946715",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x989d22d5ba66353cb132b5643100a99e78133e5d40aef1ac91ed6ab0a2290299"
},
{
"amount": "913.910324501590625",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -46645,8 +46577,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "161285.6283934563635",
"remaining_tokens": "98713.2591065436365"
"withdrawn_tokens": "160546.342221561692",
"remaining_tokens": "99452.545278438308"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -59171,7 +59103,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "44544.1737890903416",
"locked_amount": "32022.838198356764871382775241",
"locked_amount": "33317.21781573185824187773769658",
"deposits": [
{
"amount": "3000",
@@ -6,7 +6,6 @@ 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(() => {
@@ -182,20 +181,9 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
cy.getByTestId(marketTitle).contains('Oracle').click();
cy.getByTestId(accordionContent)
.getByTestId(providerName)
.getByTestId('provider-name')
.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,10 +16,6 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
cy.wait('@Markets');
});
beforeEach(() => {
cy.mockTradingPage();
});
describe('limit order', () => {
before(() => {
cy.getByTestId(toggleLimit).click();
@@ -102,7 +98,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,6 +1,10 @@
import * as Schema from '@vegaprotocol/types';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { accountsQuery, amendGeneralAccountBalance } from '@vegaprotocol/mock';
import {
accountsQuery,
amendGeneralAccountBalance,
estimateOrderQuery,
} from '@vegaprotocol/mock';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { createOrder } from '../support/create-order';
@@ -40,10 +44,13 @@ describe(
cy.setVegaWallet();
cy.mockTradingPage();
const accounts = accountsQuery();
amendGeneralAccountBalance(accounts, 'market-0', '1');
amendGeneralAccountBalance(accounts, 'market-0', '100000000');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockGQL((req) => {
aliasGQLQuery(req, 'EstimateOrder', estimateOrderQuery());
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
@@ -59,7 +66,7 @@ describe(
);
cy.getByTestId('dealticket-warning-margin').should(
'contain.text',
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
'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.'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
cy.getByTestId('dialog-content')
@@ -17,6 +17,7 @@ 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();
@@ -29,6 +30,7 @@ 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();
+2 -12
View File
@@ -10,7 +10,7 @@ import {
chainIdQuery,
chartQuery,
depositsQuery,
estimateFeesQuery,
estimateOrderQuery,
marginsQuery,
marketCandlesQuery,
marketDataQuery,
@@ -22,14 +22,11 @@ import {
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';
@@ -160,16 +157,9 @@ const mockTradingPage = (
aliasGQLQuery(req, 'Candles', candlesQuery());
aliasGQLQuery(req, 'Withdrawals', withdrawalsQuery());
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
aliasGQLQuery(req, 'EstimateFees', estimateFeesQuery());
aliasGQLQuery(req, 'EstimatePosition', estimatePositionQuery());
aliasGQLQuery(req, 'EstimateOrder', estimateOrderQuery());
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
@@ -33,9 +33,9 @@ export const useAccountBalance = (assetId?: string) => {
return useMemo(
() => ({
accountBalance: pubKey ? accountBalance : '',
accountDecimals: pubKey ? accountDecimals : null,
accountBalance,
accountDecimals,
}),
[accountBalance, accountDecimals, pubKey]
[accountBalance, accountDecimals]
);
};
@@ -32,9 +32,9 @@ export const useMarketAccountBalance = (marketId: string) => {
return useMemo(
() => ({
accountBalance: pubKey ? accountBalance : '',
accountDecimals: pubKey ? accountDecimals : null,
accountBalance,
accountDecimals,
}),
[accountBalance, accountDecimals, pubKey]
[accountBalance, accountDecimals]
);
};
@@ -1,280 +0,0 @@
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,
});
});
});
});
+3 -88
View File
@@ -1,11 +1,4 @@
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';
@@ -160,6 +153,7 @@ export class VegaDataSource implements DataSource {
},
fetchPolicy: 'no-cache',
});
if (data?.market?.candlesConnection?.edges) {
const decimalPlaces = data.market.decimalPlaces;
const positionDecimalPlaces = data.market.positionDecimalPlaces;
@@ -169,8 +163,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 [];
@@ -219,85 +213,6 @@ 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,
-3
View File
@@ -23,8 +23,5 @@ 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';
+1 -6
View File
@@ -17,12 +17,7 @@ const hasOperationName = (
operationName: string
) => {
const { body } = req;
return (
typeof body === 'object' &&
body !== null &&
'operationName' in body &&
body.operationName === operationName
);
return 'operationName' in body && body.operationName === operationName;
};
export function addMockGQLCommand() {
+2 -6
View File
@@ -20,12 +20,8 @@ const mockSocketServer = Cypress.env('VEGA_URL')
: null;
// DO NOT REMOVE: PASSTHROUGH for walletconnect
new Server('wss://relay.walletconnect.com', {
mock: false,
});
// DO NOT REMOVE: PASSTHROUGH for hot module reload
new Server('ws://localhost:4200/_next/webpack-hmr', {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const relayServer = new Server('wss://relay.walletconnect.com', {
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(
@@ -0,0 +1,132 @@
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,8 +1,24 @@
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import classnames from 'classnames';
import type { ReactNode } from 'react';
import { getFeeDetailsValues } from '../../hooks/use-fee-deal-ticket-details';
import type { FeeDetails } from '../../hooks/use-fee-deal-ticket-details';
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;
}
export interface DealTicketFeeDetailProps {
label: string;
@@ -29,8 +45,17 @@ export const DealTicketFeeDetail = ({
</div>
);
export const DealTicketFeeDetails = (props: FeeDetails) => {
const details = getFeeDetailsValues(props);
export const DealTicketFeeDetails = ({
order,
market,
marketData,
...args
}: DealTicketFeeDetailsProps) => {
const feeDetails = useFeeDealTicketDetails(order, market, marketData);
const details = getFeeDetailsValues({
...feeDetails,
...args,
});
return (
<div>
{details.map(({ label, value, labelDescription, symbol, indent }) => (
@@ -25,16 +25,6 @@ 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,
@@ -44,6 +34,7 @@ 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 {
@@ -113,67 +104,7 @@ export const DealTicket = ({
market.positionDecimalPlaces
);
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 { margin, totalMargin } = useInitialMargin(market.id, normalizedOrder);
const { data: currentMargins } = useDataProvider({
dataProvider: marketMarginDataProvider,
@@ -470,10 +401,7 @@ export const DealTicket = ({
asset={asset}
marketTradingMode={marketData.marketTradingMode}
balance={balance}
margin={
positionEstimate?.estimatePosition?.margin.bestCase.initialLevel ||
'0'
}
margin={totalMargin}
isReadOnly={isReadOnly}
pubKey={pubKey}
onClickCollateral={onClickCollateral}
@@ -485,15 +413,15 @@ export const DealTicket = ({
}
/>
<DealTicketFeeDetails
feeEstimate={feeEstimate}
notionalSize={notionalSize}
assetSymbol={assetSymbol}
marginAccountBalance={marginAccountBalance}
generalAccountBalance={generalAccountBalance}
positionEstimate={positionEstimate?.estimatePosition}
order={normalizedOrder}
market={market}
marketData={marketData}
estimatedInitialMargin={margin}
estimatedTotalInitialMargin={totalMargin}
currentInitialMargin={currentMargins?.initialLevel}
currentMaintenanceMargin={currentMargins?.maintenanceLevel}
marginAccountBalance={marginAccountBalance}
generalAccountBalance={generalAccountBalance}
/>
</form>
</TinyScroll>
+1
View File
@@ -1,3 +1,4 @@
export * from './deal-ticket';
export * from './deal-ticket-validation';
export * from './trading-mode-tooltip';
export * from './deal-ticket-estimates';
-4
View File
@@ -59,10 +59,6 @@ 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 EstimateFees(
query EstimateOrder(
$marketId: ID!
$partyId: ID!
$price: String
@@ -8,7 +8,7 @@ query EstimateFees(
$expiration: Timestamp
$type: OrderType!
) {
estimateFees(
estimateOrder(
marketId: $marketId
partyId: $partyId
price: $price
@@ -18,11 +18,14 @@ query EstimateFees(
expiration: $expiration
type: $type
) {
fees {
fee {
makerFee
infrastructureFee
liquidityFee
}
marginLevels {
initialLevel
}
totalFeeAmount
}
}
+20 -17
View File
@@ -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 EstimateFeesQueryVariables = Types.Exact<{
export type EstimateOrderQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
partyId: Types.Scalars['ID'];
price?: Types.InputMaybe<Types.Scalars['String']>;
@@ -15,12 +15,12 @@ export type EstimateFeesQueryVariables = Types.Exact<{
}>;
export type EstimateFeesQuery = { __typename?: 'Query', estimateFees: { __typename?: 'FeeEstimate', totalFeeAmount: string, fees: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string } } };
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 const EstimateFeesDocument = gql`
query EstimateFees($marketId: ID!, $partyId: ID!, $price: String, $size: String!, $side: Side!, $timeInForce: OrderTimeInForce!, $expiration: Timestamp, $type: OrderType!) {
estimateFees(
export const EstimateOrderDocument = gql`
query EstimateOrder($marketId: ID!, $partyId: ID!, $price: String, $size: String!, $side: Side!, $timeInForce: OrderTimeInForce!, $expiration: Timestamp, $type: OrderType!) {
estimateOrder(
marketId: $marketId
partyId: $partyId
price: $price
@@ -30,27 +30,30 @@ export const EstimateFeesDocument = gql`
expiration: $expiration
type: $type
) {
fees {
fee {
makerFee
infrastructureFee
liquidityFee
}
marginLevels {
initialLevel
}
totalFeeAmount
}
}
`;
/**
* __useEstimateFeesQuery__
* __useEstimateOrderQuery__
*
* 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
* 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
* 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 } = useEstimateFeesQuery({
* const { data, loading, error } = useEstimateOrderQuery({
* variables: {
* marketId: // value for 'marketId'
* partyId: // value for 'partyId'
@@ -63,14 +66,14 @@ export const EstimateFeesDocument = gql`
* },
* });
*/
export function useEstimateFeesQuery(baseOptions: Apollo.QueryHookOptions<EstimateFeesQuery, EstimateFeesQueryVariables>) {
export function useEstimateOrderQuery(baseOptions: Apollo.QueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<EstimateFeesQuery, EstimateFeesQueryVariables>(EstimateFeesDocument, options);
return Apollo.useQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
}
export function useEstimateFeesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimateFeesQuery, EstimateFeesQueryVariables>) {
export function useEstimateOrderLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EstimateOrderQuery, EstimateOrderQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<EstimateFeesQuery, EstimateFeesQueryVariables>(EstimateFeesDocument, options);
return Apollo.useLazyQuery<EstimateOrderQuery, EstimateOrderQueryVariables>(EstimateOrderDocument, options);
}
export type EstimateFeesQueryHookResult = ReturnType<typeof useEstimateFeesQuery>;
export type EstimateFeesLazyQueryHookResult = ReturnType<typeof useEstimateFeesLazyQuery>;
export type EstimateFeesQueryResult = Apollo.QueryResult<EstimateFeesQuery, EstimateFeesQueryVariables>;
export type EstimateOrderQueryHookResult = ReturnType<typeof useEstimateOrderQuery>;
export type EstimateOrderLazyQueryHookResult = ReturnType<typeof useEstimateOrderLazyQuery>;
export type EstimateOrderQueryResult = Apollo.QueryResult<EstimateOrderQuery, EstimateOrderQueryVariables>;
@@ -1,20 +1,21 @@
import type { PartialDeep } from 'type-fest';
import merge from 'lodash/merge';
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
import type { EstimateOrderQuery } from './__generated__/EstimateOrder';
export const estimateFeesQuery = (
override?: PartialDeep<EstimateFeesQuery>
): EstimateFeesQuery => {
const defaultResult: EstimateFeesQuery = {
estimateFees: {
__typename: 'FeeEstimate',
export const estimateOrderQuery = (
override?: PartialDeep<EstimateOrderQuery>
): EstimateOrderQuery => {
const defaultResult: EstimateOrderQuery = {
estimateOrder: {
__typename: 'OrderEstimate',
totalFeeAmount: '0.0006',
fees: {
fee: {
__typename: 'TradeFee',
makerFee: '100000',
infrastructureFee: '100000',
liquidityFee: '100000',
},
marginLevels: { __typename: 'MarginLevels', initialLevel: '1' },
},
};
return merge(defaultResult, override);
@@ -1,9 +1,14 @@
import { FeesBreakdown } from '@vegaprotocol/market-info';
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
import {
addDecimal,
addDecimalsFormatNumber,
formatNumber,
toBigNum,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useVegaWallet } from '@vegaprotocol/wallet';
import type { Market } from '@vegaprotocol/market-list';
import type { EstimatePositionQuery } from '@vegaprotocol/positions';
import { useMemo } from 'react';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import {
EST_TOTAL_MARGIN_TOOLTIP_TEXT,
@@ -12,30 +17,58 @@ 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';
import { useEstimateFeesQuery } from './__generated__/EstimateOrder';
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
export const useEstimateFees = (
order?: OrderSubmissionBody['orderSubmission']
export const useFeeDealTicketDetails = (
order: OrderSubmissionBody['orderSubmission'],
market: Market,
marketData: MarketData
) => {
const { pubKey } = useVegaWallet();
const { accountBalance } = useMarketAccountBalance(market.id);
const { data } = useEstimateFeesQuery({
variables: order && {
marketId: order.marketId,
const price = useMemo(() => {
return getDerivedPrice(order, marketData);
}, [order, marketData]);
const { data: estMargin } = useEstimateOrderQuery({
variables: {
marketId: market.id,
partyId: pubKey || '',
price: order.price,
price,
size: order.size,
side: order.side,
timeInForce: order.timeInForce,
type: order.type,
},
skip: !pubKey || !order?.size || !order?.price,
skip: !pubKey || !market || !order.size || !price,
});
return data?.estimateFees;
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]);
};
export interface FeeDetails {
@@ -44,54 +77,42 @@ export interface FeeDetails {
market: Market;
assetSymbol: string;
notionalSize: string | null;
feeEstimate: EstimateFeesQuery['estimateFees'] | undefined;
estimateOrder: EstimateOrderQuery['estimateOrder'] | undefined;
estimatedInitialMargin: string;
estimatedTotalInitialMargin: string;
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,
feeEstimate,
estimateOrder,
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;
@@ -101,15 +122,15 @@ export const getFeeDetailsValues = ({
}[] = [
{
label: t('Notional'),
value: formatValue(notionalSize, assetDecimals),
value: formatValueWithMarketDp(notionalSize),
symbol: assetSymbol,
labelDescription: NOTIONAL_SIZE_TOOLTIP_TEXT(assetSymbol),
},
{
label: t('Fees'),
value:
feeEstimate?.totalFeeAmount &&
`~${formatValue(feeEstimate?.totalFeeAmount, assetDecimals)}`,
estimateOrder?.totalFeeAmount &&
`~${formatValueWithAssetDp(estimateOrder?.totalFeeAmount)}`,
labelDescription: (
<>
<span>
@@ -118,7 +139,7 @@ export const getFeeDetailsValues = ({
)}
</span>
<FeesBreakdown
fees={feeEstimate?.fees}
fees={estimateOrder?.fee}
feeFactors={market.fees.factors}
symbol={assetSymbol}
decimals={assetDecimals}
@@ -127,147 +148,66 @@ 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),
},
];
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);
if (totalBalance) {
const totalMarginAvailable = (
currentMaintenanceMargin
? totalBalance - BigInt(currentMaintenanceMargin)
: totalBalance
).toString();
details.push({
indent: true,
label: t('Deduction from collateral'),
value: formatRange(
deductionFromCollateralBestCase > 0
? deductionFromCollateralBestCase.toString()
: '0',
deductionFromCollateralWorstCase > 0
? deductionFromCollateralWorstCase.toString()
: '0',
assetDecimals
),
label: t('Total margin available'),
value: `~${formatValueWithAssetDp(totalMarginAvailable)}`,
symbol: assetSymbol,
labelDescription: DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT(assetSymbol),
labelDescription: TOTAL_MARGIN_AVAILABLE(
formatValueWithAssetDp(generalAccountBalance),
formatValueWithAssetDp(marginAccountBalance),
formatValueWithAssetDp(currentMaintenanceMargin),
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: formatRange(
marginEstimate?.bestCase.initialLevel,
marginEstimate?.worstCase.initialLevel,
assetDecimals
),
value: `~${formatValueWithAssetDp(estimatedTotalInitialMargin)}`,
symbol: assetSymbol,
labelDescription: EST_TOTAL_MARGIN_TOOLTIP_TEXT,
});
}
details.push({
label: t('Current margin allocation'),
value: formatValue(marginAccountBalance, assetDecimals),
value: `${formatValueWithAssetDp(marginAccountBalance)}`,
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;
};
@@ -0,0 +1,74 @@
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]
);
};
@@ -38,7 +38,7 @@ export const useNodeHealth = () => {
return;
}
if (!('Cypress' in window) && window.location.hostname !== 'localhost') {
if (!('Cypress' in window)) {
startPolling(POLL_INTERVAL);
}
}, [error, startPolling, stopPolling]);
+1
View File
@@ -2,6 +2,7 @@ import { Fragment } from 'react';
import { t } from '@vegaprotocol/i18n';
import { Link, Lozenge } from '@vegaprotocol/ui-toolkit';
import {
NodeSwitcherDialog,
useEnvironment,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
+1
View File
@@ -2,6 +2,7 @@ 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';
-41
View File
@@ -75,44 +75,3 @@ 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
}
}
}
}
+1 -79
View File
@@ -35,16 +35,6 @@ 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
@@ -230,72 +220,4 @@ 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 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>;
export type MarginsSubscriptionSubscriptionResult = Apollo.SubscriptionResult<MarginsSubscriptionSubscription>;
@@ -1,40 +0,0 @@
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);
};
@@ -0,0 +1,95 @@
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,6 +5,7 @@ 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,
@@ -27,6 +28,14 @@ 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<
@@ -327,3 +336,98 @@ 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(),
};
}
);
@@ -1,17 +0,0 @@
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);
};
@@ -1,13 +0,0 @@
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);
};
@@ -1,30 +1,19 @@
import { useMemo, useEffect } from 'react';
import { useMemo } 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, 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 { data, loading, error } = useProtocolUpgradeProposalsQuery({
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
inState:
Schema.ProtocolUpgradeProposalStatus
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
},
});
const nextUpgrades = useMemo(() => {
if (!data) return [];
@@ -8,29 +8,20 @@ const durations = [] as number[];
const useAverageBlockDuration = (polls = DEFAULT_POLLS) => {
const [avg, setAvg] = useState<number | undefined>(undefined);
const { data, startPolling, stopPolling, error } = useBlockStatisticsQuery({
const { data } = useBlockStatisticsQuery({
pollInterval: INTERVAL,
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]);
+2 -4
View File
@@ -32,10 +32,8 @@ export function addDecimal(
return toBigNum(value, decimals).toFixed(decimalPrecision);
}
export function removeDecimal(
value: string | BigNumber,
decimals: number
): string {
export function removeDecimal(value: string, decimals: number): string {
if (!decimals) return value;
return new BigNumber(value || 0).times(Math.pow(10, decimals)).toFixed(0);
}