Compare commits

..
91 changed files with 1328 additions and 2266 deletions
+4 -6
View File
@@ -62,19 +62,17 @@ jobs:
- name: Define variables
run: |
envName=''
domain="vega.rocks"
if [[ "${{ github.event_name }}" = "push" ]]; then
domain="vega.rocks"
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
domain="vega.community"
fi
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
fi
bucketName="${{ matrix.app }}.${envName}.${domain}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
-1
View File
@@ -14,7 +14,6 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
# App flags
NX_EXPLORER_ASSETS=1
@@ -48,13 +48,11 @@ export const Footer = () => {
</Link>
</div>
{ENV.addresses.feedback ? (
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</div>
) : null}
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</div>
</div>
</footer>
);
@@ -64,5 +62,9 @@ const NodeUrl = ({ url }: { url: string }) => {
// get base url from api url, api sub domain
const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
return <span className="cursor-default">{nodeUrl}</span>;
return (
<Link href={'https://' + nodeUrl} target="_blank">
{nodeUrl}
</Link>
);
};
@@ -20,10 +20,12 @@ import isEqual from 'lodash/isEqual';
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
if (!market) return null;
const settlementData = market.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
const terminationData = market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
const settlementData =
market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData
.data;
const terminationData =
market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data;
const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
@@ -1,56 +0,0 @@
import type { Proposal } from './tx-proposal';
import { proposalRequiresSignatureBundle } from './tx-proposal';
describe('proposalRequiresSignatureBundle', () => {
it('should return false for freeform proposals, which do not require a signature bundle to enact', () => {
const mock = {
terms: {
newFreeform: {},
},
};
expect(proposalRequiresSignatureBundle(mock)).toEqual(false);
});
it('should return false for newMarket proposals, which do not require a signature bundle to enact', () => {
const mock = {
terms: {
newMarket: {},
},
};
expect(proposalRequiresSignatureBundle(mock)).toEqual(false);
});
it('should return true for newAsset proposals, which do require a signature bundle to enact', () => {
const mock = {
terms: {
newAsset: {},
},
};
expect(proposalRequiresSignatureBundle(mock)).toEqual(true);
});
it('should return true for updateAsset proposals, which do require a signature bundle to enact', () => {
const mock = {
terms: {
updateAsset: {},
},
};
expect(proposalRequiresSignatureBundle(mock)).toEqual(true);
});
it('should return false when bad data is supplied', () => {
expect(
proposalRequiresSignatureBundle(false as unknown as Proposal)
).toEqual(false);
expect(
proposalRequiresSignatureBundle(undefined as unknown as Proposal)
).toEqual(false);
expect(
proposalRequiresSignatureBundle({ test: false } as unknown as Proposal)
).toEqual(false);
});
});
@@ -28,16 +28,11 @@ interface TxProposalProps {
* @returns boolean True if a signature bundle is required. Used to fetch a signature bundle
*/
export function proposalRequiresSignatureBundle(proposal?: Proposal): boolean {
const proposalsThatRequireBundles = ['newAsset', 'updateAsset'];
if (!proposal?.terms) {
return false;
}
return (
proposalsThatRequireBundles.filter((requiredIfExists) =>
has(proposal.terms, requiredIfExists)
).length > 0
return !!['newAsset', 'updateAsset'].filter((requiredIfExists) =>
has(proposal.terms, requiredIfExists)
);
}
@@ -86,7 +81,6 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
const tx = proposal.terms?.newAsset || proposal.terms?.updateAsset;
// This component is not rendered if no bundle is required
const SignatureBundleComponent = proposal.terms?.newAsset
? ProposalSignatureBundleNewAsset
: ProposalSignatureBundleUpdateAsset;
-1
View File
@@ -17,7 +17,6 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
@@ -1,113 +0,0 @@
{
"rationale": {
"title": "New Market Proposal E2E submission",
"description": "E2E new market proposal"
},
"terms": {
"newMarket": {
"changes": {
"decimalPlaces": "5",
"positionDecimalPlaces": "5",
"linearSlippageFactor": "0.001",
"quadraticSlippageFactor": "0",
"lpPriceRange": "10",
"instrument": {
"name": "Token test market",
"code": "TEST.24h",
"future": {
"settlementAsset": "73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0",
"quoteName": "fBTC",
"dataSourceSpecForSettlementData": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "prices.ETH.value",
"type": "TYPE_INTEGER",
"numberDecimalPlaces": "0"
},
"conditions": [
{
"operator": "OPERATOR_GREATER_THAN",
"value": "0"
}
]
}
]
}
}
},
"dataSourceSpecForTradingTermination": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "trading.terminated.ETH5",
"type": "TYPE_BOOLEAN"
},
"conditions": [
{
"operator": "OPERATOR_EQUALS",
"value": "true"
}
]
}
]
}
}
},
"dataSourceSpecBinding": {
"settlementDataProperty": "prices.ETH.value",
"tradingTerminationProperty": "trading.terminated.ETH5"
}
}
},
"metadata": ["sector:energy", "sector:tech", "source:docs.vega.xyz"],
"priceMonitoringParameters": {
"triggers": [
{
"horizon": "43200",
"probability": "0.9999999",
"auctionExtension": "600"
}
]
},
"liquidityMonitoringParameters": {
"targetStakeParameters": {
"timeWindow": "3600",
"scalingFactor": 10
},
"triggeringRatio": "0.7",
"auctionExtension": "1"
},
"logNormal": {
"tau": 0.0001140771161,
"riskAversionParameter": 0.01,
"params": {
"mu": 0,
"r": 0.016,
"sigma": 0.5
}
}
}
},
"closingTimestamp": 0,
"enactmentTimestamp": 0
}
}
@@ -7,12 +7,17 @@ import {
import {
createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays,
enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle,
getDateFormatForSpecifiedDays,
getProposalFromTitle,
getProposalIdFromList,
getProposalInformationFromTable,
submitUniqueRawProposal,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
} from '../../../../governance-e2e/src/support/governance.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governance-e2e/src/support/staking.functions';
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
@@ -33,8 +38,6 @@ const proposalDetailsTitle = '[data-testid="proposal-title"]';
const proposalDetailsDescription = '[data-testid="proposal-description"]';
const openProposals = '[data-testid="open-proposals"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-terms-toggle';
describe(
'Governance flow for proposal details',
@@ -59,25 +62,23 @@ describe(
// 3001-VOTE-050 3001-VOTE-054 3001-VOTE-055 3002-PROP-019
it('Newly created raw proposal details - shows proposal title and full description', function () {
const proposalDescription =
'I propose that everyone evaluate the following IPFS document and vote Yes if they agree. bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si';
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
cy.get(openProposals).within(() => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.get(viewProposalButton).should('be.visible').click();
getProposalIdFromList(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => {
cy.get(`#${proposalId}`).within(() => {
cy.get(viewProposalButton).should('be.visible').click();
});
});
});
cy.get(proposalDetailsTitle).should(
'contain.text',
rawProposal.rationale.title
);
cy.get(proposalDetailsTitle)
.should('contain', rawProposal.rationale.title)
.and('be.visible');
cy.get(proposalDetailsDescription)
.find('p')
.should('have.text', proposalDescription);
.should('contain', rawProposal.rationale.description)
.and('be.visible');
});
cy.getByTestId(proposalTermsToggle).click();
// 3001-VOTE-052
cy.get('code.language-json')
.should('exist')
@@ -88,36 +89,33 @@ describe(
// 3001-VOTE-043
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
const closingVoteHrs = '72';
const proposalTitle = generateFreeFormProposalTitle();
const proposalTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
// const currentDate = new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
// const proposedDate = new Date(currentDate.getTime() + 60000)
submitUniqueRawProposal({
proposalTitle: proposalTitle,
closingTimestamp: proposalTimeStamp,
});
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody(closingVoteHrs, proposalTitle);
waitForProposalSubmitted();
waitForProposalSync();
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
);
cy.wrap(
formatDateWithLocalTimezone(new Date(proposalTimeStamp * 1000))
).then((closingDate) => {
getProposalInformationFromTable('Closes on').should(
'have.text',
closingDate
);
getProposalInformationFromTable('Closes on')
.contains(closingDate)
.should('be.visible');
});
cy.wrap(
formatDateWithLocalTimezone(
new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
)
).then((proposalDate) => {
getProposalInformationFromTable('Proposed on').should(
'have.text',
proposalDate
);
getProposalInformationFromTable('Proposed on')
.contains(proposalDate)
.should('be.visible');
});
});
@@ -127,14 +125,13 @@ describe(
// 3001-VOTE-067
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
'be.visible'
);
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Expected to pass')
.contains('👎')
.should('be.visible');
@@ -154,9 +151,9 @@ describe(
it('Newly created proposal details - ability to vote for and against proposal - with minimum required tokens associated', function () {
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-080
cy.getByTestId('vote-buttons').contains('against').should('be.visible');
@@ -182,7 +179,6 @@ describe(
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
@@ -224,15 +220,14 @@ describe(
vegaWalletSetSpecifiedApprovalAmount('1000');
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
voteForProposal('for');
// 3001-VOTE-079
cy.contains('You voted: For').should('be.visible');
cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total Supply')
.invoke('text')
.then((totalSupply) => {
@@ -240,15 +235,14 @@ describe(
(Number(totalSupply.replace(/,/g, '')) * 0.001) /
100
).toFixed(2);
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated(
tokensRequiredToAchieveResult
);
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalVoteProgressForPercentage)
.contains('100.00%')
@@ -265,7 +259,6 @@ describe(
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total tokens voted percentage')
.should('have.text', '0.00%')
.and('be.visible');
@@ -58,12 +58,14 @@ context(
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.get(proposalStatus).should('have.text', 'Enacted');
cy.get(proposalStatus).should('have.text', 'Enacted ');
cy.get(viewProposalButton).click();
});
});
cy.getByTestId('proposal-type').should('have.text', 'New market');
cy.get(proposalStatus).should('have.text', 'Enacted');
getProposalInformationFromTable('State')
.contains('Enacted')
.and('be.visible');
cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
@@ -85,10 +87,16 @@ context(
.last()
.within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalStatus).should('have.text', 'Open');
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
voteForProposal('for');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Passed');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
getProposalInformationFromTable('State') // 3001-VOTE-047
.contains('Passed', proposalTimeout)
.and('be.visible');
getProposalInformationFromTable('State')
.contains('Enacted', proposalTimeout)
.and('be.visible');
cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
@@ -113,9 +121,13 @@ context(
.last()
.within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalStatus).should('have.text', 'Open');
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
voteForProposal('for');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
getProposalInformationFromTable('State')
.contains('Enacted', proposalTimeout)
.and('be.visible');
});
// 3001-VOTE-048 3001-VOTE-049 3001-VOTE-050
@@ -132,8 +144,12 @@ context(
.last()
.within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalStatus).should('have.text', 'Open');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Declined');
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
getProposalInformationFromTable('State') // 3001-VOTE-047
.contains('Declined', proposalTimeout)
.and('be.visible');
getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_PARTICIPATION_THRESHOLD_NOT_REACHED')
.and('be.visible');
@@ -5,11 +5,10 @@ import {
enterRawProposalBody,
enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle,
getProposalFromTitle,
getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
submitUniqueRawProposal,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
@@ -82,6 +81,7 @@ context(
});
vegaWalletSetSpecifiedApprovalAmount('1000');
cy.associateTokensToVegaWallet('1');
});
beforeEach('visit governance tab', function () {
@@ -95,8 +95,7 @@ context(
navigateTo(navigation.proposals);
});
// Test can only pass if run before other proposal tests.
it.skip('Should be able to see that no proposals exist', function () {
it('Should be able to see that no proposals exist', function () {
// 3001-VOTE-003
cy.get(noOpenProposals)
.should('be.visible')
@@ -108,7 +107,7 @@ context(
// 3002-PROP-002
// 3002-PROP-003
it('Proposal form - shows how many vega tokens are required to make a proposal', function () {
it('Submit a proposal form - shows how many vega tokens are required to make a proposal', function () {
// 3002-PROP-005
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.contains(
@@ -116,9 +115,8 @@ context(
).should('be.visible');
});
// Skipping as currently unable to propose using forms other than raw
// 3002-PROP-011
it.skip('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
cy.get(minVoteButton).should('be.visible'); // 3002-PROP-008
cy.get(maxVoteButton).should('be.visible');
@@ -142,13 +140,16 @@ context(
closeStakingDialog();
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
createRawProposal();
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', generateFreeFormProposalTitle());
waitForProposalSubmitted();
});
it.skip('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
@@ -157,7 +158,7 @@ context(
.should('equal', 'Value must be greater than or equal to 1.');
});
it.skip('Creating a proposal - proposal rejected - when closing time later than system default', function () {
it('Creating a proposal - proposal rejected - when closing time later than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody(
'100000',
@@ -184,13 +185,17 @@ context(
navigateTo(navigation.proposals);
cy.get(rejectProposalsLink).click();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => {
cy.contains('Rejected').should('be.visible');
cy.contains('Close time too late').should('be.visible');
cy.get(viewProposalButton).click();
});
});
cy.getByTestId('proposal-status').should('have.text', 'Rejected');
getProposalInformationFromTable('State')
.contains('Rejected')
.and('be.visible');
getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE')
.and('be.visible');
@@ -285,19 +290,18 @@ context(
const proposalTitle = generateFreeFormProposalTitle();
ensureSpecifiedUnstakedTokensAreAssociated('1');
submitUniqueRawProposal({ proposalTitle: proposalTitle });
ethereumWalletConnect();
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', proposalTitle);
waitForProposalSubmitted();
stakingPageDisassociateTokens('0.0001');
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
});
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
);
cy.contains('Vote breakdown').should('be.visible', {
@@ -315,9 +319,9 @@ context(
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="disconnect"]').click();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-075
// 3001-VOTE-076
@@ -8,7 +8,6 @@ import {
import {
getProposalInformationFromTable,
goToMakeNewProposal,
governanceProposalType,
voteForProposal,
waitForProposalSubmitted,
} from '../../support/governance.functions';
@@ -57,8 +56,18 @@ const fUSDCId =
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
UPDATE_ASSET: 'Update asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
// 3001-VOTE-007
context.skip(
context(
'Governance flow - form validations for different governance proposals',
{ tags: '@slow' },
function () {
@@ -6,15 +6,16 @@ import {
waitForSpinner,
} from '../../support/common.functions';
import {
createFreeformProposal,
createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays,
enterRawProposalBody,
generateFreeFormProposalTitle,
getProposalFromTitle,
getProposalIdFromList,
getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
submitUniqueRawProposal,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
@@ -23,14 +24,10 @@ import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/stakin
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
const proposalListItem = 'proposals-list-item';
const openProposals = '[data-testid="open-proposals"]';
const voteStatus = 'vote-status';
const proposalType = 'proposal-type';
const proposalStatus = 'proposal-status';
const voteStatus = '[data-testid="vote-status"]';
const proposalClosingDate = '[data-testid="vote-details"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const voteBreakDownToggle = 'vote-breakdown-toggle';
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
before('connect wallets and set approval limit', function () {
@@ -63,12 +60,12 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
navigateTo(navigation.proposals);
cy.get(openProposals).within(() => {
cy.get(proposalClosingDate).first().should('contain.text', 'year');
cy.get(proposalClosingDate).should('contain.text', 'months');
cy.get(proposalClosingDate)
.first()
.last()
.invoke('text')
.should('match', /days|minutes/);
cy.get(proposalClosingDate).should('contain.text', 'months');
cy.get(proposalClosingDate).last().should('contain.text', 'year');
});
});
@@ -76,27 +73,36 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
const proposerId = Cypress.env('vegaWalletPublicKey');
const proposalTitle = generateFreeFormProposalTitle();
submitUniqueRawProposal({ proposalTitle: proposalTitle });
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId);
// cy.get(`#${proposalId}`).should('contain', proposalId);
cy.contains(proposalTitle).should('be.visible');
cy.get('[data-testid="filter-input"]').type('123');
cy.getByTestId(proposalListItem).should('not.exist');
createFreeformProposal(proposalTitle);
getProposalIdFromList(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId);
cy.get(`#${proposalId}`).should('contain', proposalId);
});
});
it('Newly created proposals list - shows title and portion of summary', function () {
const proposalPath = '/proposals/new-market-raw.json';
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
submitUniqueRawProposal({
proposalBody: proposalPath,
enactmentTimestamp: enactmentTimestamp,
}); // 3001-VOTE-052
// 3001-VOTE-008
// 3001-VOTE-034
// 3001-VOTE-097
cy.contains('New Market Proposal E2E submission');
cy.contains('Code: TEST.24h. fBTC settled future.').should('be.visible');
createRawProposal(this.minProposerBalance); // 3001-VOTE-052
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalIdFromList(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => {
// 3001-VOTE-008
// 3001-VOTE-034
cy.get(`#${proposalId}`)
// 3001-VOTE-097
.should('contain', rawProposal.rationale.title)
.and('be.visible');
cy.get(`#${proposalId}`)
.should(
'contain',
rawProposal.rationale.description.substring(0, 59)
)
.and('be.visible');
});
});
});
});
it('Newly created proposals list - shows open proposals in an open state', function () {
@@ -104,11 +110,23 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
// 3001-VOTE-035
createRawProposal(this.minProposerBalance);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.get(viewProposalButton).should('be.visible');
cy.getByTestId(proposalType).should('have.text', 'Freeform');
cy.getByTestId(proposalStatus).should('have.text', 'Open');
getSubmittedProposalFromProposalList(rawProposal.rationale.title).within(
() => {
cy.get(viewProposalButton).should('be.visible').click();
}
);
cy.get('@proposalIdText').then((proposalId) => {
getProposalInformationFromTable('ID')
.contains(String(proposalId))
.and('be.visible');
});
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
getProposalInformationFromTable('Type')
.contains('Freeform')
.and('be.visible');
});
});
@@ -116,22 +134,18 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
it('Newly created freeform proposals list - shows proposal participation - both met and not', function () {
const proposalTitle = generateFreeFormProposalTitle();
submitUniqueRawProposal({ proposalTitle: proposalTitle });
getProposalFromTitle(proposalTitle).within(() => {
createFreeformProposal(proposalTitle);
getSubmittedProposalFromProposalList(proposalTitle).within(() => {
// 3001-VOTE-039
cy.getByTestId(voteStatus).should(
'have.text',
'Participation not reached'
);
cy.get(voteStatus).should('have.text', 'Participation not reached');
cy.get(viewProposalButton).click();
});
voteForProposal('for');
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => {
cy.getByTestId(voteStatus).should('have.text', 'Set to pass');
getSubmittedProposalFromProposalList(proposalTitle).within(() => {
cy.get(voteStatus).should('have.text', 'Set to pass');
cy.get(viewProposalButton).click();
});
cy.getByTestId(voteBreakDownToggle).click();
getProposalInformationFromTable('Token participation met')
.contains('👍')
.should('be.visible');
@@ -96,7 +96,7 @@ context(
navigateTo(navigation.validators);
// 2002-SINC-007
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Able to view validators staked by me', function () {
@@ -146,7 +146,7 @@ context(
verifyThisEpochValue(2.0);
closeStakingDialog();
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Able to stake against a validator - using vega from both wallet and vesting contract', function () {
@@ -166,11 +166,10 @@ context(
verifyThisEpochValue(6.0);
closeStakingDialog();
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,006.00', '50.05%');
validateValidatorListTotalStakeAndShare('0', '6.00', '100.00%');
});
it('Able to stake against multiple validators', function () {
vegaWalletTeardown();
stakingPageAssociateTokens('5');
verifyUnstakedBalance(5.0);
cy.get('button').contains('Select a validator to nominate').click();
@@ -198,10 +197,14 @@ context(
.eq(1)
.within(() => {
cy.getByTestId(stakeValidatorListTotalStake)
.should('have.text', '3,002.00')
.should('have.text', '2.00')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalShare)
.should('have.text', '50.01%')
.should('have.text', '66.67%')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '2.00')
.and('be.visible');
});
cy.get(`[row-id="${1}"]`)
@@ -209,10 +212,14 @@ context(
.within(() => {
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '3,001.00')
.should('have.text', '1.00')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalShare)
.should('have.text', '49.99%')
.should('have.text', '33.33%')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible');
});
});
@@ -275,7 +282,7 @@ context(
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%');
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
cy.getByTestId(userStakeBtn).should('not.exist');
cy.getByTestId(userStake).should('not.exist');
@@ -347,7 +354,7 @@ context(
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%');
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
});
it('Disassociating all vesting contract tokens max - removes all staked tokens', function () {
@@ -375,7 +382,7 @@ context(
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%');
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
});
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
@@ -397,7 +404,7 @@ context(
});
verifyStakedBalance(2.0);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () {
@@ -111,10 +111,10 @@ context(
// 1004-ASSO-028
// 1004-ASSO-029
// 1004-ASSO-031
vegaWalletTeardown();
stakingPageAssociateTokens('2');
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('6,002.00');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get('button').contains('Select a validator to nominate').click();
stakingPageDisassociateTokens('2');
cy.getByTestId('currency-title', txTimeout).should(
@@ -125,18 +125,17 @@ context(
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(
'[data-testid="eth-wallet-associated-balances"]:visible',
txTimeout
).should('have.length', 2);
verifyEthWalletTotalAssociatedBalance('6,000.00');
cy.getByTestId('eth-wallet-associated-balances', txTimeout).should(
'not.exist'
);
verifyEthWalletTotalAssociatedBalance('0.00');
});
it('Able to associate more tokens than the approved amount of 1000 - requires re-approval', function () {
//1004-ASSO-011
stakingPageAssociateTokens('1001', { approve: true });
verifyEthWalletAssociatedBalance('1,001.00');
verifyEthWalletTotalAssociatedBalance('7,001.00');
verifyEthWalletTotalAssociatedBalance('1,001.00');
cy.get(vegaWallet)
.last()
.within(() => {
@@ -47,8 +47,11 @@ context(
function () {
before('visit withdrawals and connect vega wallet', function () {
cy.visit('/');
ethereumWalletConnect();
depositAsset(usdcEthAddress, '1000', 5);
// When running tests locally, will fail if run without restarting capsule
cy.updateCapsuleMultiSig().then(() => {
ethereumWalletConnect();
depositAsset(usdcEthAddress, '1000', 5);
});
});
beforeEach('Navigate to withdrawal page', function () {
@@ -9,7 +9,6 @@ import {
import {
enterUniqueFreeFormProposalBody,
goToMakeNewProposal,
governanceProposalType,
} from '../../support/governance.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
@@ -51,7 +50,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM);
goToMakeNewProposal('Freeform');
enterUniqueFreeFormProposalBody('50', 'pub key proposal test');
cy.getByTestId('dialog-content')
.first()
@@ -1,12 +1,8 @@
import { format } from 'date-fns';
import {
closeDialog,
navigateTo,
navigation,
waitForSpinner,
} from './common.functions';
import { closeDialog, navigateTo, navigation } from './common.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from './staking.functions';
const newProposalButton = '[data-testid="new-proposal-link"]';
const proposalInformationTableRows = '[data-testid="key-value-table-row"]';
const proposalListItem = '[data-testid="proposals-list-item"]';
const newProposalTitle = '[data-testid="proposal-title"]';
@@ -50,53 +46,6 @@ export function enterRawProposalBody(timestamp: number) {
});
}
export function submitUniqueRawProposal(proposalFields: {
proposalBody?: string;
proposalTitle?: string;
proposalDescription?: string;
closingTimestamp?: number;
enactmentTimestamp?: number;
submit?: boolean;
}) {
goToMakeNewProposal(governanceProposalType.RAW);
let proposalBodyPath = '/proposals/raw.json';
if (proposalFields.proposalBody) {
proposalBodyPath = proposalFields.proposalBody;
}
cy.fixture(proposalBodyPath).then((rawProposal) => {
if (proposalFields.proposalTitle) {
rawProposal.rationale.title = proposalFields.proposalTitle;
cy.wrap(proposalFields.proposalTitle).as('proposalTitle');
}
if (proposalFields.proposalDescription) {
rawProposal.rationale.description = proposalFields.proposalDescription;
}
if (proposalFields.closingTimestamp) {
rawProposal.terms.closingTimestamp = proposalFields.closingTimestamp;
} else {
const minTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
rawProposal.terms.closingTimestamp = minTimeStamp;
}
if (proposalFields.enactmentTimestamp) {
rawProposal.terms.enactmentTimestamp = proposalFields.enactmentTimestamp;
}
const proposalPayload = JSON.stringify(rawProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
if (proposalFields.submit !== false) {
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wrap(rawProposal).as('rawProposal');
waitForProposalSubmitted();
waitForProposalSync();
navigateTo(navigation.proposals);
}
});
}
export function enterUniqueFreeFormProposalBody(
timestamp: string,
proposalTitle: string
@@ -109,10 +58,6 @@ export function enterUniqueFreeFormProposalBody(
cy.getByTestId('proposal-submit').should('be.visible').click();
}
export function getProposalFromTitle(proposalTitle: string) {
return cy.contains(proposalTitle).parentsUntil(proposalListItem).last();
}
export function getSubmittedProposalFromProposalList(proposalTitle: string) {
getProposalIdFromList(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
@@ -175,17 +120,13 @@ export function waitForProposalSync() {
});
}
export function goToMakeNewProposal(proposalType: governanceProposalType) {
cy.visit('/proposals/propose');
waitForSpinner();
export function goToMakeNewProposal(proposalType: string) {
navigateTo(navigation.proposals);
cy.get(newProposalButton).should('be.visible').click();
cy.url().should('include', '/proposals/propose');
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
if (proposalType == governanceProposalType.RAW) {
cy.get('[href="/proposals/propose/raw"]').click();
} else {
cy.get('li').should('contain.text', proposalType).and('be.visible');
cy.get('li').contains(proposalType).click();
}
cy.get('li').should('contain.text', proposalType).and('be.visible');
cy.get('li').contains(proposalType).click();
}
export function waitForProposalSubmitted() {
@@ -222,12 +163,11 @@ export function createFreeformProposal(proposalTitle: string) {
navigateTo(navigation.proposals);
}
export enum governanceProposalType {
NETWORK_PARAMETER = 'Network parameter',
NEW_MARKET = 'New market',
UPDATE_MARKET = 'Update market',
NEW_ASSET = 'New asset',
UPDATE_ASSET = 'Update asset',
FREEFORM = 'Freeform',
RAW = 'raw proposal',
}
export const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
@@ -85,132 +85,6 @@ export function createFreeFormProposalTxBody(): ProposalSubmissionBody {
};
}
export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
const MIN_CLOSE_SEC = 5;
const MIN_ENACT_SEC = 7;
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
return {
proposalSubmission: {
rationale: {
title: 'New Market Proposal E2E submission',
description: 'E2E new market proposal',
},
terms: {
newMarket: {
changes: {
decimalPlaces: '5',
positionDecimalPlaces: '5',
linearSlippageFactor: '0.001',
quadraticSlippageFactor: '0',
lpPriceRange: '10',
instrument: {
name: 'Token test market',
code: 'TEST.24h',
future: {
settlementAsset:
'73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0',
quoteName: 'fBTC',
dataSourceSpecForSettlementData: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'prices.ETH.value',
type: 'TYPE_INTEGER' as const,
numberDecimalPlaces: '0',
},
conditions: [
{
operator: 'OPERATOR_GREATER_THAN' as const,
value: '0',
},
],
},
],
},
},
},
dataSourceSpecForTradingTermination: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'trading.terminated.ETH5',
type: 'TYPE_BOOLEAN' as const,
},
conditions: [
{
operator: 'OPERATOR_EQUALS' as const,
value: 'true',
},
],
},
],
},
},
},
dataSourceSpecBinding: {
settlementDataProperty: 'prices.ETH.value',
tradingTerminationProperty: 'trading.terminated.ETH5',
},
},
},
metadata: ['sector:energy', 'sector:tech', 'source:docs.vega.xyz'],
priceMonitoringParameters: {
triggers: [
{
horizon: '43200',
probability: '0.9999999',
auctionExtension: '600',
},
],
},
liquidityMonitoringParameters: {
targetStakeParameters: {
timeWindow: '3600',
scalingFactor: 10,
},
triggeringRatio: '0.7',
auctionExtension: '1',
},
logNormal: {
tau: 0.0001140771161,
riskAversionParameter: 0.01,
params: {
mu: 0,
r: 0.016,
sigma: 0.5,
},
},
},
},
closingTimestamp,
enactmentTimestamp,
},
},
};
}
export function mockNetworkUpgradeProposal() {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Nodes', nodeData);
@@ -67,6 +67,7 @@ export async function faucetAsset(assetEthAddress: string) {
export async function vegaWalletTeardown() {
cy.get(associatedAmountInWallet)
.should('be.visible')
.invoke('text')
.then((associatedAmount) => {
cy.get('body').then(($body) => {
@@ -81,11 +82,9 @@ export async function vegaWalletTeardown() {
cy.get(vegaWalletContainer).within(() => {
cy.get(associatedAmountInWallet, {
timeout: transactionTimeout,
})
.should('have.length', 1, { timeout: transactionTimeout })
.contains('0.00', {
timeout: transactionTimeout,
});
}).contains('0.00', {
timeout: transactionTimeout,
});
});
});
}
-1
View File
@@ -15,7 +15,6 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
-1
View File
@@ -17,7 +17,6 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
-1
View File
@@ -11,4 +11,3 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
-1
View File
@@ -12,4 +12,3 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
-1
View File
@@ -8,4 +8,3 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
-1
View File
@@ -12,4 +12,3 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
-1
View File
@@ -9,4 +9,3 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
-1
View File
@@ -62,7 +62,6 @@ export const ENV = {
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
rest: windowOrDefault('NX_VEGA_REST_URL'),
flags: {
NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')),
MOCK: TRUTHY.includes(windowOrDefault('NX_MOCKED')),
+6 -15
View File
@@ -27,10 +27,6 @@ import type { ProposalFieldsFragment } from '../proposals/proposals/__generated_
import type { NodesFragmentFragment } from '../staking/home/__generated__/Nodes';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
import {
orderByDate,
orderByUpgradeBlockHeight,
} from '../proposals/components/proposals-list/proposals-list';
const nodesToShow = 6;
@@ -204,17 +200,17 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
const proposals = useMemo(
() =>
proposalsData
? getNotRejectedProposals(proposalsData.proposalsConnection)
? getNotRejectedProposals<ProposalFieldsFragment>(
proposalsData.proposalsConnection
)
: [],
[proposalsData]
);
const sortedProposals = useMemo(() => orderByDate(proposals), [proposals]);
const protocolUpgradeProposals = useMemo(
() =>
protocolUpgradesData
? getNotRejectedProtocolUpgradeProposals(
? getNotRejectedProtocolUpgradeProposals<ProtocolUpgradeProposalFieldsFragment>(
protocolUpgradesData.protocolUpgradeProposals
).filter(
(p) =>
@@ -225,20 +221,15 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
[protocolUpgradesData]
);
const sortedProtocolUpgradeProposals = useMemo(
() => orderByUpgradeBlockHeight(protocolUpgradeProposals),
[protocolUpgradeProposals]
);
const totalProposalsDesired = 4;
const protocolUpgradeProposalsToShow = sortedProtocolUpgradeProposals.slice(
const protocolUpgradeProposalsToShow = protocolUpgradeProposals.slice(
0,
totalProposalsDesired
);
const proposalsToShow =
protocolUpgradeProposalsToShow.length === totalProposalsDesired
? []
: sortedProposals.slice(
: proposals.slice(
0,
totalProposalsDesired - protocolUpgradeProposalsToShow.length
);
@@ -43,17 +43,13 @@ jest.mock('../list-asset', () => ({
it('Renders with data-testid', async () => {
const proposal = generateProposal();
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(await screen.findByTestId('proposal')).toBeInTheDocument();
});
it('renders each section', async () => {
const proposal = generateProposal();
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
@@ -80,8 +76,6 @@ it('renders whitelist section if proposal is new asset and source is erc20', asy
},
},
});
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(screen.getByTestId('proposal-list-asset')).toBeInTheDocument();
});
@@ -24,11 +24,9 @@ export enum ProposalType {
}
export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any;
}
export const Proposal = ({ proposal, restData }: ProposalProps) => {
export const Proposal = ({ proposal }: ProposalProps) => {
const { params, loading, error } = useNetworkParams([
NetworkParams.governance_proposal_market_minVoterBalance,
NetworkParams.governance_proposal_updateMarket_minVoterBalance,
@@ -101,15 +99,12 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
<ProposalDescription description={proposal.rationale.description} />
</div>
{proposal.terms.change.__typename !== 'NewMarket' &&
proposal.terms.change.__typename !== 'UpdateMarket' && (
<div className="mb-4">
<ProposalTerms data={proposal.terms} />
</div>
)}
<div className="mb-4">
<ProposalTerms data={proposal.terms} />
</div>
<div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} />
<ProposalJson proposal={proposal} />
</div>
<div className="mb-10">
@@ -132,10 +132,10 @@ describe('Proposals list', () => {
const closedProposalsItems = closedProposals.getAllByTestId(
'proposals-list-item'
);
expect(openProposalsItems[0]).toHaveAttribute('id', 'proposal2');
expect(openProposalsItems[1]).toHaveAttribute('id', 'proposal1');
expect(closedProposalsItems[0]).toHaveAttribute('id', 'proposal3');
expect(closedProposalsItems[1]).toHaveAttribute('id', 'proposal4');
expect(openProposalsItems[0]).toHaveAttribute('id', 'proposal1');
expect(openProposalsItems[1]).toHaveAttribute('id', 'proposal2');
expect(closedProposalsItems[0]).toHaveAttribute('id', 'proposal4');
expect(closedProposalsItems[1]).toHaveAttribute('id', 'proposal3');
});
it('Displays info on no proposals', () => {
@@ -1,6 +1,5 @@
import orderBy from 'lodash/orderBy';
import { isFuture } from 'date-fns';
import { useState, useMemo } from 'react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Heading, SubHeading } from '../../../../components/heading';
import { ProposalsListItem } from '../proposals-list-item';
@@ -31,25 +30,6 @@ interface SortedProtocolUpgradeProposalsProps {
closed: ProtocolUpgradeProposalFieldsFragment[];
}
export const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => new Date(p?.datetime).getTime(),
],
['asc', 'asc']
);
export const orderByUpgradeBlockHeight = (
arr: ProtocolUpgradeProposalFieldsFragment[]
) =>
orderBy(
arr,
[(p) => p?.upgradeBlockHeight, (p) => p.vegaReleaseTag],
['desc', 'desc']
);
export const ProposalsList = ({
proposals,
protocolUpgradeProposals,
@@ -57,57 +37,35 @@ export const ProposalsList = ({
}: ProposalsListProps) => {
const { t } = useTranslation();
const [filterString, setFilterString] = useState('');
const sortedProposals: SortedProposalsProps = useMemo(() => {
const initialSorting = proposals.reduce(
(acc: SortedProposalsProps, proposal) => {
if (isFuture(new Date(proposal?.terms.closingDatetime))) {
acc.open.push(proposal);
} else {
acc.closed.push(proposal);
}
return acc;
},
{
open: [],
closed: [],
const sortedProposals = proposals.reduce(
(acc: SortedProposalsProps, proposal) => {
if (isFuture(new Date(proposal?.terms.closingDatetime))) {
acc.open.push(proposal);
} else {
acc.closed.push(proposal);
}
);
return {
open:
initialSorting.open.length > 0
? orderByDate(initialSorting.open as ProposalFieldsFragment[])
: [],
closed:
initialSorting.closed.length > 0
? orderByDate(
initialSorting.closed as ProposalFieldsFragment[]
).reverse()
: [],
};
}, [proposals]);
return acc;
},
{
open: [],
closed: [],
}
);
const sortedProtocolUpgradeProposals: SortedProtocolUpgradeProposalsProps =
useMemo(() => {
const initialSorting = protocolUpgradeProposals.reduce(
(acc: SortedProtocolUpgradeProposalsProps, proposal) => {
if (Number(proposal?.upgradeBlockHeight) > Number(lastBlockHeight)) {
acc.open.push(proposal);
} else {
acc.closed.push(proposal);
}
return acc;
},
{
open: [],
closed: [],
}
);
return {
open: orderByUpgradeBlockHeight(initialSorting.open),
closed: orderByUpgradeBlockHeight(initialSorting.closed).reverse(),
};
}, [protocolUpgradeProposals, lastBlockHeight]);
const sortedProtocolUpgradeProposals = protocolUpgradeProposals.reduce(
(acc: SortedProtocolUpgradeProposalsProps, proposal) => {
if (Number(proposal?.upgradeBlockHeight) > Number(lastBlockHeight)) {
acc.open.push(proposal);
} else {
acc.closed.push(proposal);
}
return acc;
},
{
open: [],
closed: [],
}
);
const filterPredicate = (
p: ProposalFieldsFragment | ProposalQuery['proposal']
@@ -107,7 +107,7 @@ export const ProtocolUpgradeProposalsListItem = ({
<div className="grid grid-cols-1 mt-3">
<div className="justify-self-end">
<Link
to={`${Routes.PROTOCOL_UPGRADES}/${stripFullStops(
to={`${Routes.PROPOSALS}/protocol-upgrade/${stripFullStops(
proposal.vegaReleaseTag
)}`}
>
@@ -5,14 +5,9 @@ import { useParams } from 'react-router-dom';
import { Proposal } from '../components/proposal';
import { ProposalNotFound } from '../components/proposal-not-found';
import { useProposalQuery } from './__generated__/Proposal';
import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../config';
export const ProposalContainer = () => {
const params = useParams<{ proposalId: string }>();
const {
state: { data: restData },
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
const { data, loading, error, refetch } = useProposalQuery({
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
@@ -28,7 +23,7 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer loading={loading} error={error} data={data}>
{data?.proposal ? (
<Proposal proposal={data.proposal} restData={restData} />
<Proposal proposal={data.proposal} />
) : (
<ProposalNotFound />
)}
@@ -1,4 +1,3 @@
import flow from 'lodash/flow';
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -7,15 +6,36 @@ import { SplashLoader } from '../../../components/splash-loader';
import { ProposalsList } from '../components/proposals-list';
import { useProposalsQuery } from './__generated__/Proposals';
import { getNodes } from '@vegaprotocol/utils';
import flow from 'lodash/flow';
import {
ProposalState,
ProtocolUpgradeProposalStatus,
} from '@vegaprotocol/types';
import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
import type { ProposalFieldsFragment } from './__generated__/Proposals';
import orderBy from 'lodash/orderBy';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => new Date(p?.datetime).getTime(),
],
['asc', 'asc']
);
const orderByUpgradeBlockHeight = (
arr: ProtocolUpgradeProposalFieldsFragment[]
) =>
orderBy(
arr,
[(p) => p?.upgradeBlockHeight, (p) => p.vegaReleaseTag],
['desc', 'desc']
);
export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
data?: NodeConnection<NodeEdge<T>> | null
): T[] {
@@ -24,6 +44,7 @@ export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
getNodes<ProposalFieldsFragment>(data, (p) =>
p ? p.state !== ProposalState.STATE_REJECTED : false
),
orderByDate,
])(data);
}
@@ -38,6 +59,7 @@ export function getNotRejectedProtocolUpgradeProposals<
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED
: false
),
orderByUpgradeBlockHeight,
])(data);
}
@@ -60,14 +82,17 @@ export const ProposalsContainer = () => {
});
const proposals = useMemo(
() => getNotRejectedProposals(data?.proposalsConnection),
() =>
getNotRejectedProposals<ProposalFieldsFragment>(
data?.proposalsConnection
),
[data]
);
const protocolUpgradeProposals = useMemo(
() =>
protocolUpgradesData
? getNotRejectedProtocolUpgradeProposals(
? getNotRejectedProtocolUpgradeProposals<ProtocolUpgradeProposalFieldsFragment>(
protocolUpgradesData.protocolUpgradeProposals
)
: [],
@@ -16,10 +16,11 @@ const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) => new Date(p?.terms?.closingDatetime || 0).getTime(), // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered
(p) => new Date(p?.terms?.enactmentDatetime || 0).getTime(), // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered.
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => p.id,
],
['desc', 'desc']
['desc', 'desc', 'desc']
);
export function getRejectedProposals<T extends ProposalFieldsFragment>(
+4 -8
View File
@@ -224,10 +224,6 @@ const redirects = [
path: '/vesting',
element: <Navigate to={Routes.REDEEM} replace />,
},
{
path: Routes.PROTOCOL_UPGRADES,
element: <Navigate to={Routes.PROPOSALS} replace />,
},
];
const routerConfig = [
@@ -268,13 +264,13 @@ const routerConfig = [
},
{ path: 'proposals', element: <LazyProposalsList /> },
{ path: ':proposalId', element: <LazyProposal /> },
{
path: 'protocol-upgrade/:proposalReleaseTag',
element: <LazyProtocolUpgradeProposal />,
},
{ path: 'rejected', element: <LazyRejectedProposalsList /> },
],
},
{
path: `${Routes.PROTOCOL_UPGRADES}/:proposalReleaseTag`,
element: <LazyProtocolUpgradeProposal />,
},
{
path: Routes.VALIDATORS,
element: <LazyStaking name="Staking" />,
-1
View File
@@ -5,7 +5,6 @@ const Routes = {
REWARDS: '/rewards',
PROPOSALS: '/proposals',
PROPOSALS_REJECTED: '/proposals/rejected',
PROTOCOL_UPGRADES: '/protocol-upgrades',
NOT_PERMITTED: '/not-permitted',
NOT_FOUND: '/not-found',
CONTRACTS: '/contracts',
File diff suppressed because it is too large Load Diff
@@ -194,7 +194,6 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
});
it('shows node health', function () {
// 0006-NETW-010
const market = this.market;
cy.visit(`/#/markets/${market.id}`);
cy.getByTestId('node-health')
@@ -1,110 +0,0 @@
import { closeWelcomeDialog } from '../support/helpers';
const dialogContent = 'dialog-content';
const nodeHealth = 'node-health';
describe('home', { tags: '@regression' }, () => {
before(() => {
cy.clearAllLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
closeWelcomeDialog();
});
describe('footer', () => {
it.skip('shows current block height', () => {
closeWelcomeDialog();
// 0006-NETW-004
// 0006-NETW-005
// 0006-NETW-008
// 0006-NETW-009
// 0006-NETW-011
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
req.on('response', (res) => {
res.setDelay(3001);
});
});
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Warning delay ( >3 sec)');
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
req.on('response', (res) => {
res.setDelay(1);
});
});
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Operational', { timeout: 10000 })
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
});
it('shows node switcher details', () => {
// 0006-NETW-012
// 0006-NETW-013
// 0006-NETW-014
// 0006-NETW-015
// 0006-NETW-016
cy.getByTestId(nodeHealth).click();
cy.getByTestId(dialogContent).should('contain.text', 'Connected node');
cy.getByTestId(dialogContent).should(
'contain.text',
'This app will only work on CUSTOM. Select a node to connect to.'
);
cy.getByTestId('node')
.first()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', 'Response time')
.next()
.should('contain.text', 'Block')
.next()
.should('contain.text', 'Subscription');
cy.getByTestId('custom-row').should('contain.text', 'Other');
cy.getByTestId('dialog-close').click();
});
it('switch to other node', () => {
// 0006-NETW-017
// 0006-NETW-018
// 0006-NETW-019
// 0006-NETW-020
cy.getByTestId(nodeHealth).click();
cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('node-url-custom').click();
cy.getByTestId('connect').should('be.disabled');
cy.get("input[placeholder='https://']")
.focus()
.type(new URL(Cypress.env('VEGA_URL')).origin + '/graphql');
cy.getByTestId('connect').click();
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Operational');
});
});
describe('Network switcher', () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
});
// 0006-NETW-002
// 0006-NETW-003
it('switch to fairground network', () => {
cy.getByTestId('network-switcher').click();
cy.getByTestId('network-item').contains('Fairground testnet').click();
cy.get('[aria-haspopup="menu"]').should('contain.text', 'Fairground');
});
});
});
+24 -3
View File
@@ -4,7 +4,6 @@ import { marketsDataQuery } from '@vegaprotocol/mock';
import * as Schema from '@vegaprotocol/types';
const selectMarketOverlay = 'select-market-list';
const dialogContent = 'dialog-content';
const generateProposal = (code: string): ProposalListFieldsFragment => ({
__typename: 'Proposal',
@@ -82,12 +81,20 @@ const generateProposal = (code: string): ProposalListFieldsFragment => ({
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
},
@@ -281,7 +288,7 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.location('hash').should('equal', '#/markets/market-1');
cy.getByTestId(dialogContent).should('not.exist');
cy.getByTestId('dialog-content').should('not.exist');
});
});
@@ -294,8 +301,22 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.location('hash').should('equal', '#/markets/market-not-existing');
cy.getByTestId(dialogContent).should('not.exist');
cy.getByTestId('dialog-content').should('not.exist');
});
});
});
describe('footer', () => {
it('shows current block height', () => {
cy.visit('/');
cy.getByTestId('node-health')
.children()
.first()
.should('contain.text', 'Operational')
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
});
});
});
@@ -482,7 +482,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
testOrderCancellation(order);
});
});
it('must be able to cancel all orders on all markets', () => {
it('must be able to cancel all orders on a market', () => {
// 7003-MORD-009
// 7003-MORD-010
// 7003-MORD-011
@@ -496,7 +496,9 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
.should('have.text', 'Cancel all')
.then(($btn) => {
cy.wrap($btn).click({ force: true });
const order: OrderCancellation = {};
const order: OrderCancellation = {
marketId: 'market-0',
};
testOrderCancellation(order);
});
});
@@ -32,6 +32,7 @@ import {
import { TradingViews } from './trade-views';
import { MarketSelector } from './market-selector';
import { HeaderStats } from './header-stats';
import { PositionsMultiKey } from '@vegaprotocol/positions';
interface TradeGridProps {
market: Market | null;
@@ -130,6 +131,11 @@ const MarketBottomPanel = memo(
>
<TradeGridChild>
<Tabs storageKey="console-trade-grid-bottom-right">
<Tab id="positions-multi" name={t('Positions v2')}>
<VegaWalletContainer>
<PositionsMultiKey />
</VegaWalletContainer>
</Tab>
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<TradingViews.positions.component
@@ -20,6 +20,7 @@ export const SettlementDateCell = ({
}: SettlementDataCellProps) => {
const linkCreator = useLinks(DApp.Explorer);
const date = closeTimestamp ? new Date(closeTimestamp) : metaDate;
console.log(metaDate);
let text = '';
if (!date) {
@@ -92,5 +92,8 @@ const cacheConfig: InMemoryCacheConfig = {
Fees: {
keyFields: false,
},
Position: {
keyFields: ['market', ['id'], 'party', ['id']],
},
},
};
+1 -1
View File
@@ -46,7 +46,7 @@ export const Navbar = ({
return (
<Navigation
appName="Console"
theme={theme}
theme={'dark'}
actions={
<>
<ProtocolUpgradeCountdown
+1 -13
View File
@@ -115,19 +115,7 @@ export function createClient({
)
: httpLink;
const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
// if any of these queries error don't capture any errors, its
// likely the user is connecting to a dodgy node, NodeGuard gets
// called on startup, NodeCheck and NodeCheckTimeUpdate are called
// by the NodeSwitcher component and the useNodeHealth hook
if (
['NodeGuard', 'NodeCheck', 'NodeCheckTimeUpdate'].includes(
operation.operationName
)
) {
return;
}
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach((e) => {
if (e.extensions && e.extensions['type'] !== NOT_FOUND) {
@@ -107,7 +107,6 @@ export const NetworkSwitcher = ({
onOpenChange={handleOpen}
trigger={
<DropdownMenuTrigger
data-testid="network-switcher"
ref={menuRef}
className={classNames(
'flex justify-between items-center text-sm text-vega-dark-600 dark:text-vega-light-600 py-1 px-2 rounded border border-vega-dark-200 whitespace-nowrap dark:hover:bg-vega-dark-500 hover:bg-vega-light-500',
@@ -3,13 +3,11 @@ import { MockedProvider } from '@apollo/react-testing';
import { act, render, screen, waitFor } from '@testing-library/react';
import { RadioGroup } from '@vegaprotocol/ui-toolkit';
import type {
NodeCheckTimeUpdateSubscription,
NodeCheckQuery,
} from '../../utils/__generated__/NodeCheck';
import {
NodeCheckDocument,
NodeCheckTimeUpdateDocument,
} from '../../utils/__generated__/NodeCheck';
BlockTimeSubscription,
StatisticsQuery,
} from '../../utils/__generated__/Node';
import { BlockTimeDocument } from '../../utils/__generated__/Node';
import { StatisticsDocument } from '../../utils/__generated__/Node';
import type { RowDataProps } from './row-data';
import { POLL_INTERVAL } from './row-data';
import { BLOCK_THRESHOLD, RowData } from './row-data';
@@ -21,9 +19,9 @@ jest.mock('@vegaprotocol/apollo-client', () => ({
useHeaderStore: jest.fn().mockReturnValue({}),
}));
const statsQueryMock: MockedResponse<NodeCheckQuery> = {
const statsQueryMock: MockedResponse<StatisticsQuery> = {
request: {
query: NodeCheckDocument,
query: StatisticsDocument,
},
result: {
data: {
@@ -36,9 +34,9 @@ const statsQueryMock: MockedResponse<NodeCheckQuery> = {
},
};
const subMock: MockedResponse<NodeCheckTimeUpdateSubscription> = {
const subMock: MockedResponse<BlockTimeSubscription> = {
request: {
query: NodeCheckTimeUpdateDocument,
query: BlockTimeDocument,
},
result: {
data: {
@@ -73,8 +71,8 @@ const mockHeaders = (
const renderComponent = (
props: RowDataProps,
queryMock: MockedResponse<NodeCheckQuery>,
subMock: MockedResponse<NodeCheckTimeUpdateSubscription>
queryMock: MockedResponse<StatisticsQuery>,
subMock: MockedResponse<BlockTimeSubscription>
) => {
return (
<MockedProvider mocks={[queryMock, subMock, subMock, subMock]}>
@@ -129,16 +127,16 @@ describe('RowData', () => {
it('radio button still enabled if query fails', async () => {
mockHeaders(props.url, {});
const failedQueryMock: MockedResponse<NodeCheckQuery> = {
const failedQueryMock: MockedResponse<StatisticsQuery> = {
request: {
query: NodeCheckDocument,
query: StatisticsDocument,
},
error: new Error('failed'),
};
const failedSubMock: MockedResponse<NodeCheckTimeUpdateSubscription> = {
const failedSubMock: MockedResponse<BlockTimeSubscription> = {
request: {
query: NodeCheckTimeUpdateDocument,
query: BlockTimeDocument,
},
error: new Error('failed'),
};
@@ -246,10 +244,10 @@ describe('RowData', () => {
jest.useFakeTimers();
const createStatsQueryMock = (
blockHeight: string
): MockedResponse<NodeCheckQuery> => {
): MockedResponse<StatisticsQuery> => {
return {
request: {
query: NodeCheckDocument,
query: StatisticsDocument,
},
result: {
data: {
@@ -263,10 +261,10 @@ describe('RowData', () => {
};
};
const createFailedStatsQueryMock = (): MockedResponse<NodeCheckQuery> => {
const createFailedStatsQueryMock = (): MockedResponse<StatisticsQuery> => {
return {
request: {
query: NodeCheckDocument,
query: StatisticsDocument,
},
result: {
data: undefined,
@@ -6,9 +6,9 @@ import { Radio } from '@vegaprotocol/ui-toolkit';
import { useEffect, useState } from 'react';
import { CUSTOM_NODE_KEY } from '../../types';
import {
useNodeCheckQuery,
useNodeCheckTimeUpdateSubscription,
} from '../../utils/__generated__/NodeCheck';
useBlockTimeSubscription,
useStatisticsQuery,
} from '../../utils/__generated__/Node';
import { LayoutCell } from './layout-cell';
export const POLL_INTERVAL = 1000;
@@ -30,14 +30,13 @@ export const RowData = ({
const [subFailed, setSubFailed] = useState(false);
const [time, setTime] = useState<number>();
// no use of data here as we need the data nodes reference to block height
const { data, error, loading, startPolling, stopPolling } = useNodeCheckQuery(
{
const { data, error, loading, startPolling, stopPolling } =
useStatisticsQuery({
pollInterval: POLL_INTERVAL,
// fix for pollInterval
// https://github.com/apollographql/apollo-client/issues/9819
ssr: false,
}
);
});
const headerStore = useHeaderStore();
const headers = headerStore[url];
@@ -45,7 +44,7 @@ export const RowData = ({
data: subData,
error: subError,
loading: subLoading,
} = useNodeCheckTimeUpdateSubscription();
} = useBlockTimeSubscription();
useEffect(() => {
const timeout = setTimeout(() => {
@@ -1,11 +1,11 @@
import {
NodeCheckDocument,
NodeCheckTimeUpdateDocument,
} from '../../utils/__generated__/NodeCheck';
StatisticsDocument,
BlockTimeDocument,
} from '../../utils/__generated__/Node';
import type {
NodeCheckQuery,
NodeCheckTimeUpdateSubscription,
} from '../../utils/__generated__/NodeCheck';
StatisticsQuery,
BlockTimeSubscription,
} from '../../utils/__generated__/Node';
import { Networks } from '../../types';
import type { RequestHandlerResponse } from 'mock-apollo-client';
import { createMockClient } from 'mock-apollo-client';
@@ -21,7 +21,7 @@ type MockClientProps = {
busEvents?: MockRequestConfig;
};
export const getMockBusEventsResult = (): NodeCheckTimeUpdateSubscription => ({
export const getMockBusEventsResult = (): BlockTimeSubscription => ({
busEvents: [
{
__typename: 'BusEvent',
@@ -32,21 +32,19 @@ export const getMockBusEventsResult = (): NodeCheckTimeUpdateSubscription => ({
export const getMockStatisticsResult = (
env: Networks = Networks.TESTNET
): NodeCheckQuery => ({
): StatisticsQuery => ({
statistics: {
__typename: 'Statistics',
chainId: `${env.toLowerCase()}-0123`,
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
});
export const getMockQueryResult = (env: Networks): NodeCheckQuery => ({
export const getMockQueryResult = (env: Networks): StatisticsQuery => ({
statistics: {
__typename: 'Statistics',
chainId: `${env.toLowerCase()}-0123`,
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
});
@@ -74,11 +72,11 @@ export default function ({
const mockClient = createMockClient();
mockClient.setRequestHandler(
NodeCheckDocument,
StatisticsDocument,
getHandler(statistics, getMockStatisticsResult(network))
);
mockClient.setRequestHandler(
NodeCheckTimeUpdateDocument,
BlockTimeDocument,
getHandler(busEvents, getMockBusEventsResult())
);
+9 -11
View File
@@ -5,13 +5,11 @@ import { useEffect } from 'react';
import { create } from 'zustand';
import { createClient } from '@vegaprotocol/apollo-client';
import type {
NodeCheckTimeUpdateSubscription,
NodeCheckQuery,
} from '../utils/__generated__/NodeCheck';
import {
NodeCheckDocument,
NodeCheckTimeUpdateDocument,
} from '../utils/__generated__/NodeCheck';
BlockTimeSubscription,
StatisticsQuery,
} from '../utils/__generated__/Node';
import { BlockTimeDocument } from '../utils/__generated__/Node';
import { StatisticsDocument } from '../utils/__generated__/Node';
import type { Environment } from '../types';
import { Networks } from '../types';
import { compileErrors } from '../utils/compile-errors';
@@ -222,8 +220,8 @@ const testNode = async (
*/
const testQuery = async (client: Client) => {
try {
const result = await client.query<NodeCheckQuery>({
query: NodeCheckDocument,
const result = await client.query<StatisticsQuery>({
query: StatisticsDocument,
});
if (!result || result.error) {
return false;
@@ -242,8 +240,8 @@ const testQuery = async (client: Client) => {
const testSubscription = (client: Client) => {
return new Promise((resolve) => {
const sub = client
.subscribe<NodeCheckTimeUpdateSubscription>({
query: NodeCheckTimeUpdateDocument,
.subscribe<BlockTimeSubscription>({
query: BlockTimeDocument,
errorPolicy: 'all',
})
.subscribe({
@@ -2,8 +2,8 @@ import { renderHook, waitFor } from '@testing-library/react';
import { useNodeHealth } from './use-node-health';
import type { MockedResponse } from '@apollo/react-testing';
import { MockedProvider } from '@apollo/react-testing';
import type { NodeCheckQuery } from '../utils/__generated__/NodeCheck';
import { NodeCheckDocument } from '../utils/__generated__/NodeCheck';
import type { StatisticsQuery } from '../utils/__generated__/Node';
import { StatisticsDocument } from '../utils/__generated__/Node';
import { useHeaderStore } from '@vegaprotocol/apollo-client';
import { Intent } from '@vegaprotocol/ui-toolkit';
@@ -16,10 +16,10 @@ jest.mock('@vegaprotocol/apollo-client');
const createStatsMock = (
blockHeight: number
): MockedResponse<NodeCheckQuery> => {
): MockedResponse<StatisticsQuery> => {
return {
request: {
query: NodeCheckDocument,
query: StatisticsDocument,
},
result: {
data: {
@@ -34,7 +34,7 @@ const createStatsMock = (
};
function setup(
mock: MockedResponse<NodeCheckQuery>,
mock: MockedResponse<StatisticsQuery>,
headers:
| {
blockHeight: number;
@@ -93,9 +93,9 @@ describe('useNodeHealth', () => {
);
it('block diff is null if query fails indicating non operational', async () => {
const failedQuery: MockedResponse<NodeCheckQuery> = {
const failedQuery: MockedResponse<StatisticsQuery> = {
request: {
query: NodeCheckDocument,
query: StatisticsDocument,
},
result: {
// @ts-ignore failed query with no result
@@ -1,5 +1,5 @@
import { useEffect, useMemo } from 'react';
import { useNodeCheckQuery } from '../utils/__generated__/NodeCheck';
import { useStatisticsQuery } from '../utils/__generated__/Node';
import { useHeaderStore } from '@vegaprotocol/apollo-client';
import { useEnvironment } from './use-environment';
import { useNavigatorOnline } from '@vegaprotocol/react-helpers';
@@ -16,7 +16,7 @@ export const useNodeHealth = () => {
const url = useEnvironment((store) => store.VEGA_URL);
const headerStore = useHeaderStore();
const headers = url ? headerStore[url] : undefined;
const { data, error, startPolling, stopPolling } = useNodeCheckQuery({
const { data, error, startPolling, stopPolling } = useStatisticsQuery({
fetchPolicy: 'no-cache',
});
+1 -1
View File
@@ -8,4 +8,4 @@ export * from './hooks';
export * from './types';
// Utils
export * from './utils/__generated__/NodeCheck';
export * from './utils/__generated__/Node';
@@ -1,4 +1,4 @@
query NodeCheck {
query Statistics {
statistics {
chainId
blockHeight
@@ -6,7 +6,7 @@ query NodeCheck {
}
}
subscription NodeCheckTimeUpdate {
subscription BlockTime {
busEvents(types: TimeUpdate, batchSize: 1) {
id
}
+81
View File
@@ -0,0 +1,81 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type StatisticsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } };
export type BlockTimeSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
export type BlockTimeSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', id: string }> | null };
export const StatisticsDocument = gql`
query Statistics {
statistics {
chainId
blockHeight
vegaTime
}
}
`;
/**
* __useStatisticsQuery__
*
* To run a query within a React component, call `useStatisticsQuery` and pass it any options that fit your needs.
* When your component renders, `useStatisticsQuery` 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 } = useStatisticsQuery({
* variables: {
* },
* });
*/
export function useStatisticsQuery(baseOptions?: Apollo.QueryHookOptions<StatisticsQuery, StatisticsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<StatisticsQuery, StatisticsQueryVariables>(StatisticsDocument, options);
}
export function useStatisticsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<StatisticsQuery, StatisticsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<StatisticsQuery, StatisticsQueryVariables>(StatisticsDocument, options);
}
export type StatisticsQueryHookResult = ReturnType<typeof useStatisticsQuery>;
export type StatisticsLazyQueryHookResult = ReturnType<typeof useStatisticsLazyQuery>;
export type StatisticsQueryResult = Apollo.QueryResult<StatisticsQuery, StatisticsQueryVariables>;
export const BlockTimeDocument = gql`
subscription BlockTime {
busEvents(types: TimeUpdate, batchSize: 1) {
id
}
}
`;
/**
* __useBlockTimeSubscription__
*
* To run a query within a React component, call `useBlockTimeSubscription` and pass it any options that fit your needs.
* When your component renders, `useBlockTimeSubscription` 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 subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useBlockTimeSubscription({
* variables: {
* },
* });
*/
export function useBlockTimeSubscription(baseOptions?: Apollo.SubscriptionHookOptions<BlockTimeSubscription, BlockTimeSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<BlockTimeSubscription, BlockTimeSubscriptionVariables>(BlockTimeDocument, options);
}
export type BlockTimeSubscriptionHookResult = ReturnType<typeof useBlockTimeSubscription>;
export type BlockTimeSubscriptionResult = Apollo.SubscriptionResult<BlockTimeSubscription>;
-81
View File
@@ -1,81 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type NodeCheckQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type NodeCheckQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } };
export type NodeCheckTimeUpdateSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
export type NodeCheckTimeUpdateSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', id: string }> | null };
export const NodeCheckDocument = gql`
query NodeCheck {
statistics {
chainId
blockHeight
vegaTime
}
}
`;
/**
* __useNodeCheckQuery__
*
* To run a query within a React component, call `useNodeCheckQuery` and pass it any options that fit your needs.
* When your component renders, `useNodeCheckQuery` 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 } = useNodeCheckQuery({
* variables: {
* },
* });
*/
export function useNodeCheckQuery(baseOptions?: Apollo.QueryHookOptions<NodeCheckQuery, NodeCheckQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<NodeCheckQuery, NodeCheckQueryVariables>(NodeCheckDocument, options);
}
export function useNodeCheckLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<NodeCheckQuery, NodeCheckQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<NodeCheckQuery, NodeCheckQueryVariables>(NodeCheckDocument, options);
}
export type NodeCheckQueryHookResult = ReturnType<typeof useNodeCheckQuery>;
export type NodeCheckLazyQueryHookResult = ReturnType<typeof useNodeCheckLazyQuery>;
export type NodeCheckQueryResult = Apollo.QueryResult<NodeCheckQuery, NodeCheckQueryVariables>;
export const NodeCheckTimeUpdateDocument = gql`
subscription NodeCheckTimeUpdate {
busEvents(types: TimeUpdate, batchSize: 1) {
id
}
}
`;
/**
* __useNodeCheckTimeUpdateSubscription__
*
* To run a query within a React component, call `useNodeCheckTimeUpdateSubscription` and pass it any options that fit your needs.
* When your component renders, `useNodeCheckTimeUpdateSubscription` 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 subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useNodeCheckTimeUpdateSubscription({
* variables: {
* },
* });
*/
export function useNodeCheckTimeUpdateSubscription(baseOptions?: Apollo.SubscriptionHookOptions<NodeCheckTimeUpdateSubscription, NodeCheckTimeUpdateSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<NodeCheckTimeUpdateSubscription, NodeCheckTimeUpdateSubscriptionVariables>(NodeCheckTimeUpdateDocument, options);
}
export type NodeCheckTimeUpdateSubscriptionHookResult = ReturnType<typeof useNodeCheckTimeUpdateSubscription>;
export type NodeCheckTimeUpdateSubscriptionResult = Apollo.SubscriptionResult<NodeCheckTimeUpdateSubscription>;
+4 -4
View File
@@ -1,11 +1,11 @@
import type { NodeCheckQuery } from './__generated__/NodeCheck';
import type { StatisticsQuery } from './__generated__/Node';
import merge from 'lodash/merge';
import type { PartialDeep } from 'type-fest';
export const statisticsQuery = (
override?: PartialDeep<NodeCheckQuery>
): NodeCheckQuery => {
const defaultResult: NodeCheckQuery = {
override?: PartialDeep<StatisticsQuery>
): StatisticsQuery => {
const defaultResult: StatisticsQuery = {
statistics: {
__typename: 'Statistics',
chainId: 'chain-id',
@@ -16,6 +16,16 @@ fragment DataSource on DataSourceDefinition {
}
}
}
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
}
}
@@ -3,14 +3,14 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type DataSourceFragment = { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } };
export type DataSourceFragment = { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } };
export type MarketInfoQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
export const DataSourceFragmentDoc = gql`
fragment DataSource on DataSourceDefinition {
@@ -31,6 +31,16 @@ export const DataSourceFragmentDoc = gql`
}
}
}
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
}
}
`;
@@ -105,10 +105,12 @@ export const MarketInfoAccordion = ({
content: <InsurancePoolInfoPanel market={market} account={a} />,
})),
];
const settlementData = market.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
const terminationData = market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
const settlementData =
market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData
.data;
const terminationData =
market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data;
const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
@@ -5,7 +5,7 @@ import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import { marketDataProvider } from '../../market-data-provider';
import { totalFeesPercentage } from '../../market-utils';
import { ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import { Dialog, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import {
addDecimalsFormatNumber,
formatNumber,
@@ -25,9 +25,12 @@ import { ConditionOperatorMapping } from '@vegaprotocol/types';
import { MarketTradingModeMapping } from '@vegaprotocol/types';
import { useEnvironment } from '@vegaprotocol/environment';
import type { Provider } from '../../oracle-schema';
import { OracleBasicProfile } from '../../components/oracle-basic-profile';
import { useOracleProofs } from '../../hooks';
import { OracleDialog } from '../oracle-dialog/oracle-dialog';
import {
OracleBasicProfile,
OracleProfileTitle,
OracleFullProfile,
} from '../../components';
import { useOracleProofs, useOracleMarkets } from '../../hooks';
import { useDataProvider } from '@vegaprotocol/data-provider';
type PanelProps = Pick<
@@ -462,17 +465,15 @@ export const OracleInfoPanel = ({
? product.dataSourceSpecForSettlementData.id
: product.dataSourceSpecForTradingTermination.id;
const dataSourceSpec = (
type === 'settlementData'
? product.dataSourceSpecForSettlementData.data
: product.dataSourceSpecForTradingTermination.data
) as DataSourceDefinition;
return (
<div className="flex flex-col gap-4">
<DataSourceProof
data-testid="oracle-proof-links"
data={dataSourceSpec}
data={
type === 'settlementData'
? product.dataSourceSpecForSettlementData.data
: product.dataSourceSpecForTradingTermination.data
}
providers={data}
type={type}
dataSourceSpecId={dataSourceSpecId}
@@ -529,27 +530,19 @@ export const DataSourceProof = ({
}
if (data.sourceType.__typename === 'DataSourceDefinitionInternal') {
if (data.sourceType.sourceType) {
return (
<div>
<h3>{t('Internal conditions')}</h3>
{data.sourceType.sourceType?.conditions.map((condition, i) => {
if (!condition) return null;
return (
<p key={i}>
{ConditionOperatorMapping[condition.operator]} {condition.value}
</p>
);
})}
</div>
);
} else {
return (
<div>
{t('No oracle spec for trading termination. Internal timestamp used')}
</div>
);
}
return (
<div>
<h3>{t('Internal conditions')}</h3>
{data.sourceType.sourceType.conditions.map((condition, i) => {
if (!condition) return null;
return (
<p key={i}>
{ConditionOperatorMapping[condition.operator]} {condition.value}
</p>
);
})}
</div>
);
}
return <div>{t('Invalid data source')}</div>;
@@ -620,6 +613,34 @@ const NoOracleProof = ({
);
};
export const OracleDialog = ({
provider,
dataSourceSpecId,
open,
onChange,
}: {
dataSourceSpecId: string;
provider: Provider;
open: boolean;
onChange?: (isOpen: boolean) => void;
}) => {
const oracleMarkets = useOracleMarkets(provider);
return (
<Dialog
title={<OracleProfileTitle provider={provider} />}
aria-labelledby="oracle-proof-dialog"
open={open}
onChange={onChange}
>
<OracleFullProfile
provider={provider}
dataSourceSpecId={dataSourceSpecId}
markets={oracleMarkets}
/>
</Dialog>
);
};
const OracleProfile = (props: {
provider: Provider;
dataSourceSpecId: string;
@@ -143,7 +143,7 @@ export const marketInfoQuery = (
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
},
},
],
@@ -164,7 +164,7 @@ export const marketInfoQuery = (
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
},
},
],
@@ -1,10 +1,8 @@
import { useMemo } from 'react';
import { useEnvironment } from '@vegaprotocol/environment';
import { Icon } from '@vegaprotocol/ui-toolkit';
import type { IconName } from '@blueprintjs/icons';
import { t } from '@vegaprotocol/i18n';
import { getMatchingOracleProvider, useOracleProofs } from '../../hooks';
import type { Market } from '../../markets-provider';
import { getVerifiedStatusIcon } from '../oracle-basic-profile';
export const OracleStatus = ({
dataSourceSpecForSettlementData,
@@ -25,15 +23,22 @@ export const OracleStatus = ({
dataSourceSpecForTradingTermination.data,
providers
);
let maliciousOracleProvider = null;
if (settlementDataProvider?.oracle.status !== 'GOOD') {
maliciousOracleProvider = settlementDataProvider;
} else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') {
maliciousOracleProvider = tradingTerminationDataProvider;
if (
(settlementDataProvider &&
settlementDataProvider.oracle.status !== 'GOOD') ||
(tradingTerminationDataProvider &&
tradingTerminationDataProvider.oracle.status !== 'GOOD')
) {
return (
<span
className="ml-1"
role="img"
aria-label={t('oracle status not healthy')}
>
</span>
);
}
if (!maliciousOracleProvider) return null;
const { icon } = getVerifiedStatusIcon(maliciousOracleProvider);
return <Icon size={3} name={icon as IconName} className="ml-1" />;
}
return null;
}, [
@@ -6,13 +6,27 @@ import {
NotificationBanner,
ButtonLink,
} from '@vegaprotocol/ui-toolkit';
import { OracleDialog } from '../oracle-dialog';
import { oracleStatuses } from './oracle-statuses';
import { OracleDialog } from '../market-info';
export const oracleStatuses = {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
};
export const OracleBanner = ({ marketId }: { marketId: string }) => {
const [open, onChange] = useState(false);
const { data: settlementOracle } = useMarketOracle(marketId);
const { data: tradingTerminationOracle } = useMarketOracle(
const settlementOracle = useMarketOracle(marketId);
const tradingTerminationOracle = useMarketOracle(
marketId,
'dataSourceSpecForTradingTermination'
);
@@ -22,7 +36,15 @@ export const OracleBanner = ({ marketId }: { marketId: string }) => {
} else if (tradingTerminationOracle?.provider.oracle.status !== 'GOOD') {
maliciousOracle = tradingTerminationOracle;
}
if (!maliciousOracle) return null;
if (!settlementOracle && !tradingTerminationOracle) {
return (
<NotificationBanner intent={Intent.Primary}>
<div>{t('There is no oracle for this market.')} </div>
</NotificationBanner>
);
}
const { provider } = maliciousOracle;
return (
@@ -1,16 +0,0 @@
import { t } from '@vegaprotocol/i18n';
export const oracleStatuses = {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
};
@@ -1 +0,0 @@
export * from './oracle-dialog';
@@ -1,35 +0,0 @@
import { Dialog } from '@vegaprotocol/ui-toolkit';
import {
OracleProfileTitle,
OracleFullProfile,
} from '../../components/oracle-full-profile';
import { useOracleMarkets } from '../../hooks';
import type { Provider } from '../../oracle-schema';
export const OracleDialog = ({
provider,
dataSourceSpecId,
open,
onChange,
}: {
dataSourceSpecId: string;
provider: Provider;
open: boolean;
onChange?: (isOpen: boolean) => void;
}) => {
const oracleMarkets = useOracleMarkets(provider);
return (
<Dialog
title={<OracleProfileTitle provider={provider} />}
aria-labelledby="oracle-proof-dialog"
open={open}
onChange={onChange}
>
<OracleFullProfile
provider={provider}
dataSourceSpecId={dataSourceSpecId}
markets={oracleMarkets}
/>
</Dialog>
);
};
@@ -1 +1,2 @@
export * from './oracle-full-profile.stories';
export * from './oracle-full-profile';
@@ -9,7 +9,7 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { oracleStatuses } from '../oracle-banner/oracle-statuses';
import { oracleStatuses } from '../oracle-banner';
import type { IconName } from '@blueprintjs/icons';
import classNames from 'classnames';
import { getLinkIcon, getVerifiedStatusIcon } from '../oracle-basic-profile';
@@ -1,6 +1,6 @@
import { renderHook } from '@testing-library/react';
import { useMarketOracle } from './use-market-oracle';
import type { MarketFieldsFragment } from '../__generated__/markets';
import type { MarketInfoQuery } from '../components/market-info/__generated__/MarketInfo';
import type { Provider } from '../oracle-schema';
const ORACLE_PROOFS_URL = 'ORACLE_PROOFS_URL';
@@ -10,42 +10,43 @@ const key = 'key';
const dataSourceSpecId = 'dataSourceSpecId';
const mockEnvironment = jest.fn(() => ({ ORACLE_PROOFS_URL }));
const mockMarket = jest.fn<{ data: MarketFieldsFragment | null }, unknown[]>(
() => ({
data: {
tradableInstrument: {
instrument: {
product: {
dataSourceSpecForSettlementData: {
id: dataSourceSpecId,
data: {
const mockDataProvider = jest.fn<
{ data: MarketInfoQuery['market'] },
unknown[]
>(() => ({
data: {
tradableInstrument: {
instrument: {
product: {
dataSourceSpecForSettlementData: {
id: dataSourceSpecId,
data: {
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
signers: [
{
signer: {
__typename: 'ETHAddress',
address,
},
signers: [
{
signer: {
__typename: 'ETHAddress',
address,
},
{
signer: {
__typename: 'PubKey',
key,
},
},
{
signer: {
__typename: 'PubKey',
key,
},
],
},
},
],
},
},
},
},
},
},
} as MarketFieldsFragment,
})
);
},
} as MarketInfoQuery['market'],
}));
const mockOracleProofs = jest.fn<{ data?: Provider[] }, unknown[]>(() => ({}));
@@ -53,8 +54,9 @@ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: jest.fn((args) => mockEnvironment()),
}));
jest.mock('../markets-provider', () => ({
useMarket: jest.fn((args) => mockMarket()),
jest.mock('@vegaprotocol/data-provider', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useDataProvider: jest.fn((args) => mockDataProvider()),
}));
jest.mock('./use-oracle-proofs', () => ({
@@ -64,15 +66,15 @@ jest.mock('./use-oracle-proofs', () => ({
const marketId = 'marketId';
describe('useMarketOracle', () => {
it('returns undefined if no market info present', () => {
mockMarket.mockReturnValueOnce({ data: null });
mockDataProvider.mockReturnValueOnce({ data: null });
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.data).toBeUndefined();
expect(result.current).toBeUndefined();
});
it('returns undefined if no oracle proofs present', () => {
mockOracleProofs.mockReturnValueOnce({ data: undefined });
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.data).toBeUndefined();
expect(result.current).toBeUndefined();
});
it('returns oracle matched by eth_address', () => {
@@ -100,8 +102,8 @@ describe('useMarketOracle', () => {
data,
});
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.data?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.data?.provider).toBe(data[1]);
expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.provider).toBe(data[1]);
});
it('returns oracle matching by public_key', () => {
@@ -129,7 +131,7 @@ describe('useMarketOracle', () => {
data,
});
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.data?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.data?.provider).toBe(data[1]);
expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.provider).toBe(data[1]);
});
});
+14 -21
View File
@@ -1,7 +1,7 @@
import { useEnvironment } from '@vegaprotocol/environment';
import { useOracleProofs } from './use-oracle-proofs';
import { useMarket } from '../markets-provider';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketInfoProvider } from '../components/market-info/market-info-data-provider';
import { useMemo } from 'react';
import type { Provider } from '../oracle-schema';
import type { DataSourceSpecFragment } from '../__generated__/OracleMarketsSpec';
@@ -41,30 +41,23 @@ export const useMarketOracle = (
dataSourceType:
| 'dataSourceSpecForSettlementData'
| 'dataSourceSpecForTradingTermination' = 'dataSourceSpecForSettlementData'
): {
data?: {
provider: NonNullable<ReturnType<typeof getMatchingOracleProvider>>;
dataSourceSpecId: string;
};
loading?: boolean;
} => {
) => {
const { ORACLE_PROOFS_URL } = useEnvironment();
const { data: market, loading: marketLoading } = useMarket(marketId);
const { data: providers, loading: providersLoading } =
useOracleProofs(ORACLE_PROOFS_URL);
const { data: marketInfo } = useDataProvider({
dataProvider: marketInfoProvider,
variables: { marketId },
});
const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL);
return useMemo(() => {
if (marketLoading || providersLoading) {
return { loading: true };
}
if (!providers || !market) {
return { data: undefined };
if (!providers || !marketInfo) {
return undefined;
}
const dataSourceSpec =
market.tradableInstrument.instrument.product[dataSourceType];
marketInfo.tradableInstrument.instrument.product[dataSourceType];
const provider = getMatchingOracleProvider(dataSourceSpec.data, providers);
if (provider) {
return { data: { provider, dataSourceSpecId: dataSourceSpec.id } };
return { provider, dataSourceSpecId: dataSourceSpec.id };
}
return { data: undefined };
}, [market, dataSourceType, providers, marketLoading, providersLoading]);
return undefined;
}, [marketInfo, dataSourceType, providers]);
};
+2 -20
View File
@@ -64,44 +64,26 @@ export const createMarketFragment = (
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
id: 'oracleId',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
},
},
],
},
},
},
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
id: 'oracleId',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
},
},
],
},
},
},
@@ -133,9 +133,11 @@ export const OrderListManager = ({
const cancelAll = useCallback(() => {
create({
orderCancellation: {},
orderCancellation: {
marketId,
},
});
}, [create]);
}, [create, marketId]);
return (
<>
@@ -226,8 +226,8 @@ describe('OrderListTable', () => {
const amendCell = getAmendCell();
const typeCell = screen.getAllByRole('gridcell')[2];
expect(typeCell).toHaveTextContent('Mid - 10.0 Peg limit');
expect(amendCell.queryByTestId('edit')).toBeInTheDocument();
expect(amendCell.queryByTestId('cancel')).toBeInTheDocument();
expect(amendCell.queryByTestId('edit')).not.toBeInTheDocument();
expect(amendCell.queryByTestId('cancel')).not.toBeInTheDocument();
});
it.each([
@@ -329,7 +329,7 @@ export const isOrderActive = (status: Schema.OrderStatus) => {
};
export const isOrderAmendable = (order: Order | undefined) => {
if (!order || order.liquidityProvision) {
if (!order || order.peggedOrder || order.liquidityProvision) {
return false;
}
+1
View File
@@ -1,6 +1,7 @@
export * from './lib/__generated__/Positions';
export * from './lib/positions-container';
export * from './lib/positions-data-providers';
export * from './lib/positions-multi-key';
export * from './lib/margin-data-provider';
export * from './lib/positions-table';
export * from './lib/use-market-margin';
+5
View File
@@ -9,6 +9,9 @@ fragment PositionFields on Position {
market {
id
}
party {
id
}
}
query Positions($partyId: ID!) {
@@ -34,6 +37,8 @@ subscription PositionsSubscription($partyId: ID!) {
marketId
lossSocializationAmount
positionStatus
partyId
marketId
}
}
@@ -0,0 +1,43 @@
fragment PositionMultiFields on Position {
realisedPNL
openVolume
unrealisedPNL
averageEntryPrice
updatedAt
positionStatus
lossSocializationAmount
market {
id
}
party {
id
}
}
query PositionsMulti($partyIds: [ID!]!) {
positions(filter: { partyIds: $partyIds }) {
edges {
node {
...PositionMultiFields
}
}
}
}
query MarketName($marketId: ID!) {
market(id: $marketId) {
id
tradableInstrument {
instrument {
code
}
}
}
}
query MarketDecimals($marketId: ID!) {
market(id: $marketId) {
id
decimalPlaces
}
}
+8 -3
View File
@@ -3,21 +3,21 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } };
export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string }, party: { __typename?: 'Party', id: string } };
export type PositionsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string }, party: { __typename?: 'Party', id: string } } }> | null } | null } | null };
export type PositionsSubscriptionSubscriptionVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PositionsSubscriptionSubscription = { __typename?: 'Subscription', positions: Array<{ __typename?: 'PositionUpdate', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, marketId: string, lossSocializationAmount: string, positionStatus: Types.PositionStatus }> };
export type PositionsSubscriptionSubscription = { __typename?: 'Subscription', positions: Array<{ __typename?: 'PositionUpdate', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, marketId: string, lossSocializationAmount: string, positionStatus: Types.PositionStatus, partyId: string }> };
export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } };
@@ -57,6 +57,9 @@ export const PositionFieldsFragmentDoc = gql`
market {
id
}
party {
id
}
}
`;
export const MarginFieldsFragmentDoc = gql`
@@ -126,6 +129,8 @@ export const PositionsSubscriptionDocument = gql`
marketId
lossSocializationAmount
positionStatus
partyId
marketId
}
}
`;
+160
View File
@@ -0,0 +1,160 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PositionMultiFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string }, party: { __typename?: 'Party', id: string } };
export type PositionsMultiQueryVariables = Types.Exact<{
partyIds: Array<Types.Scalars['ID']> | Types.Scalars['ID'];
}>;
export type PositionsMultiQuery = { __typename?: 'Query', positions?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string }, party: { __typename?: 'Party', id: string } } }> | null } | null };
export type MarketNameQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketNameQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', code: string } } } | null };
export type MarketDecimalsQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
}>;
export type MarketDecimalsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number } | null };
export const PositionMultiFieldsFragmentDoc = gql`
fragment PositionMultiFields on Position {
realisedPNL
openVolume
unrealisedPNL
averageEntryPrice
updatedAt
positionStatus
lossSocializationAmount
market {
id
}
party {
id
}
}
`;
export const PositionsMultiDocument = gql`
query PositionsMulti($partyIds: [ID!]!) {
positions(filter: {partyIds: $partyIds}) {
edges {
node {
...PositionMultiFields
}
}
}
}
${PositionMultiFieldsFragmentDoc}`;
/**
* __usePositionsMultiQuery__
*
* To run a query within a React component, call `usePositionsMultiQuery` and pass it any options that fit your needs.
* When your component renders, `usePositionsMultiQuery` 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 } = usePositionsMultiQuery({
* variables: {
* partyIds: // value for 'partyIds'
* },
* });
*/
export function usePositionsMultiQuery(baseOptions: Apollo.QueryHookOptions<PositionsMultiQuery, PositionsMultiQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PositionsMultiQuery, PositionsMultiQueryVariables>(PositionsMultiDocument, options);
}
export function usePositionsMultiLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PositionsMultiQuery, PositionsMultiQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PositionsMultiQuery, PositionsMultiQueryVariables>(PositionsMultiDocument, options);
}
export type PositionsMultiQueryHookResult = ReturnType<typeof usePositionsMultiQuery>;
export type PositionsMultiLazyQueryHookResult = ReturnType<typeof usePositionsMultiLazyQuery>;
export type PositionsMultiQueryResult = Apollo.QueryResult<PositionsMultiQuery, PositionsMultiQueryVariables>;
export const MarketNameDocument = gql`
query MarketName($marketId: ID!) {
market(id: $marketId) {
id
tradableInstrument {
instrument {
code
}
}
}
}
`;
/**
* __useMarketNameQuery__
*
* To run a query within a React component, call `useMarketNameQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketNameQuery` 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 } = useMarketNameQuery({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useMarketNameQuery(baseOptions: Apollo.QueryHookOptions<MarketNameQuery, MarketNameQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketNameQuery, MarketNameQueryVariables>(MarketNameDocument, options);
}
export function useMarketNameLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketNameQuery, MarketNameQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketNameQuery, MarketNameQueryVariables>(MarketNameDocument, options);
}
export type MarketNameQueryHookResult = ReturnType<typeof useMarketNameQuery>;
export type MarketNameLazyQueryHookResult = ReturnType<typeof useMarketNameLazyQuery>;
export type MarketNameQueryResult = Apollo.QueryResult<MarketNameQuery, MarketNameQueryVariables>;
export const MarketDecimalsDocument = gql`
query MarketDecimals($marketId: ID!) {
market(id: $marketId) {
id
decimalPlaces
}
}
`;
/**
* __useMarketDecimalsQuery__
*
* To run a query within a React component, call `useMarketDecimalsQuery` and pass it any options that fit your needs.
* When your component renders, `useMarketDecimalsQuery` 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 } = useMarketDecimalsQuery({
* variables: {
* marketId: // value for 'marketId'
* },
* });
*/
export function useMarketDecimalsQuery(baseOptions: Apollo.QueryHookOptions<MarketDecimalsQuery, MarketDecimalsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<MarketDecimalsQuery, MarketDecimalsQueryVariables>(MarketDecimalsDocument, options);
}
export function useMarketDecimalsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketDecimalsQuery, MarketDecimalsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<MarketDecimalsQuery, MarketDecimalsQueryVariables>(MarketDecimalsDocument, options);
}
export type MarketDecimalsQueryHookResult = ReturnType<typeof useMarketDecimalsQuery>;
export type MarketDecimalsLazyQueryHookResult = ReturnType<typeof useMarketDecimalsLazyQuery>;
export type MarketDecimalsQueryResult = Apollo.QueryResult<MarketDecimalsQuery, MarketDecimalsQueryVariables>;
@@ -0,0 +1,159 @@
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
useMarketDecimalsQuery,
useMarketNameQuery,
usePositionsMultiQuery,
} from './__generated__/PositionsMulti';
import { AgGridLazy } from '@vegaprotocol/datagrid';
import { useEffect, useMemo } from 'react';
import type {
PositionsSubscriptionSubscription,
PositionsSubscriptionSubscriptionVariables,
} from './__generated__/Positions';
import { PositionsSubscriptionDocument } from './__generated__/Positions';
import { addDecimalsFormatNumber, truncateByChars } from '@vegaprotocol/utils';
import { useApolloClient } from '@apollo/client';
export const PositionsMultiKey = () => {
const { pubKeys } = useVegaWallet();
const { data } = usePositions();
const colDefs = useMemo(() => {
return [
{
field: 'party.id',
valueFormatter: ({ value }) => {
const truncated = truncateByChars(value);
const pk = pubKeys?.find((pk) => pk.publicKey === value);
return pk ? pk.name + ' ' + truncated : truncated;
},
},
{
headerName: 'Market',
field: 'market.id',
cellRenderer: ({ value }) => {
return <MarketCell id={value} />;
},
},
{
field: 'openVolume',
},
{
field: 'unrealisedPNL',
cellRenderer: ({ data }) => {
return (
<PNLCell marketId={data.market.id} value={data.unrealisedPNL} />
);
},
},
{
field: 'realisedPNL',
cellRenderer: ({ data }) => {
return <PNLCell marketId={data.market.id} value={data.realisedPNL} />;
},
},
{
field: 'updatedAt',
},
];
}, [pubKeys]);
const rowData = data?.positions?.edges?.length
? data.positions.edges.map((e) => e.node)
: [];
return (
<AgGridLazy
getRowId={({ data }) => `${data.party.id}:${data.market.id}`}
style={{ width: '100%', height: '100%' }}
columnDefs={colDefs}
rowData={rowData}
/>
);
};
const usePositions = () => {
const { pubKeys } = useVegaWallet();
const client = useApolloClient();
const { data, loading, error } = usePositionsMultiQuery({
variables: {
partyIds: pubKeys ? pubKeys.map((pk) => pk.publicKey) : [],
},
skip: !pubKeys || pubKeys.length === 0,
});
useEffect(() => {
if (!pubKeys?.length) return;
const subs = pubKeys.map((p) => {
return client
.subscribe<
PositionsSubscriptionSubscription,
PositionsSubscriptionSubscriptionVariables
>({
query: PositionsSubscriptionDocument,
variables: {
partyId: p.publicKey,
},
// no cache as we only want to store data in the root Position query,
// we modify this cache entry directly below
fetchPolicy: 'no-cache',
})
.subscribe(({ data }) => {
data?.positions.forEach((position) => {
const id = client.cache.identify({
__typename: 'Position',
party: { id: position.partyId },
market: { id: position.marketId },
});
client.cache.modify({
id,
fields: {
realisedPNL: () => position.realisedPNL,
unrealisedPNL: () => position.unrealisedPNL,
openVolume: () => position.openVolume,
averageEntryPrice: () => position.averageEntryPrice,
positionStatus: () => position.positionStatus,
lossSocializationAmount: () => position.lossSocializationAmount,
updatedAt: () => position.updatedAt,
},
});
});
});
});
return () => {
subs.forEach((sub) => {
sub.unsubscribe();
});
};
}, [pubKeys, client]);
return { data, loading, error };
};
const MarketCell = ({ id }: { id: string }) => {
const { data } = useMarketNameQuery({
variables: {
marketId: id,
},
// We should cache all static market data higher up the render tree so this
// can be cache only
// fetchPolicy: 'cache-only',
});
if (!data?.market) return <span>-</span>;
return <span>{data.market.tradableInstrument.instrument.code}</span>;
};
const PNLCell = ({ marketId, value }: { marketId: string; value: string }) => {
const { data } = useMarketDecimalsQuery({
variables: {
marketId,
},
// We should cache all static market data higher up the render tree so this
// can be cache only
// fetchPolicy: 'cache-only'
});
if (!data?.market) return <span>-</span>;
return (
<span>{addDecimalsFormatNumber(value, data.market.decimalPlaces)}</span>
);
};
@@ -37,7 +37,7 @@ export const useColumnDefs = () => {
colId: 'market',
headerName: t('Market'),
field: 'terms.change.instrument.code',
minWidth: 150,
width: 150,
cellStyle: { lineHeight: '14px' },
cellRenderer: ({
data,
@@ -13,6 +13,16 @@ fragment NewMarketFields on NewMarket {
quoteName
dataSourceSpecForSettlementData {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
@@ -43,6 +53,16 @@ fragment NewMarketFields on NewMarket {
}
dataSourceSpecForTradingTermination {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
@@ -125,6 +145,16 @@ fragment UpdateMarketFields on UpdateMarket {
quoteName
dataSourceSpecForSettlementData {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
@@ -155,6 +185,16 @@ fragment UpdateMarketFields on UpdateMarket {
}
dataSourceSpecForTradingTermination {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
File diff suppressed because one or more lines are too long
@@ -76,12 +76,20 @@ export const marketUpdateProposal: ProposalListFieldsFragment = {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
},
@@ -177,12 +185,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -267,12 +283,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -357,12 +381,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -447,12 +479,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -537,12 +577,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -627,12 +675,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -717,12 +773,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -807,12 +871,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -897,12 +969,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -987,12 +1067,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -1077,12 +1165,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -1167,12 +1263,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -1257,12 +1361,20 @@ const proposalListFields: ProposalListFieldsFragment[] = [
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
@@ -133,11 +133,19 @@ const generateUpdateMarketProposal = (
dataSourceSpecForSettlementData: {
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename:
@@ -1,5 +1,4 @@
import classNames from 'classnames';
import { useMemo } from 'react';
import Highlighter from 'react-syntax-highlighter';
export const SyntaxHighlighter = ({
@@ -9,13 +8,6 @@ export const SyntaxHighlighter = ({
data: unknown;
size?: 'smaller' | 'default';
}) => {
const parsedData = useMemo(() => {
try {
return JSON.stringify(data, null, ' ');
} catch (e) {
return 'Unable to parse data';
}
}, [data]);
return (
<div
className={classNames('syntax-highlighter-wrapper', {
@@ -23,7 +15,7 @@ export const SyntaxHighlighter = ({
})}
>
<Highlighter language="json" useInlineStyles={false}>
{parsedData}
{JSON.stringify(data, null, ' ')}
</Highlighter>
</div>
);