Compare commits

..
Author SHA1 Message Date
Matthew Russell 3ea341a818 chore: only revalidate once form is submitted 2023-03-07 17:08:05 -08:00
Matthew Russell 86ce4d28fe Merge branch 'fix/order-store-watch' of github.com:vegaprotocol/frontend-monorepo into fix/order-store-watch 2023-03-07 14:55:37 -08:00
Matthew Russell ba2341ea57 chore: ignore mocks from deal ticket build 2023-03-07 14:39:48 -08:00
Madalina Raicu ad8eb7dd79 fix(trading): remove experimental deal ticket 2023-03-07 13:46:57 +00:00
Madalina Raicu e07fce3bb2 fix(trading): update trades container to use order store 2023-03-07 13:28:17 +00:00
Madalina Raicu 43d6c787bd Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into fix/order-store-watch 2023-03-07 13:18:20 +00:00
Madalina Raicu 4c6a46ad40 Merge branch 'fix/order-store-watch' of github.com:vegaprotocol/frontend-monorepo into fix/order-store-watch
# Conflicts:
#	libs/deal-ticket/src/components/deal-ticket/deal-ticket-amount.tsx
#	libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx
#	libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx
#	libs/deal-ticket/src/hooks/use-order-form.ts
#	libs/market-depth/src/lib/orderbook-manager.tsx
#	libs/orders/src/lib/order-hooks/index.ts
#	libs/orders/src/lib/order-hooks/use-order-store.ts
#	libs/orders/src/lib/order-hooks/use-persisted-order.ts
#
# It looks like you may be committing a merge.
# If this is not correct, please run
#	git update-ref -d MERGE_HEAD
# and try again.

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch fix/order-store-watch
# Your branch and 'origin/fix/order-store-watch' have diverged,
# and have 7 and 19 different commits each, respectively.
#   (use "git pull" to merge the remote branch into yours)
#
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#	new file:   libs/deal-ticket/__mocks__/zustand.ts
#	modified:   libs/deal-ticket/src/components/deal-ticket-validation/zero-balance-error.tsx
#	modified:   libs/deal-ticket/src/components/deal-ticket/deal-ticket-amount.tsx
#	modified:   libs/deal-ticket/src/components/deal-ticket/deal-ticket-container.tsx
#	modified:   libs/deal-ticket/src/components/deal-ticket/deal-ticket.spec.tsx
#	modified:   libs/deal-ticket/src/components/deal-ticket/deal-ticket.tsx
#	modified:   libs/deal-ticket/src/components/deal-ticket/expiry-selector.tsx
#	new file:   libs/deal-ticket/src/hooks/use-order-form.spec.ts
#	modified:   libs/deal-ticket/src/hooks/use-order-form.ts
#	modified:   libs/environment/tsconfig.spec.json
#	modified:   libs/market-depth/src/lib/orderbook-manager.tsx
#	new file:   libs/orders/__mocks__/zustand.ts
#	modified:   libs/orders/src/lib/components/order-list-manager/order-list-manager.spec.tsx
#	modified:   libs/orders/src/lib/components/order-list/order-list.spec.tsx
#	modified:   libs/orders/src/lib/order-hooks/index.ts
#	new file:   libs/orders/src/lib/order-hooks/use-order-store.spec.ts
#	modified:   libs/orders/src/lib/order-hooks/use-order-store.ts
#	deleted:    libs/orders/src/lib/order-hooks/use-persisted-order.ts
#	modified:   libs/orders/tsconfig.lib.json
#	modified:   libs/wallet/src/utils.ts
#
# Changes not staged for commit:
#	modified:   nx.json
#	modified:   tsconfig.base.json
#
2023-03-07 13:17:58 +00:00
Madalina Raicu f299aa161b Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into fix/order-store-watch 2023-03-07 08:21:19 +00:00
Matthew Russell c35e85ef6a chore: ignore mocks from orders build 2023-03-06 22:22:51 -08:00
Matthew Russell 0fa124ed2c fix: mock theme switcher to resolve zustand mocking issue 2023-03-06 21:40:47 -08:00
Matthew Russell 6fae87d380 chore: remove accidental import 2023-03-06 16:21:36 -08:00
Matthew Russell a2feff77dc chore: clean up order store tests by mocking zustand correctly 2023-03-06 16:07:48 -08:00
Matthew Russell d13d67d9c3 chore: add comment rename store selector result for clarity 2023-03-06 15:46:36 -08:00
Matthew Russell 3592971b2b chore: add guard for order being undefined 2023-03-06 15:38:26 -08:00
Matthew Russell 20dfd7c22a chore: tidy types for onClickCollateral 2023-03-06 15:37:08 -08:00
Matthew Russell bf959c5c4e chore: remove unused type 2023-03-06 15:32:20 -08:00
Matthew Russell 594546bb06 chore: add comment about storing last used tif 2023-03-06 15:31:33 -08:00
Matthew Russell 288bea44db chore: tidy expiry field unused props 2023-03-06 15:26:15 -08:00
Matthew Russell 9fe2a7f55c chore: delete unused temp code 2023-03-06 15:23:53 -08:00
Matthew Russell 32cb10e82e test: add test for form store hook 2023-03-06 15:01:59 -08:00
Matthew Russell ba82a52855 chore: remove persisted order store 2023-03-06 14:35:28 -08:00
Matthew Russell fd9992189f fix: update tests to use userEvent 2023-03-06 14:29:11 -08:00
Matthew Russell 180649e2a8 test: add tests for order store 2023-03-06 14:29:11 -08:00
Matthew Russell 740237f355 chore: tidy types 2023-03-06 14:29:11 -08:00
Matthew Russell 74e814c5fb feat: update store 2023-03-06 14:29:11 -08:00
Matthew Russell beadef6aec feat: add additional hook to handle order creation if not existing 2023-03-06 14:29:11 -08:00
Matthew Russell 26afa3210d chore: deal ticket state refactor 2023-03-06 14:29:11 -08:00
Matthew Russell 1212645d87 wip: fix me 2023-03-03 09:19:39 -08:00
Matthew Russell 6ffc22a940 feat: add additional hook to handle order creation if not existing 2023-03-02 09:50:51 -08:00
Matthew Russell a563a87daa chore: deal ticket state refactor 2023-03-01 15:49:12 -08:00
169 changed files with 2478 additions and 3836 deletions
+1 -1
View File
@@ -87,7 +87,7 @@ jobs:
run: ls -alsh /home/runner/.vegacapsule/testnet/logs/
- uses: actions/upload-artifact@v3
if: ${{ failure() }}
if: ${{ always() }}
with:
name: logs-${{ matrix.project }}
path: /home/runner/.vegacapsule/testnet/logs
@@ -22,6 +22,7 @@
"governance.proposal.updateAsset.minProposerBalance",
"governance.proposal.updateAsset.minVoterBalance",
"governance.proposal.updateAsset.requiredParticipation",
"governance.proposal.updateMarket.minProposerEquityLikeShare",
"market.fee.factors.infrastructureFee",
"market.fee.factors.makerFee",
"market.liquidity.bondPenaltyParameter",
@@ -76,7 +77,6 @@
"governance.proposal.updateMarket.requiredParticipationLP",
"governance.proposal.updateNetParam.requiredMajority",
"governance.proposal.updateNetParam.requiredParticipation",
"governance.proposal.updateMarket.minProposerEquityLikeShare",
"validators.vote.required"
],
"duration": [
@@ -4,8 +4,9 @@ import {
getMarketExpiryDateFormatted,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/market-info';
import type { MarketInfoNoCandlesQuery } from '@vegaprotocol/market-info';
import { MarketInfoTable } from '@vegaprotocol/market-info';
import pick from 'lodash/pick';
import {
MarketStateMapping,
MarketTradingModeMapping,
@@ -16,7 +17,11 @@ import BigNumber from 'bignumber.js';
import { useMemo } from 'react';
import { Link } from 'react-router-dom';
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
export const MarketDetails = ({
market,
}: {
market: MarketInfoNoCandlesQuery['market'];
}) => {
const quoteUnit = market?.tradableInstrument.instrument.product.quoteName;
const assetId = useMemo(
() => market?.tradableInstrument.instrument.product?.settlementAsset.id,
@@ -27,9 +32,7 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
if (!market) return null;
const keyDetails = {
decimalPlaces: market.decimalPlaces,
positionDecimalPlaces: market.positionDecimalPlaces,
tradingMode: market.tradingMode,
...pick(market, 'decimalPlaces', 'positionDecimalPlaces', 'tradingMode'),
state: MarketStateMapping[market.state],
};
const assetDecimals =
+1 -1
View File
@@ -21,7 +21,7 @@ type NavStore = {
hide: () => void;
};
export const useNavStore = create<NavStore>()((set, get) => ({
export const useNavStore = create<NavStore>((set, get) => ({
open: false,
toggle: () => set({ open: !get().open }),
hide: () => set({ open: false }),
@@ -1,26 +0,0 @@
import { t } from '@vegaprotocol/i18n';
interface CancelSummaryProps {
orderId?: string;
marketId?: string;
}
/**
* Simple component for rendering a reasonable string from an order cancellation
*/
export const CancelSummary = ({ orderId, marketId }: CancelSummaryProps) => {
return <span className="font-bold">{getLabel(orderId, marketId)}</span>;
};
export function getLabel(
orderId: string | undefined,
marketId: string | undefined
): string {
if (!orderId && !marketId) {
return t('All orders');
} else if (marketId && !orderId) {
return t('All in market');
}
return '-';
}
@@ -2,7 +2,6 @@ import type { BatchCancellationInstruction } from '../../../../routes/types/bloc
import { TxOrderType } from '../../tx-order-type';
import { MarketLink } from '../../../links';
import OrderSummary from '../../../order-summary/order-summary';
import { CancelSummary } from '../../../order-summary/order-cancellation';
interface BatchCancelProps {
index: number;
@@ -20,14 +19,7 @@ export const BatchCancel = ({ index, submission }: BatchCancelProps) => {
<TxOrderType orderType={'OrderCancellation'} />
</td>
<td>
{submission.orderId ? (
<OrderSummary id={submission.orderId} modifier="cancelled" />
) : (
<CancelSummary
orderId={submission.orderId}
marketId={submission.marketId}
/>
)}
<OrderSummary id={submission.orderId} modifier="cancelled" />
</td>
<td>
<MarketLink id={submission.marketId} />
@@ -5,8 +5,6 @@ import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import DeterministicOrderDetails from '../../order-details/deterministic-order-details';
import { CancelSummary } from '../../order-summary/order-cancellation';
import Hash from '../../links/hash';
interface TxDetailsOrderCancelProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -26,8 +24,8 @@ export const TxDetailsOrderCancel = ({
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const marketId: string = txData.command.orderCancellation.marketId;
const orderId: string = txData.command.orderCancellation.orderId;
const marketId: string = txData.command.orderCancellation.marketId || '-';
const orderId: string = txData.command.orderCancellation.orderId || '-';
return (
<>
@@ -40,24 +38,18 @@ export const TxDetailsOrderCancel = ({
<TableRow modifier="bordered">
<TableCell>{t('Order')}</TableCell>
<TableCell>
{orderId ? (
<Hash text={orderId} />
) : (
<CancelSummary orderId={orderId} marketId={marketId} />
)}
<code>{orderId}</code>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
<TableCell>
<MarketLink id={marketId} />
</TableCell>
</TableRow>
{marketId ? (
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
<TableCell>
<MarketLink id={marketId} />
</TableCell>
</TableRow>
) : null}
</TableWithTbody>
{orderId ? <DeterministicOrderDetails id={orderId} /> : null}
{orderId !== '-' ? <DeterministicOrderDetails id={orderId} /> : null}
</>
);
};
@@ -12,7 +12,6 @@ export const Proposals = () => {
const { data, loading, error } = useDataProvider({
dataProvider: proposalsDataProvider,
variables: {},
});
useDocumentTitle([t('Governance Proposals')]);
@@ -1,14 +1,14 @@
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { MarketDetails } from '../../components/markets/market-details';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import compact from 'lodash/compact';
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
import { marketInfoProvider } from '@vegaprotocol/market-info';
import { marketInfoNoCandlesDataProvider } from '@vegaprotocol/market-info';
import { PageTitle } from '../../components/page-helpers/page-title';
export const MarketPage = () => {
@@ -16,17 +16,24 @@ export const MarketPage = () => {
const { marketId } = useParams<{ marketId: string }>();
const variables = useMemo(
() => ({
marketId,
}),
[marketId]
);
const { data, loading, error } = useDataProvider({
dataProvider: marketInfoProvider,
dataProvider: marketInfoNoCandlesDataProvider,
skipUpdates: true,
variables: {
marketId: marketId || '',
skip: !marketId,
},
variables,
});
useDocumentTitle(
compact(['Market details', data?.tradableInstrument.instrument.name])
compact([
'Market details',
data?.market?.tradableInstrument.instrument.name,
])
);
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
@@ -36,10 +43,10 @@ export const MarketPage = () => {
<section className="relative">
<PageTitle
data-testid="markets-heading"
title={data?.tradableInstrument.instrument.name || ''}
title={data?.market?.tradableInstrument.instrument.name || ''}
actions={
<Button
disabled={!data}
disabled={!data?.market}
size="xs"
onClick={() => setDialogOpen(true)}
>
@@ -53,14 +60,14 @@ export const MarketPage = () => {
loading={loading}
error={error}
>
{data && <MarketDetails market={data} />}
<MarketDetails market={data?.market} />
</AsyncRenderer>
</section>
<JsonViewerDialog
open={dialogOpen}
onChange={(isOpen) => setDialogOpen(isOpen)}
title={data?.tradableInstrument.instrument.name || ''}
content={data}
title={data?.market?.tradableInstrument.instrument.name || ''}
content={data?.market}
/>
</>
);
@@ -13,7 +13,6 @@ export const MarketsPage = () => {
const { data, loading, error } = useDataProvider({
dataProvider: marketsProvider,
variables: undefined,
skipUpdates: true,
});
@@ -31,7 +31,6 @@ const PERCENTAGE_PARAMS = [
'governance.proposal.updateMarket.requiredParticipationLP',
'governance.proposal.updateNetParam.requiredMajority',
'governance.proposal.updateNetParam.requiredParticipation',
'governance.proposal.updateMarket.minProposerEquityLikeShare',
'validators.vote.required',
];
@@ -22,7 +22,7 @@
"filters": [
{
"key": {
"name": "prices.BTC.value",
"name": "prices.ETH.value",
"type": "TYPE_INTEGER",
"numberDecimalPlaces": "0"
},
@@ -38,34 +38,30 @@
}
},
"dataSourceSpecForTradingTermination": {
"external": {
"oracle": {
"signers": [
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "trading.terminated.ETH5",
"type": "TYPE_BOOLEAN"
},
"conditions": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "trading.terminated.ETH5",
"type": "TYPE_BOOLEAN"
},
"conditions": [
{
"operator": "OPERATOR_EQUALS",
"value": "true"
}
]
"operator": "OPERATOR_EQUALS",
"value": "true"
}
]
}
}
]
},
"dataSourceSpecBinding": {
"settlementDataProperty": "prices.BTC.value",
"settlementPriceProperty": "prices.BTC.value",
"tradingTerminationProperty": "trading.terminated.ETH5"
}
}
@@ -1,86 +0,0 @@
{
"lpPriceRange": "11",
"instrument": {
"code": "Token.24h",
"future": {
"quoteName": "fBTC",
"dataSourceSpecForSettlementData": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "prices.BTC.value",
"type": "TYPE_INTEGER"
},
"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_GREATER_THAN_OR_EQUAL",
"value": "1648684800000000000"
}
]
}
]
}
}
},
"dataSourceSpecBinding": {
"settlementDataProperty": "prices.BTC.value",
"tradingTerminationProperty": "trading.terminated.ETH5"
}
}
},
"metadata": ["sector:energy", "sector:food", "source:docs.vega.xyz"],
"priceMonitoringParameters": {
"triggers": [
{
"horizon": "43200",
"probability": "0.9999999",
"auctionExtension": "600"
}
]
},
"logNormal": {
"tau": 0.0001140771161,
"riskAversionParameter": 0.001,
"params": {
"mu": 0,
"r": 0.016,
"sigma": 0.3
}
}
}
@@ -1,76 +1,62 @@
{
"lpPriceRange": "10",
"instrument": {
"code": "TEST.24h",
"future": {
"quoteName": "fUSDC",
"settlementDataDecimals": 5,
"dataSourceSpecForSettlementData": {
"external": {
"oracle": {
"signers": [
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "prices.ETH.value",
"type": "TYPE_INTEGER"
},
"conditions": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "prices.ETH.value",
"type": "TYPE_INTEGER",
"numberDecimalPlaces": "0"
},
"conditions": [
{
"operator": "OPERATOR_GREATER_THAN",
"value": "0"
}
]
"operator": "OPERATOR_GREATER_THAN",
"value": "0"
}
]
}
}
]
},
"dataSourceSpecForTradingTermination": {
"external": {
"oracle": {
"signers": [
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "trading.terminated.ETH5",
"type": "TYPE_BOOLEAN"
},
"conditions": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "trading.terminated.ETH5",
"type": "TYPE_BOOLEAN"
},
"conditions": [
{
"operator": "OPERATOR_EQUALS",
"value": "true"
}
]
"operator": "OPERATOR_GREATER_THAN_OR_EQUAL",
"value": "1648684800000000000"
}
]
}
}
]
},
"dataSourceSpecBinding": {
"settlementDataProperty": "prices.ETH.value",
"settlementPriceProperty": "prices.ETH.value",
"tradingTerminationProperty": "trading.terminated.ETH5"
}
}
},
"metadata": [
"sector:energy",
"sector:food",
"source:docs.vega.xyz",
"test:update"
],
"metadata": ["sector:energy", "sector:food", "source:docs.vega.xyz"],
"priceMonitoringParameters": {
"triggers": [
{
@@ -80,14 +66,6 @@
}
]
},
"liquidityMonitoringParameters": {
"targetStakeParameters": {
"timeWindow": "3600",
"scalingFactor": 10
},
"triggeringRatio": "0.7",
"auctionExtension": "1"
},
"logNormal": {
"tau": 0.0001140771161,
"riskAversionParameter": 0.001,
@@ -26,10 +26,6 @@ const enactmentDeadlineError =
'[data-testid="enactment-before-voting-deadline"]';
const proposalDownloadBtn = '[data-testid="proposal-download-json"]';
const feedbackError = '[data-testid="Error"]';
const viewProposalBtn = 'view-proposal-btn';
const liquidityVoteStatus = 'liquidity-votes-status';
const tokenVoteStatus = 'token-votes-status';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
@@ -49,7 +45,6 @@ context(
{ tags: '@slow' },
function () {
before('connect wallets and set approval limit', function () {
cy.createMarket();
cy.visit('/');
cy.vega_wallet_set_specified_approval_amount('1000');
});
@@ -70,7 +65,7 @@ context(
// 3002-PROP-007
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalParameterSelect).find('option').should('have.length', 117);
cy.get(proposalParameterSelect).find('option').should('have.length', 116);
cy.get(proposalParameterSelect).select(
// 3007-PNEC-002
'governance_proposal_asset_minEnact'
@@ -180,8 +175,9 @@ context(
cy.get(enactmentDeadlineError).should('not.exist');
});
// 3003-PMAN-001
it('Able to submit valid new market proposal', function () {
// Skipping because unclear what the required json is yet for new market proposal, will update once docs have been updated
// 3003-todo-PMAN-001
it.skip('Able to submit valid new market proposal', function () {
cy.go_to_make_new_proposal(governanceProposalType.NEW_MARKET);
cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
@@ -203,7 +199,6 @@ context(
cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
newMarketProposal.invalid = 'I am an invalid field';
let newMarketPayload = JSON.stringify(newMarketProposal);
cy.get(newProposalTerms).type(newMarketPayload, {
parseSpecialCharSequences: false,
@@ -214,66 +209,11 @@ context(
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Invalid params: the transaction is not a valid Vega command: unknown field "invalid" in vega.NewMarket'
'Invalid params: the transaction is malformed'
);
});
// Will fail if run after 'Able to submit update market proposal and vote for proposal'
// 3002-PROP-022
it('Unable to submit update market proposal without equity-like share in the market', function () {
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal - rejected');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalMarketSelect).select('Test market 1');
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
let newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
cy.getByTestId('dialog-content')
.find('p')
.should('have.text', 'PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
cy.ensure_specified_unstaked_tokens_are_associated('1');
});
// 3002-PROP-020
it('Unable to submit update market proposal without minimum amount of tokens', function () {
cy.vega_wallet_teardown();
cy.vega_wallet_faucet_assets_without_check(
'fUSDC',
'1000000',
vegaWalletPublicKey
);
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal - rejected');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalMarketSelect).select('Test market 1');
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
let newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Network error: the network blocked the transaction through the spam protection: party has insufficient associated governance tokens in their staking account to submit proposal request (ABCI code 89)'
);
});
// 3001-VOTE-092
it('Able to submit update market proposal and vote for proposal', function () {
cy.vega_wallet_faucet_assets_without_check(
'fUSDC',
'1000000',
vegaWalletPublicKey
);
it.skip('Able to submit update market proposal', function () {
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
@@ -282,10 +222,6 @@ context(
cy.get('dd').eq(0).should('have.text', 'Test market 1');
cy.get('dd').eq(1).should('have.text', 'TEST.24h');
cy.get('dd').eq(2).should('not.be.empty');
cy.get('dd').eq(2).invoke('text').as('EnactedMarketId');
});
cy.get('@EnactedMarketId').then((marketId) => {
cy.VegaWalletSubmitLiquidityProvision(marketId, '1');
});
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
let newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
@@ -296,34 +232,6 @@ context(
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
cy.navigate_to('proposals');
cy.get('@EnactedMarketId').then((marketId) => {
cy.contains(marketId)
.parentsUntil(proposalListItem)
.within(() => {
cy.getByTestId(viewProposalBtn).click();
});
});
cy.getByTestId(liquidityVoteStatus).should(
'contain.text',
'Currently expected to fail'
);
cy.getByTestId(tokenVoteStatus).should(
'contain.text',
'Currently expected to fail'
);
cy.vote_for_proposal('for');
cy.getByTestId(liquidityVoteStatus).should(
'contain.text',
'Currently expected to pass'
);
cy.getByTestId(tokenVoteStatus).should(
'contain.text',
'Currently expected to pass'
);
cy.get_proposal_information_from_table('Expected to pass')
.contains('👍 by Token vote')
.should('be.visible');
});
// 3001-VOTE-026 3001-VOTE-027 3001-VOTE-028 3001-VOTE-095 3001-VOTE-096 3005-PASN-001
@@ -390,7 +298,7 @@ context(
.parentsUntil(proposalListItem)
.within(() => {
cy.get(proposalDetails).should('contain.text', assetId); // 3001-VOTE-029
cy.getByTestId(viewProposalBtn).click();
cy.getByTestId('view-proposal-btn').click();
});
});
cy.get_proposal_information_from_table('Proposed enactment') // 3001-VOTE-044
@@ -82,10 +82,9 @@ Cypress.Commands.add(
Cypress.Commands.add(
'get_submitted_proposal_from_proposal_list',
(proposalTitle) => {
cy.get_proposal_id_from_list(proposalTitle).then(() => {
cy.get('@proposalIdText').then((proposalId) => {
return cy.get(`#${proposalId}`);
});
cy.get_proposal_id_from_list(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
return cy.get(`#${proposalId}`);
});
}
);
@@ -9,28 +9,26 @@ export type PendingTxsStore = {
resetPendingTxs: () => void;
};
export const usePendingBalancesStore = create<PendingTxsStore>()(
(set, get) => ({
pendingBalances: [],
addPendingTxs: (event: Event[]) => {
set({
pendingBalances: uniqBy(
[...get().pendingBalances, ...event],
'transactionHash'
export const usePendingBalancesStore = create<PendingTxsStore>((set, get) => ({
pendingBalances: [],
addPendingTxs: (event: Event[]) => {
set({
pendingBalances: uniqBy(
[...get().pendingBalances, ...event],
'transactionHash'
),
});
},
removePendingTx: (event: Event) => {
set({
pendingBalances: [
...get().pendingBalances.filter(
({ transactionHash }) => transactionHash !== event.transactionHash
),
});
},
removePendingTx: (event: Event) => {
set({
pendingBalances: [
...get().pendingBalances.filter(
({ transactionHash }) => transactionHash !== event.transactionHash
),
],
});
},
resetPendingTxs: () => {
set({ pendingBalances: [] });
},
})
);
],
});
},
resetPendingTxs: () => {
set({ pendingBalances: [] });
},
}));
@@ -38,7 +38,7 @@ export interface RefreshBalances {
vestingAssociatedBalance: BigNumber;
}
export const useBalances = create<BalancesStore>()((set) => ({
export const useBalances = create<BalancesStore>((set) => ({
associationBreakdown: {
stakingAssociations: {},
vestingAssociations: {},
@@ -1,7 +1,7 @@
import { toBigNum } from '@vegaprotocol/utils';
import type { TrancheServiceResponse } from '@vegaprotocol/smart-contracts';
import type BigNumber from 'bignumber.js';
import { create } from 'zustand';
import create from 'zustand';
import { ENV } from '../../config';
export interface Tranche {
@@ -36,7 +36,7 @@ export type TranchesStore = {
const secondsToDate = (seconds: number) => new Date(seconds * 1000);
export const useTranches = create<TranchesStore>()((set) => ({
export const useTranches = create<TranchesStore>((set) => ({
tranches: null,
loading: false,
error: null,
@@ -56,7 +56,7 @@ export const VoteDetails = ({
{proposalType === ProposalType.PROPOSAL_UPDATE_MARKET && (
<section>
<SubHeading title={t('liquidityVotes')} />
<p data-testid="liquidity-votes-status">
<p>
<span>
<CurrentProposalStatus proposal={proposal} />
</span>
@@ -105,7 +105,7 @@ export const VoteDetails = ({
)}
<section data-testid="votes-table">
<SubHeading title={t('tokenVotes')} />
<p data-testid="token-votes-status">
<p>
<span>
<CurrentProposalStatus proposal={proposal} />
</span>
+25 -22
View File
@@ -1,4 +1,5 @@
import type ethers from 'ethers';
import type { GetState, SetState } from 'zustand';
import { create } from 'zustand';
export interface TxData {
@@ -15,25 +16,27 @@ interface TransactionStore {
remove: (tx: TxData) => void;
}
export const useTransactionStore = create<TransactionStore>()((set, get) => ({
transactions: [],
add: (tx) => {
const { transactions } = get();
set({ transactions: [...transactions, tx] });
},
update: (tx) => {
const { transactions } = get();
set({
transactions: [
...transactions.filter((t) => t.tx.hash !== tx.tx.hash),
tx,
],
});
},
remove: (tx) => {
const { transactions } = get();
set({
transactions: transactions.filter((t) => t.tx.hash !== tx.tx.hash),
});
},
}));
export const useTransactionStore = create(
(set: SetState<TransactionStore>, get: GetState<TransactionStore>) => ({
transactions: [],
add: (tx) => {
const { transactions } = get();
set({ transactions: [...transactions, tx] });
},
update: (tx) => {
const { transactions } = get();
set({
transactions: [
...transactions.filter((t) => t.tx.hash !== tx.tx.hash),
tx,
],
});
},
remove: (tx) => {
const { transactions } = get();
set({
transactions: transactions.filter((t) => t.tx.hash !== tx.tx.hash),
});
},
})
);
@@ -1,4 +1,5 @@
import { useParams } from 'react-router-dom';
import { useMemo } from 'react';
import { makeDerivedDataProvider } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/react-helpers';
@@ -42,7 +43,7 @@ const useMarketDetails = (marketId: string | undefined) => {
const { data, loading, error } = useDataProvider({
dataProvider: lpDataProvider,
skipUpdates: true,
variables: { marketId: marketId || '' },
variables: useMemo(() => ({ marketId }), [marketId]),
});
const liquidityProviders = data?.liquidityProviders || [];
@@ -39,11 +39,14 @@ export const Last24hVolume = ({
[marketId, yTimestamp]
);
const variables24hAgo = {
marketId: marketId,
interval: Schema.Interval.INTERVAL_I1D,
since: yTimestamp,
};
const variables24hAgo = useMemo(
() => ({
marketId: marketId,
interval: Schema.Interval.INTERVAL_I1D,
since: yTimestamp,
}),
[marketId, yTimestamp]
);
const throttledSetCandles = useRef(
throttle((data: Candle[]) => {
@@ -61,7 +64,7 @@ export const Last24hVolume = ({
[throttledSetCandles]
);
const { data, error } = useDataProvider({
const { data, error } = useDataProvider<Candle[], Candle>({
dataProvider: marketCandlesProvider,
variables: variables,
update,
@@ -85,7 +88,7 @@ export const Last24hVolume = ({
[throttledSetVolumeChange]
);
useDataProvider({
useDataProvider<Candle[], Candle>({
dataProvider: marketCandlesProvider,
update: updateCandle24hAgo,
variables: variables24hAgo,
File diff suppressed because it is too large Load Diff
@@ -26,20 +26,20 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
it('market price', () => {
cy.getByTestId(marketTitle).contains('Market price').click();
validateMarketDataRow(0, 'Mark Price', '46,126.90058');
validateMarketDataRow(1, 'Best Bid Price', '44,126.90058 ');
validateMarketDataRow(2, 'Best Offer Price', '48,126.90058 ');
validateMarketDataRow(0, 'Mark Price', '0.05749');
validateMarketDataRow(1, 'Best Bid Price', '6.81765 ');
validateMarketDataRow(2, 'Best Offer Price', '6.81769 ');
validateMarketDataRow(3, 'Quote Unit', 'BTC');
});
it('market volume displayed', () => {
cy.getByTestId(marketTitle).contains('Market volume').click();
validateMarketDataRow(0, '24 Hour Volume', '1');
validateMarketDataRow(0, '24 Hour Volume', '-');
validateMarketDataRow(1, 'Open Interest', '0');
validateMarketDataRow(2, 'Best Bid Volume', '1');
validateMarketDataRow(3, 'Best Offer Volume', '3');
validateMarketDataRow(4, 'Best Static Bid Volume', '2');
validateMarketDataRow(5, 'Best Static Offer Volume', '4');
validateMarketDataRow(2, 'Best Bid Volume', '5');
validateMarketDataRow(3, 'Best Offer Volume', '1');
validateMarketDataRow(4, 'Best Static Bid Volume', '5');
validateMarketDataRow(5, 'Best Static Offer Volume', '1');
});
it('insurance pool displayed', () => {
@@ -149,9 +149,9 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
.contains(/Liquidity(?! m)/)
.click();
validateMarketDataRow(0, 'Target Stake', '10.00 tBTC');
validateMarketDataRow(1, 'Supplied Stake', '0.01 tBTC');
validateMarketDataRow(2, 'Market Value Proxy', '20.00 tBTC');
validateMarketDataRow(0, 'Target Stake', '0.56789 tBTC');
validateMarketDataRow(1, 'Supplied Stake', '0.56767 tBTC');
validateMarketDataRow(2, 'Market Value Proxy', '6.77678 tBTC');
cy.getByTestId('view-liquidity-link').should(
'have.text',
@@ -163,8 +163,8 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
cy.getByTestId(marketTitle).contains('Liquidity price range').click();
validateMarketDataRow(0, 'Liquidity Price Range', '2.00% of mid price');
validateMarketDataRow(1, 'Lowest Price', '45,204.362 BTC');
validateMarketDataRow(2, 'Highest Price', '47,049.438 BTC');
validateMarketDataRow(1, 'Lowest Price', '0.05634 BTC');
validateMarketDataRow(2, 'Highest Price', '0.05864 BTC');
});
it('oracle displayed', () => {
@@ -38,7 +38,7 @@ describe('accounts', { tags: '@smoke' }, () => {
cy.getByTestId('tab-accounts')
.get(tradingAccountRowId)
.find('[col-id="deposited"]')
.should('have.text', '100,001.01');
.should('have.text', '1,001.00');
});
describe('sorting by ag-grid columns should work well', () => {
it('sorting by asset', () => {
@@ -58,24 +58,24 @@ describe('accounts', { tags: '@smoke' }, () => {
cy.getByTestId('Collateral').click();
const marketsSortedDefault = [
'1,000.00002',
'100,001.01',
'1,001.00',
'1,000.01',
'1,000.01',
'1,000.00',
'1,000.00001',
];
const marketsSortedAsc = [
'1,000.00',
'1,000.00001',
'1,000.00002',
'1,000.01',
'100,001.01',
'1,000.01',
'1,001.00',
];
const marketsSortedDesc = [
'100,001.01',
'1,001.00',
'1,000.01',
'1,000.01',
'1,000.00002',
'1,000.00001',
'1,000.00',
];
checkSorting(
'deposited',
@@ -87,9 +87,9 @@ describe('accounts', { tags: '@smoke' }, () => {
it('sorting by used', () => {
cy.getByTestId('Collateral').click();
const marketsSortedDefault = ['0.00', '1.01', '0.01', '0.00', '0.00'];
const marketsSortedAsc = ['0.00', '0.00', '0.00', '0.01', '1.01'];
const marketsSortedDesc = ['1.01', '0.01', '0.00', '0.00', '0.00'];
const marketsSortedDefault = ['0.00', '1.00', '0.01', '0.01', '0.00'];
const marketsSortedAsc = ['0.00', '0.00', '0.01', '0.01', '1.00'];
const marketsSortedDesc = ['1.00', '0.01', '0.01', '0.00', '0.00'];
checkSorting(
'used',
marketsSortedDefault,
@@ -102,24 +102,24 @@ describe('accounts', { tags: '@smoke' }, () => {
cy.getByTestId('Collateral').click();
const marketsSortedDefault = [
'1,000.00002',
'100,000.00',
'1,000.00',
'1,000.00',
'1,000.00',
'1,000.00001',
];
const marketsSortedAsc = [
'1,000.00',
'1,000.00',
'1,000.00',
'1,000.00001',
'1,000.00002',
'100,000.00',
];
const marketsSortedDesc = [
'100,000.00',
'1,000.00002',
'1,000.00001',
'1,000.00',
'1,000.00',
'1,000.00',
];
checkSorting(
@@ -2,11 +2,7 @@ import * as Schema from '@vegaprotocol/types';
import { aliasGQLQuery, mockConnectWallet } from '@vegaprotocol/cypress';
import { testOrderSubmission } from '../support/order-validation';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import {
accountsQuery,
estimateOrderQuery,
amendGeneralAccountBalance,
} from '@vegaprotocol/mock';
import { accountsQuery, estimateOrderQuery } from '@vegaprotocol/mock';
import { createOrder } from '../support/create-order';
const orderSizeField = 'order-size';
@@ -587,10 +583,6 @@ describe('suspended market validation', { tags: '@regression' }, () => {
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY
);
const accounts = accountsQuery();
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
@@ -640,10 +632,30 @@ describe('account validation', { tags: '@regression' }, () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
const accounts = accountsQuery();
amendGeneralAccountBalance(accounts, 'market-0', '0');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
aliasGQLQuery(
req,
'Accounts',
accountsQuery({
party: {
accountsConnection: {
edges: [
{
node: {
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
balance: '0',
market: null,
asset: {
__typename: 'Asset',
id: 'asset-0',
},
},
},
],
},
},
})
);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
@@ -667,13 +679,19 @@ describe('account validation', { tags: '@regression' }, () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
const accounts = accountsQuery();
amendGeneralAccountBalance(accounts, 'market-0', '100000000');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockGQL((req) => {
aliasGQLQuery(req, 'EstimateOrder', estimateOrderQuery());
aliasGQLQuery(
req,
'EstimateOrder',
estimateOrderQuery({
estimateOrder: {
marginLevels: {
__typename: 'MarginLevels',
initialLevel: '1000000000',
},
},
})
);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
@@ -689,7 +707,7 @@ describe('account validation', { tags: '@regression' }, () => {
);
cy.getByTestId('dealticket-warning-margin').should(
'contain.text',
'You may not have enough margin available to open this position. 2,354.72283 tDAI is currently required. You have only 1,000.01 tDAI available.'
'9,999.99 tDAI is currently required. You have only 1,000.00 tDAI available.Deposit tDAI'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
cy.getByTestId('dialog-content')
@@ -31,7 +31,7 @@ describe('orders list', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
});
cy.wait('@Markets');
});
@@ -136,7 +136,7 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
});
});
const orderId = '1234567890';
@@ -354,7 +354,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-0');
cy.getByTestId('Orders').click();
cy.wait('@Orders').then(() => {
expect(subscriptionMocks.OrdersUpdate).to.be.calledThrice;
expect(subscriptionMocks.OrdersUpdate).to.be.calledTwice;
});
cy.mockVegaWalletTransaction();
});
@@ -164,10 +164,10 @@ describe('positions', { tags: '@smoke' }, () => {
cy.get('[col-id="liquidationPrice"]').should('contain.text', '0'); // liquidation price
cy.get('[col-id="currentLeverage"]').should('contain.text', '2.846.1');
cy.get('[col-id="currentLeverage"]').should('contain.text', '138.446.1');
cy.get('[col-id="marginAccountBalance"]') // margin allocated
.should('contain.text', '0.01');
.should('contain.text', '1,000');
cy.get('[col-id="unrealisedPNL"]').each(($unrealisedPnl) => {
cy.wrap($unrealisedPnl).invoke('text').should('not.be.empty');
@@ -17,13 +17,6 @@ describe('connect hosted wallet', { tags: '@smoke' }, () => {
});
it('can connect', () => {
// 0002-WCON-002
// 0002-WCON-003
// 0002-WCON-039
// 0002-WCON-017
// 0002-WCON-018
// 0002-WCON-019
// Mock authentication
cy.intercept('POST', 'https://wallet.testnet.vega.xyz/api/v1/auth/token', {
body: {
@@ -48,9 +41,6 @@ describe('connect hosted wallet', { tags: '@smoke' }, () => {
},
});
cy.getByTestId(connectVegaBtn).click();
cy.contains(
'Choose wallet app to connect, or to change port or server URL enter a custom wallet location first'
);
cy.contains('Connect Vega wallet');
cy.contains('Hosted Fairground wallet');
@@ -61,13 +51,9 @@ describe('connect hosted wallet', { tags: '@smoke' }, () => {
cy.getByTestId(form).find('#passphrase').click().type('pass');
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
cy.getByTestId(manageVegaBtn).should('exist');
cy.getByTestId('manage-vega-wallet').click();
cy.getByTestId('keypair-list').should('exist');
});
it('doesnt connect with invalid credentials', () => {
// 0002-WCON-020
// Mock incorrect username/password
cy.intercept('POST', 'https://wallet.testnet.vega.xyz/api/v1/auth/token', {
body: {
@@ -113,10 +99,6 @@ describe('connect vega wallet', { tags: '@smoke' }, () => {
});
it('can connect', () => {
// 0002-WCON-002
// 0002-WCON-005
// 0002-WCON-007
mockConnectWallet();
cy.getByTestId(connectVegaBtn).click();
cy.getByTestId('connectors-list')
@@ -128,40 +110,16 @@ describe('connect vega wallet', { tags: '@smoke' }, () => {
});
it('can change selected public key and disconnect', () => {
// 0002-WCON-022
// 0002-WCON-023
// 0002-WCON-025
// 0002-WCON-026
// 0002-WCON-021
// 0002-WCON-027
// 0002-WCON-030
// 0002-WCON-029
// 0002-WCON-008
// 0002-WCON-035
// 0002-WCON-014
// 0002-WCON-010
mockConnectWallet();
const key2 = Cypress.env('VEGA_PUBLIC_KEY2');
const truncatedKey2 = Cypress.env('TRUNCATED_VEGA_PUBLIC_KEY2');
cy.connectVegaWallet();
cy.getByTestId('manage-vega-wallet').click();
cy.getByTestId('keypair-list').should('exist');
cy.getByTestId(`key-${key2}`).should('contain.text', truncatedKey2);
cy.getByTestId(`key-${key2}`)
.find('[data-testid="copy-vega-public-key"]')
.should('be.visible');
cy.get(`[data-testid="key-${key2}"] > .mr-2`).click();
cy.getByTestId('keypair-list')
.find('[data-state="checked"]')
.should('be.visible');
cy.getByTestId('disconnect').click();
cy.getByTestId('connect-vega-wallet').should('exist');
cy.getByTestId('manage-vega-wallet').should('not.exist');
cy.getByTestId('connect-vega-wallet').click();
cy.contains(
'Choose wallet app to connect, or to change port or server URL enter a custom wallet location first'
);
});
});
@@ -76,9 +76,6 @@ describe(
cy.getByTestId(closeDialog).click();
cy.getByTestId('Trading').first().click();
cy.getByTestId(collateralTab).click();
cy.getByTestId(openTransferDialog).should('not.exist');
cy.getByTestId('Portfolio').eq(0).click();
cy.getByTestId(collateralTab).click();
cy.getByTestId(openTransferDialog).click();
cy.getByTestId(dialogTransferText).should(
'contain.text',
@@ -7,13 +7,13 @@ import type { onMessage } from '@vegaprotocol/cypress';
import type { PartialDeep } from 'type-fest';
import { orderUpdateSubscription } from '@vegaprotocol/mock';
const sendOrderUpdate: ((data: OrdersUpdateSubscription) => void)[] = [];
let sendOrderUpdate: (data: OrdersUpdateSubscription) => void;
const getOnOrderUpdate = () => {
const onOrderUpdate: onMessage<
OrdersUpdateSubscription,
OrdersUpdateSubscriptionVariables
> = (send) => {
sendOrderUpdate.push(send);
sendOrderUpdate = send;
};
return onOrderUpdate;
};
@@ -31,5 +31,5 @@ export function updateOrder(
if (!sendOrderUpdate) {
throw new Error('OrderSub not called');
}
sendOrderUpdate.forEach((send) => send(update));
sendOrderUpdate(update);
}
+18 -1
View File
@@ -28,6 +28,7 @@ import {
} from '@vegaprotocol/mock';
import type { PartialDeep } from 'type-fest';
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/market-list';
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
type MarketPageMockData = {
state: Schema.MarketState;
@@ -68,6 +69,18 @@ const marketsDataOverride = (
},
});
const marketInfoOverride = (
data: MarketPageMockData
): PartialDeep<MarketInfoQuery> => ({
market: {
state: data.state,
tradingMode: data.tradingMode,
data: {
trigger: data.trigger,
},
},
});
const mockTradingPage = (
req: CyHttpMessages.IncomingHttpRequest,
state: Schema.MarketState = Schema.MarketState.STATE_ACTIVE,
@@ -96,7 +109,11 @@ const mockTradingPage = (
aliasGQLQuery(req, 'Margins', marginsQuery());
aliasGQLQuery(req, 'Assets', assetsQuery());
aliasGQLQuery(req, 'Asset', assetQuery());
aliasGQLQuery(req, 'MarketInfo', marketInfoQuery());
aliasGQLQuery(
req,
'MarketInfo',
marketInfoQuery(marketInfoOverride({ state, tradingMode, trigger }))
);
aliasGQLQuery(req, 'Trades', tradesQuery());
aliasGQLQuery(req, 'Chart', chartQuery());
aliasGQLQuery(req, 'Candles', candlesQuery());
-1
View File
@@ -12,7 +12,6 @@ export const Home = () => {
// should be the oldest market that is currently trading in us mode(i.e. not in auction).
const { data, error, loading } = useDataProvider({
dataProvider: marketsWithDataProvider,
variables: undefined,
});
const update = useGlobalStore((store) => store.update);
const marketId = useGlobalStore((store) => store.marketId);
@@ -47,8 +47,7 @@ export const Liquidity = () => {
const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider,
variables: { marketId: marketId || '' },
skip: !marketId,
variables: useMemo(() => ({ marketId }), [marketId]),
});
useEffect(() => {
const interval = setInterval(reload, 10000);
@@ -78,8 +77,7 @@ export const LiquidityContainer = ({
const { data, loading, error } = useDataProvider({
dataProvider: lpAggregatedDataProvider,
update,
variables: { marketId: marketId || '' },
skip: !marketId,
variables: useMemo(() => ({ marketId }), [marketId]),
});
const assetDecimalPlaces =
@@ -163,8 +161,7 @@ export const LiquidityViewContainer = ({
} = useDataProvider({
dataProvider: lpAggregatedDataProvider,
update,
variables: { marketId: marketId || '' },
skip: !marketId,
variables: useMemo(() => ({ marketId }), [marketId]),
});
const targetStake = marketData?.targetStake;
+9 -2
View File
@@ -7,6 +7,10 @@ import {
useThrottledDataProvider,
} from '@vegaprotocol/react-helpers';
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list';
import { marketProvider, marketDataProvider } from '@vegaprotocol/market-list';
import { useGlobalStore, usePageTitleStore } from '../../stores';
@@ -31,10 +35,13 @@ const TitleUpdater = ({
}) => {
const pageTitle = usePageTitleStore((store) => store.pageTitle);
const updateTitle = usePageTitleStore((store) => store.updateTitle);
const { data: marketData } = useThrottledDataProvider(
const { data: marketData } = useThrottledDataProvider<
MarketData,
MarketDataUpdateFieldsFragment
>(
{
dataProvider: marketDataProvider,
variables: { marketId: marketId || '' },
variables: useMemo(() => ({ marketId }), [marketId]),
skip: !marketId,
},
1000
@@ -179,10 +179,7 @@ const MainGrid = ({
</Tab>
<Tab id="accounts" name={t('Collateral')}>
<VegaWalletContainer>
<TradingViews.Collateral
pinnedAsset={pinnedAsset}
hideButtons
/>
<TradingViews.Collateral pinnedAsset={pinnedAsset} />
</VegaWalletContainer>
</Tab>
</Tabs>
@@ -2,16 +2,10 @@ import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
import { depositsProvider } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n';
import {
useDataProvider,
useBottomPlaceholder,
} from '@vegaprotocol/react-helpers';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
export const DepositsContainer = () => {
const gridRef = useRef<AgGridReact | null>(null);
const { pubKey, isReadOnly } = useVegaWallet();
const { data, loading, error, reload } = useDataProvider({
dataProvider: depositsProvider,
@@ -19,15 +13,13 @@ export const DepositsContainer = () => {
skip: !pubKey,
});
const openDepositDialog = useDepositDialog((state) => state.open);
const bottomPlaceholderProps = useBottomPlaceholder({ gridRef });
return (
<div className="h-full">
<div className="h-full grid grid-rows-[1fr,min-content]">
<div className="h-full relative">
<DepositsTable
rowData={data || []}
noRowsOverlayComponent={() => null}
ref={gridRef}
{...bottomPlaceholderProps}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
@@ -41,9 +33,8 @@ export const DepositsContainer = () => {
</div>
</div>
{!isReadOnly && (
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
<div className="w-full dark:bg-black bg-white absolute bottom-0 h-auto flex justify-end px-[11px] py-2">
<Button
variant="primary"
size="sm"
onClick={() => openDepositDialog()}
data-testid="deposit-button"
@@ -49,10 +49,7 @@ export const Portfolio = () => {
</Tab>
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<PositionsContainer
onMarketClick={onMarketClick}
noBottomPlaceholder
/>
<PositionsContainer onMarketClick={onMarketClick} />
</VegaWalletContainer>
</Tab>
<Tab id="orders" name={t('Orders')}>
@@ -20,35 +20,36 @@ export const WithdrawalsContainer = () => {
return (
<VegaWalletContainer>
<div className="h-full relative">
<WithdrawalsTable
data-testid="withdrawals-history"
rowData={data}
noRowsOverlayComponent={() => null}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
data={data}
loading={loading}
error={error}
noDataCondition={(data) => !(data && data.length)}
noDataMessage={t('No withdrawals')}
reload={reload}
<div className="h-full relative grid grid-rows-[1fr,min-content]">
<div className="h-full relative">
<WithdrawalsTable
data-testid="withdrawals-history"
rowData={data}
noRowsOverlayComponent={() => null}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
data={data}
loading={loading}
error={error}
noDataCondition={(data) => !(data && data.length)}
noDataMessage={t('No withdrawals')}
reload={reload}
/>
</div>
</div>
{!isReadOnly && (
<div className="w-full dark:bg-black bg-white absolute bottom-0 h-auto flex justify-end px-[11px] py-2">
<Button
size="sm"
onClick={() => openWithdrawDialog()}
data-testid="withdraw-dialog-button"
>
{t('Make withdrawal')}
</Button>
</div>
)}
</div>
{!isReadOnly && (
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
<Button
variant="primary"
size="sm"
onClick={() => openWithdrawDialog()}
data-testid="withdraw-dialog-button"
>
{t('Make withdrawal')}
</Button>
</div>
)}
</VegaWalletContainer>
);
};
@@ -11,10 +11,8 @@ import { useDepositDialog } from '@vegaprotocol/deposits';
export const AccountsContainer = ({
pinnedAsset,
hideButtons,
}: {
pinnedAsset?: PinnedAsset;
hideButtons?: boolean;
}) => {
const { pubKey, isReadOnly } = useVegaWallet();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
@@ -38,30 +36,27 @@ export const AccountsContainer = ({
}
return (
<div className="h-full relative">
<AccountManager
partyId={pubKey}
onClickAsset={onClickAsset}
onClickWithdraw={openWithdrawalDialog}
onClickDeposit={openDepositDialog}
isReadOnly={isReadOnly}
pinnedAsset={pinnedAsset}
/>
{!isReadOnly && !hideButtons && (
<div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded">
<div className="h-full relative grid grid-rows-[1fr,min-content]">
<div>
<AccountManager
partyId={pubKey}
onClickAsset={onClickAsset}
onClickWithdraw={openWithdrawalDialog}
onClickDeposit={openDepositDialog}
isReadOnly={isReadOnly}
pinnedAsset={pinnedAsset}
/>
</div>
{!isReadOnly && (
<div className="flex gap-2 justify-end p-2 px-[11px]">
<Button
variant="primary"
size="sm"
data-testid="open-transfer-dialog"
onClick={() => openTransferDialog()}
>
{t('Transfer')}
</Button>
<Button
variant="primary"
size="sm"
onClick={() => openDepositDialog()}
>
<Button size="sm" onClick={() => openDepositDialog()}>
{t('Deposit')}
</Button>
</div>
+1 -1
View File
@@ -14,7 +14,7 @@ export const Footer = () => {
const { blockDiff, datanodeBlockHeight } = useNodeHealth();
return (
<footer className="px-4 py-1 text-xs border-t border-default text-vega-light-300 dark:text-vega-dark-300 lg:fixed bottom-0 left-0 border-r bg-white dark:bg-black">
<footer className="px-4 py-1 text-xs border-t border-default text-vega-light-300 dark:text-vega-dark-300">
{/* Pull left to align with top nav, due to button padding */}
<div className="-ml-2">
{VEGA_URL && (
@@ -1,4 +1,5 @@
import type { RefObject } from 'react';
import { useMemo } from 'react';
import { useInView } from 'react-intersection-observer';
import { isNumeric } from '@vegaprotocol/utils';
import {
@@ -8,6 +9,7 @@ import {
import { PriceChangeCell } from '@vegaprotocol/datagrid';
import * as Schema from '@vegaprotocol/types';
import type { CandleClose } from '@vegaprotocol/types';
import type { Candle } from '@vegaprotocol/market-list';
import { marketCandlesProvider } from '@vegaprotocol/market-list';
import { THROTTLE_UPDATE_TIME } from '../constants';
@@ -28,14 +30,19 @@ export const Last24hPriceChange = ({
}: Props) => {
const [ref, inView] = useInView({ root: inViewRoot?.current });
const yesterday = useYesterday();
const { data, error } = useThrottledDataProvider(
const variables = useMemo(
() => ({
marketId: marketId,
interval: Schema.Interval.INTERVAL_I1H,
since: new Date(yesterday).toISOString(),
}),
[marketId, yesterday]
);
const { data, error } = useThrottledDataProvider<Candle[], Candle>(
{
dataProvider: marketCandlesProvider,
variables: {
marketId: marketId || '',
interval: Schema.Interval.INTERVAL_I1H,
since: new Date(yesterday).toISOString(),
},
variables,
skip: !marketId || !inView,
},
THROTTLE_UPDATE_TIME
@@ -10,6 +10,8 @@ import {
useYesterday,
} from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { useMemo } from 'react';
import type { Candle } from '@vegaprotocol/market-list';
import { THROTTLE_UPDATE_TIME } from '../constants';
interface Props {
@@ -30,14 +32,19 @@ export const Last24hVolume = ({
const yesterday = useYesterday();
const [ref, inView] = useInView({ root: inViewRoot?.current });
const { data } = useThrottledDataProvider(
const variables = useMemo(
() => ({
marketId: marketId,
interval: Schema.Interval.INTERVAL_I1H,
since: new Date(yesterday).toISOString(),
}),
[marketId, yesterday]
);
const { data } = useThrottledDataProvider<Candle[], Candle>(
{
dataProvider: marketCandlesProvider,
variables: {
marketId: marketId || '',
interval: Schema.Interval.INTERVAL_I1H,
since: new Date(yesterday).toISOString(),
},
variables,
skip: !(inView && marketId),
},
THROTTLE_UPDATE_TIME
@@ -4,7 +4,10 @@ import {
useDataProvider,
useNetworkParams,
} from '@vegaprotocol/react-helpers';
import type { MarketData } from '@vegaprotocol/market-list';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list';
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
import { HeaderStat } from '../header';
import {
@@ -67,7 +70,7 @@ export const MarketLiquiditySupplied = ({
[noUpdate]
);
useDataProvider({
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
dataProvider: marketDataProvider,
update,
variables,
@@ -1,8 +1,13 @@
import type { RefObject } from 'react';
import { useMemo } from 'react';
import { useInView } from 'react-intersection-observer';
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
import { useThrottledDataProvider } from '@vegaprotocol/react-helpers';
import { PriceCell } from '@vegaprotocol/datagrid';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list';
import { marketDataProvider } from '@vegaprotocol/market-list';
import { THROTTLE_UPDATE_TIME } from '../constants';
@@ -22,10 +27,15 @@ export const MarketMarkPrice = ({
asPriceCell,
}: Props) => {
const [ref, inView] = useInView({ root: inViewRoot?.current });
const { data } = useThrottledDataProvider(
const variables = useMemo(() => ({ marketId }), [marketId]);
const { data } = useThrottledDataProvider<
MarketData,
MarketDataUpdateFieldsFragment
>(
{
dataProvider: marketDataProvider,
variables: { marketId: marketId || '' },
variables,
skip: !inView,
},
THROTTLE_UPDATE_TIME
@@ -1,11 +1,15 @@
import throttle from 'lodash/throttle';
import type { MarketData, Market } from '@vegaprotocol/market-list';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
Market,
} from '@vegaprotocol/market-list';
import { marketDataProvider } from '@vegaprotocol/market-list';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { HeaderStat } from '../header';
import { useCallback, useRef, useState } from 'react';
import { useCallback, useMemo, useRef, useState } from 'react';
import * as constants from '../constants';
export const MarketState = ({ market }: { market: Market | null }) => {
@@ -29,10 +33,14 @@ export const MarketState = ({ market }: { market: Market | null }) => {
[throttledSetMarketState]
);
useDataProvider({
const variables = useMemo(
() => ({ marketId: market?.id || '' }),
[market?.id]
);
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
dataProvider: marketDataProvider,
update,
variables: { marketId: market?.id || '' },
variables,
skip: !market?.id,
});
@@ -1,16 +1,24 @@
import { useCallback, useRef, useState } from 'react';
import { useCallback, useMemo, useRef, useState } from 'react';
import throttle from 'lodash/throttle';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import type { MarketData } from '@vegaprotocol/market-list';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
} from '@vegaprotocol/market-list';
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
import { HeaderStat } from '../header';
import * as constants from '../constants';
export const MarketVolume = ({ marketId }: { marketId: string }) => {
const [marketVolume, setMarketVolume] = useState<string>('-');
const variables = { marketId };
const variables = useMemo(
() => ({
marketId: marketId,
}),
[marketId]
);
const { data } = useDataProvider({
dataProvider: marketProvider,
variables,
@@ -38,7 +46,7 @@ export const MarketVolume = ({ marketId }: { marketId: string }) => {
[data?.positionDecimalPlaces, throttledSetMarketVolume]
);
useDataProvider({
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
dataProvider: marketDataProvider,
update,
variables,
@@ -106,13 +106,14 @@ export const SelectMarketPopover = ({
loading: marketsLoading,
reload: marketListReload,
} = useMarketList();
const variables = useMemo(() => ({ partyId: pubKey }), [pubKey]);
const {
data: positions,
loading: positionsLoading,
reload,
} = useDataProvider({
dataProvider: positionsDataProvider,
variables: { partyId: pubKey || '' },
variables,
skip: !pubKey,
});
const onSelectMarket = useCallback(
@@ -20,7 +20,6 @@ export const WelcomeDialog = () => {
const [riskAccepted] = useLocalStorage(constants.RISK_ACCEPTED_KEY);
const { data } = useDataProvider({
dataProvider: activeMarketsProvider,
variables: undefined,
});
const { update, shouldDisplayWelcomeDialog } = useGlobalStore((store) => ({
+1 -1
View File
@@ -72,7 +72,7 @@ function AppBody({ Component }: AppProps) {
const gridClasses = classNames(
'h-full relative z-0 grid',
'grid-rows-[repeat(3,min-content),1fr]'
'grid-rows-[repeat(3,min-content),1fr,min-content]'
);
return (
+1 -1
View File
@@ -15,7 +15,7 @@ interface PageTitleStore {
updateTitle: (title: string) => void;
}
export const useGlobalStore = create<GlobalStore>()((set) => ({
export const useGlobalStore = create<GlobalStore>((set) => ({
nodeSwitcherDialog: false,
marketId: LocalStorage.getItem('marketId') || null,
shouldDisplayWelcomeDialog: false,
@@ -18,7 +18,6 @@ import type {
AccountFieldsFragment,
AccountsQuery,
AccountEventsSubscription,
AccountsQueryVariables,
} from './__generated__/Accounts';
import type { Market } from '@vegaprotocol/market-list';
import type { Asset } from '@vegaprotocol/assets';
@@ -86,8 +85,7 @@ export const accountsOnlyDataProvider = makeDataProvider<
AccountsQuery,
AccountFieldsFragment[],
AccountEventsSubscription,
AccountEventsSubscription['accounts'],
AccountsQueryVariables
AccountEventsSubscription['accounts']
>({
query: AccountsDocument,
subscriptionQuery: AccountEventsDocument,
@@ -161,16 +159,8 @@ const getAssetAccountAggregation = (
return { ...balanceAccount, breakdown };
};
export const accountsDataProvider = makeDerivedDataProvider<
Account[],
never,
AccountsQueryVariables
>(
[
accountsOnlyDataProvider,
(callback, client) => marketsProvider(callback, client, undefined),
(callback, client) => assetsProvider(callback, client, undefined),
],
export const accountsDataProvider = makeDerivedDataProvider<Account[], never>(
[accountsOnlyDataProvider, marketsProvider, assetsProvider],
([accounts, markets, assets]): Account[] | null => {
return accounts
? accounts
@@ -204,8 +194,7 @@ export const accountsDataProvider = makeDerivedDataProvider<
export const aggregatedAccountsDataProvider = makeDerivedDataProvider<
AccountFields[],
never,
AccountsQueryVariables
never
>(
[accountsDataProvider],
(parts) => parts[0] && getAccountData(parts[0] as Account[])
+7 -25
View File
@@ -1,16 +1,12 @@
import { useRef, useMemo, memo, useCallback } from 'react';
import { t } from '@vegaprotocol/i18n';
import {
useDataProvider,
useBottomPlaceholder,
} from '@vegaprotocol/react-helpers';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import { useRef, useMemo, memo } from 'react';
import type { AccountFields } from './accounts-data-provider';
import { aggregatedAccountsDataProvider } from './accounts-data-provider';
import type { PinnedAsset } from './accounts-table';
import { AccountTable } from './accounts-table';
import type { RowHeightParams } from 'ag-grid-community';
interface AccountManagerProps {
partyId: string;
@@ -31,26 +27,14 @@ export const AccountManager = ({
}: AccountManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const variables = useMemo(() => ({ partyId }), [partyId]);
const { data, loading, error, reload } = useDataProvider({
const { data, loading, error, reload } = useDataProvider<
AccountFields[],
never
>({
dataProvider: aggregatedAccountsDataProvider,
variables,
});
const setId = useCallback(
(data: AccountFields) => ({
...data,
asset: { ...data.asset, id: `${data.asset.id}-1` },
}),
[]
);
const bottomPlaceholderProps = useBottomPlaceholder<AccountFields>({
gridRef,
setId,
});
const getRowHeight = useCallback(
(params: RowHeightParams) => (params.node.rowPinned ? 32 : 22),
[]
);
return (
<div className="relative h-full">
<AccountTable
@@ -62,8 +46,6 @@ export const AccountManager = ({
isReadOnly={isReadOnly}
noRowsOverlayComponent={() => null}
pinnedAsset={pinnedAsset}
getRowHeight={getRowHeight}
{...bottomPlaceholderProps}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
+86 -154
View File
@@ -1,21 +1,13 @@
import { forwardRef, useMemo, useState } from 'react';
import {
addDecimalsFormatNumber,
isNumeric,
toBigNum,
} from '@vegaprotocol/utils';
import { addDecimalsFormatNumber, isNumeric } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
VegaValueGetterParams,
} from '@vegaprotocol/datagrid';
import { Button, ButtonLink, Dialog } from '@vegaprotocol/ui-toolkit';
import { ButtonLink, Dialog } from '@vegaprotocol/ui-toolkit';
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
import {
AgGridDynamic as AgGrid,
CenteredGridCellWrapper,
} from '@vegaprotocol/datagrid';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
import { AgGridColumn } from 'ag-grid-react';
import type { IDatasource, IGetRowsParams } from 'ag-grid-community';
import type { AgGridReact, AgGridReactProps } from 'ag-grid-react';
@@ -94,23 +86,18 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
cellRenderer={({
value,
data,
node,
}: VegaICellRendererParams<AccountFields, 'asset.symbol'>) => {
return value ? (
<CenteredGridCellWrapper
className={node.rowPinned ? 'h-[30px]' : undefined}
<ButtonLink
data-testid="asset"
onClick={() => {
if (data) {
onClickAsset(data.asset.id);
}
}}
>
<ButtonLink
data-testid="asset"
onClick={() => {
if (data) {
onClickAsset(data.asset.id);
}
}}
>
{value}
</ButtonLink>
</CenteredGridCellWrapper>
{value}
</ButtonLink>
) : null;
}}
maxWidth={300}
@@ -122,31 +109,16 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
headerTooltip={t(
'This is the total amount of collateral used plus the amount available in your general account.'
)}
valueGetter={({
valueFormatter={({
value,
data,
}: VegaValueGetterParams<AccountFields, 'deposited'>) => {
return !data?.deposited
? undefined
: toBigNum(data.deposited, data.asset.decimals).toNumber();
}}
}: VegaValueFormatterParams<AccountFields, 'deposited'>) =>
data &&
data.asset &&
isNumeric(value) &&
addDecimalsFormatNumber(value, data.asset.decimals)
}
maxWidth={300}
cellRenderer={({
data,
node,
}: VegaICellRendererParams<AccountFields, 'deposited'>) => {
const valueFormatted =
data &&
data.asset &&
isNumeric(data.deposited) &&
addDecimalsFormatNumber(data.deposited, data.asset.decimals);
return node.rowPinned ? (
<CenteredGridCellWrapper className="h-[30px] justify-end">
{valueFormatted}
</CenteredGridCellWrapper>
) : (
valueFormatted
);
}}
/>
<AgGridColumn
headerName={t('Used')}
@@ -155,31 +127,16 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
headerTooltip={t(
'This is the amount of collateral used from your general account.'
)}
valueGetter={({
valueFormatter={({
value,
data,
}: VegaValueGetterParams<AccountFields, 'used'>) => {
return !data?.used
? undefined
: toBigNum(data.used, data.asset.decimals).toNumber();
}}
}: VegaValueFormatterParams<AccountFields, 'used'>) =>
data &&
data.asset &&
isNumeric(value) &&
addDecimalsFormatNumber(value, data.asset.decimals)
}
maxWidth={300}
cellRenderer={({
data,
node,
}: VegaICellRendererParams<AccountFields, 'used'>) => {
const valueFormatted =
data &&
data.asset &&
isNumeric(data.used) &&
addDecimalsFormatNumber(data.used, data.asset.decimals);
return node.rowPinned ? (
<CenteredGridCellWrapper className="h-[30px] justify-end">
{valueFormatted}
</CenteredGridCellWrapper>
) : (
valueFormatted
);
}}
/>
<AgGridColumn
headerName={t('Available')}
@@ -188,108 +145,83 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
headerTooltip={t(
'This is the amount of collateral available in your general account.'
)}
valueGetter={({
data,
}: VegaValueGetterParams<AccountFields, 'available'>) => {
return !data?.available
? undefined
: toBigNum(data.available, data.asset.decimals).toNumber();
}}
valueFormatter={({
value,
data,
}: VegaValueFormatterParams<AccountFields, 'available'>) =>
data &&
data.asset &&
isNumeric(data.available) &&
addDecimalsFormatNumber(data.available, data.asset.decimals)
isNumeric(value) &&
addDecimalsFormatNumber(value, data.asset.decimals)
}
maxWidth={300}
cellRenderer={({
data,
node,
}: VegaICellRendererParams<AccountFields, 'available'>) => {
const valueFormatted =
data &&
data.asset &&
isNumeric(data.available) &&
addDecimalsFormatNumber(data.available, data.asset.decimals);
return node.rowPinned ? (
<CenteredGridCellWrapper className="h-[30px] justify-end">
{valueFormatted}
</CenteredGridCellWrapper>
) : (
valueFormatted
);
}}
/>
<AgGridColumn
colId="breakdown"
headerName=""
sortable={false}
minWidth={200}
type="rightAligned"
cellRenderer={({
data,
}: VegaICellRendererParams<AccountFields>) => {
if (!data) return null;
else {
if (
data.asset.id === pinnedAssetId &&
new BigNumber(data.deposited).isLessThanOrEqualTo(0)
) {
return (
<CenteredGridCellWrapper className="h-[30px] justify-end py-1">
<Button
size="xs"
variant="primary"
{
<AgGridColumn
colId="breakdown"
headerName=""
sortable={false}
minWidth={200}
type="rightAligned"
cellRenderer={({
data,
}: VegaICellRendererParams<AccountFields>) => {
if (!data) return null;
else {
if (
data.asset.id === pinnedAssetId &&
new BigNumber(data.deposited).isLessThanOrEqualTo(0)
) {
return (
<ButtonLink
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(data.asset.id);
}}
>
{t('Deposit to trade')}
</Button>
</CenteredGridCellWrapper>
);
}
return (
<>
<ButtonLink
data-testid="breakdown"
onClick={() => {
setOpenBreakdown(!openBreakdown);
setBreakdown(data.breakdown || null);
}}
>
{t('Breakdown')}
</ButtonLink>
<span className="mx-1" />
{!props.isReadOnly && (
</ButtonLink>
);
}
return (
<>
<ButtonLink
data-testid="deposit"
data-testid="breakdown"
onClick={() => {
onClickDeposit && onClickDeposit(data.asset.id);
setOpenBreakdown(!openBreakdown);
setBreakdown(data.breakdown || null);
}}
>
{t('Deposit')}
{t('Breakdown')}
</ButtonLink>
)}
<span className="mx-1" />
{!props.isReadOnly && (
<ButtonLink
data-testid="withdraw"
onClick={() =>
onClickWithdraw && onClickWithdraw(data.asset.id)
}
>
{t('Withdraw')}
</ButtonLink>
)}
</>
);
}
}}
/>
<span className="mx-1" />
{!props.isReadOnly && (
<ButtonLink
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(data.asset.id);
}}
>
{t('Deposit')}
</ButtonLink>
)}
<span className="mx-1" />
{!props.isReadOnly && (
<ButtonLink
data-testid="withdraw"
onClick={() =>
onClickWithdraw && onClickWithdraw(data.asset.id)
}
>
{t('Withdraw')}
</ButtonLink>
)}
</>
);
}
}}
/>
}
</AgGrid>
<Dialog size="medium" open={openBreakdown} onChange={setOpenBreakdown}>
<div className="h-[35vh] w-full m-auto flex flex-col">
+6 -24
View File
@@ -43,6 +43,10 @@ export const accountFields: AccountFieldsFragment[] = [
__typename: 'AccountBalance',
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
balance: '100000000',
market: {
id: 'market-0',
__typename: 'Market',
},
asset: {
__typename: 'Asset',
id: 'asset-id-2',
@@ -71,7 +75,7 @@ export const accountFields: AccountFieldsFragment[] = [
},
asset: {
__typename: 'Asset',
id: 'asset-0',
id: 'asset-id-2',
},
},
{
@@ -90,7 +94,7 @@ export const accountFields: AccountFieldsFragment[] = [
{
__typename: 'AccountBalance',
type: Schema.AccountType.ACCOUNT_TYPE_GENERAL,
balance: '10000000000',
balance: '100000000',
market: null,
asset: {
__typename: 'Asset',
@@ -137,25 +141,3 @@ export const accountEventsSubscription = (
};
return merge(defaultResult, override);
};
export const amendGeneralAccountBalance = (
accounts: AccountsQuery,
marketId: string,
balance: string
) => {
if (accounts.party?.accountsConnection?.edges) {
const marginAccount = accounts.party.accountsConnection.edges.find(
(edge) => edge?.node.market?.id === marketId
);
if (marginAccount) {
const generalAccount = accounts.party.accountsConnection.edges.find(
(edge) =>
edge?.node.asset.id === marginAccount.node.asset.id &&
!edge?.node.market
);
if (generalAccount) {
generalAccount.node.balance = balance;
}
}
}
};
+1 -1
View File
@@ -20,7 +20,7 @@ export const TransferContainer = () => {
const { param } = useNetworkParam(NetworkParams.transfer_fee_factor);
const { data } = useDataProvider({
dataProvider: accountsDataProvider,
variables: { partyId: pubKey || '' },
variables: { partyId: pubKey },
skip: !pubKey,
});
const create = useVegaTransactionStore((store) => store.create);
+1 -1
View File
@@ -11,7 +11,7 @@ interface Actions {
open: (open?: boolean) => void;
}
export const useTransferDialog = create<State & Actions>()((set) => ({
export const useTransferDialog = create<State & Actions>((set) => ({
isOpen: false,
open: (open = true) => {
set(() => ({ isOpen: open }));
+1 -1
View File
@@ -9,4 +9,4 @@ type HeaderStore = {
[url: string]: HeaderEntry | undefined;
};
export const useHeaderStore = create<HeaderStore>()(() => ({}));
export const useHeaderStore = create<HeaderStore>(() => ({}));
+10 -13
View File
@@ -1,11 +1,8 @@
import { makeDataProvider } from '@vegaprotocol/utils';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { useMemo } from 'react';
import type {
AssetQuery,
AssetFieldsFragment,
AssetQueryVariables,
} from './__generated__/Asset';
import type { AssetQuery, AssetFieldsFragment } from './__generated__/Asset';
import { AssetDocument } from './__generated__/Asset';
export type Asset = AssetFieldsFragment;
@@ -18,21 +15,21 @@ export const getData = (responseData: AssetQuery | null | undefined) => {
return null;
};
export const assetProvider = makeDataProvider<
AssetQuery,
Asset,
never,
never,
AssetQueryVariables
>({
export const assetProvider = makeDataProvider<AssetQuery, Asset, never, never>({
query: AssetDocument,
getData,
});
export const useAssetDataProvider = (assetId: string) => {
const variables = useMemo(
() => ({
assetId,
}),
[assetId]
);
return useDataProvider({
dataProvider: assetProvider,
variables: { assetId: assetId || '' },
variables,
skip: !assetId,
});
};
+1 -1
View File
@@ -20,7 +20,7 @@ export type AssetDetailsDialogStore = {
open: (id: string, trigger?: HTMLElement | null, asJson?: boolean) => void;
};
export const useAssetDetailsDialogStore = create<AssetDetailsDialogStore>()(
export const useAssetDetailsDialogStore = create<AssetDetailsDialogStore>(
(set) => ({
isOpen: false,
id: '',
@@ -40,5 +40,4 @@ export const enabledAssetsProvider = makeDerivedDataProvider<
export const useAssetsDataProvider = () =>
useDataProvider({
dataProvider: assetsProvider,
variables: undefined,
});
-2
View File
@@ -19,7 +19,6 @@ import { addCreateMarket } from './lib/commands/create-market';
import { addConnectPublicKey } from './lib/commands/add-connect-public-key';
import { addVegaWalletSubmitProposal } from './lib/commands/vega-wallet-submit-proposal';
import { addGetNodes } from './lib/commands/get-nodes';
import { addVegaWalletSubmitLiquidityProvision } from './lib/commands/vega-wallet-submit-liquidity-provision';
addGetTestIdcommand();
addSlackCommand();
@@ -40,7 +39,6 @@ addMockTransactionResponse();
addCreateMarket();
addConnectPublicKey();
addVegaWalletSubmitProposal();
addVegaWalletSubmitLiquidityProvision();
export { mockConnectWallet } from './lib/commands/vega-wallet-connect';
export type { onMessage } from './lib/mock-ws';
@@ -1,67 +0,0 @@
import { PeggedReference } from '@vegaprotocol/types';
import type { LiquidityProvisionSubmission } from '@vegaprotocol/wallet';
import { createWalletClient, sendVegaTx } from '../capsule/wallet-client';
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
VegaWalletSubmitLiquidityProvision(
marketId: string,
amount: string
): void;
}
}
}
export const addVegaWalletSubmitLiquidityProvision = () => {
Cypress.Commands.add(
'VegaWalletSubmitLiquidityProvision',
(marketId, amount) => {
const vegaWalletUrl = Cypress.env('VEGA_WALLET_URL');
const token = Cypress.env('VEGA_WALLET_API_TOKEN');
const vegaPubKey = Cypress.env('VEGA_PUBLIC_KEY');
const liquidityProvisionTx: LiquidityProvisionSubmission = {
liquidityProvisionSubmission: {
marketId: marketId,
commitmentAmount: amount,
fee: '0.001',
buys: [
{
offset: '10',
proportion: '1',
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
{
offset: '12',
proportion: '2',
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
],
sells: [
{
offset: '10',
proportion: '2',
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
{
offset: '12',
proportion: '2',
reference: PeggedReference.PEGGED_REFERENCE_MID,
},
],
},
pubKey: vegaPubKey,
propagate: true,
};
createWalletClient(vegaWalletUrl, token);
cy.highlight('Submitting liquidity provision');
sendVegaTx(vegaPubKey, liquidityProvisionTx);
}
);
};
-1
View File
@@ -8,7 +8,6 @@ export * from './lib/cells/price-cell';
export * from './lib/cells/price-change-cell';
export * from './lib/cells/price-flash-cell';
export * from './lib/cells/vol-cell';
export * from './lib/cells/centered-grid-cell';
export * from './lib/filters/date-range-filter';
export * from './lib/filters/set-filter';
@@ -22,15 +22,13 @@ const agGridDarkVariables = `
border-width: 1px 0;
border-bottom: 1px solid transparent;
}
.ag-theme-balham-dark .ag-row.no-hover, .ag-theme-balham-dark .ag-row.no-hover:hover {
background: black;
}
.ag-theme-balham-dark .ag-react-container {
overflow: hidden;
text-overflow: ellipsis;
}
.ag-theme-balham-dark .ag-cell, .ag-theme-balham-dark .ag-full-width-row .ag-cell-wrapper.ag-row-group {
.ag-theme-balham-dark .ag-cell, .ag-theme-balham-dark .ag-full-width-row .ag-cell-wrapper.ag-row-group {
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
}
`;
@@ -22,15 +22,13 @@ const agGridLightVariables = `
border-width: 1px 0;
border-bottom: 1px solid transparent;
}
.ag-theme-balham .ag-row.no-hover, .ag-theme-balham .ag-row.no-hover:hover {
background: white;
}
.ag-theme-balham .ag-react-container {
overflow: hidden;
text-overflow: ellipsis;
}
.ag-theme-balham .ag-cell, .ag-theme-balham .ag-full-width-row .ag-cell-wrapper.ag-row-group {
.ag-theme-balham .ag-cell, .ag-theme-balham .ag-full-width-row .ag-cell-wrapper.ag-row-group {
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
}
`;
@@ -1,16 +0,0 @@
import type { ReactNode } from 'react';
import classNames from 'classnames';
export const CenteredGridCellWrapper = ({
children,
className,
}: {
children: ReactNode;
className?: string;
}) => (
<div
className={classNames('flex h-[20px] p-0 justify-items-center', className)}
>
<div className="self-center">{children}</div>
</div>
);
@@ -1,7 +1,7 @@
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { formatNumber } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { Notification, Intent } from '@vegaprotocol/ui-toolkit';
import { useDepositDialog } from '@vegaprotocol/deposits';
import { DepositDialog, useDepositDialog } from '@vegaprotocol/deposits';
interface Props {
margin: string;
@@ -16,22 +16,25 @@ interface Props {
export const MarginWarning = ({ margin, balance, asset }: Props) => {
const openDepositDialog = useDepositDialog((state) => state.open);
return (
<Notification
intent={Intent.Warning}
testId="dealticket-warning-margin"
message={`You may not have enough margin available to open this position. ${addDecimalsFormatNumber(
margin,
asset.decimals
)} ${asset.symbol} ${t(
'is currently required. You have only'
)} ${addDecimalsFormatNumber(balance, asset.decimals)} ${
asset.symbol
} ${t('available.')}`}
buttonProps={{
text: t(`Deposit ${asset.symbol}`),
action: () => openDepositDialog(asset.id),
dataTestId: 'deal-ticket-deposit-dialog-button',
}}
/>
<>
<Notification
intent={Intent.Warning}
testId="dealticket-warning-margin"
message={`You may not have enough margin available to open this position. ${formatNumber(
margin,
asset.decimals
)} ${asset.symbol} ${t(
'is currently required. You have only'
)} ${formatNumber(balance, asset.decimals)} ${asset.symbol} ${t(
'available.'
)}`}
buttonProps={{
text: t(`Deposit ${asset.symbol}`),
action: () => openDepositDialog(asset.id),
dataTestId: 'deal-ticket-deposit-dialog-button',
}}
/>
<DepositDialog />
</>
);
};
@@ -1,3 +1,4 @@
import { useMemo } from 'react';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import { useThrottledDataProvider } from '@vegaprotocol/react-helpers';
@@ -28,7 +29,7 @@ export const DealTicketContainer = ({
} = useThrottledDataProvider(
{
dataProvider: marketDataProvider,
variables: { marketId },
variables: useMemo(() => ({ marketId }), [marketId]),
},
1000
);
@@ -1,5 +1,6 @@
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react';
import { useMemo } from 'react';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import {
@@ -11,51 +12,22 @@ interface DealTicketFeeDetailsProps {
order: OrderSubmissionBody['orderSubmission'];
market: Market;
marketData: MarketData;
margin: string;
totalMargin: string;
balance: string;
}
export interface DealTicketFeeDetailProps {
export interface DealTicketFeeDetails {
label: string;
value?: string | number | null;
labelDescription?: string | ReactNode;
symbol?: string;
}
export const DealTicketFeeDetail = ({
label,
value,
labelDescription,
symbol,
}: DealTicketFeeDetailProps) => (
<div className="text-xs mt-2 flex justify-between items-center gap-4 flex-wrap">
<div>
<Tooltip description={labelDescription}>
<div>{label}</div>
</Tooltip>
</div>
<div className="text-neutral-500 dark:text-neutral-300">{`${value ?? '-'} ${
symbol || ''
}`}</div>
</div>
);
export const DealTicketFeeDetails = ({
order,
market,
marketData,
margin,
totalMargin,
balance,
}: DealTicketFeeDetailsProps) => {
const feeDetails = useFeeDealTicketDetails(order, market, marketData);
const details = getFeeDetailsValues({
...feeDetails,
margin,
totalMargin,
balance,
});
const details = useMemo(() => getFeeDetailsValues(feeDetails), [feeDetails]);
return (
<div>
{details.map(({ label, value, labelDescription, symbol }) => (
@@ -21,7 +21,8 @@ import {
Intent,
Notification,
} from '@vegaprotocol/ui-toolkit';
import { useOrderMarginValidation } from '../../hooks/use-order-margin-validation';
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
import {
validateExpiration,
validateMarketState,
@@ -31,16 +32,11 @@ import {
} from '../../utils';
import { ZeroBalanceError } from '../deal-ticket-validation/zero-balance-error';
import { SummaryValidationType } from '../../constants';
import { useInitialMargin } from '../../hooks/use-initial-margin';
import { useHasNoBalance } from '../../hooks/use-has-no-balance';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import { MarginWarning } from '../deal-ticket-validation/margin-warning';
import {
useMarketAccountBalance,
useAccountBalance,
} from '@vegaprotocol/accounts';
import { OrderTimeInForce, OrderType } from '@vegaprotocol/types';
import { useOrderForm } from '../../hooks/use-order-form';
import type { OrderObj } from '@vegaprotocol/orders';
export interface DealTicketProps {
market: Market;
@@ -58,12 +54,10 @@ export const DealTicket = ({
const { pubKey, isReadOnly } = useVegaWallet();
// store last used tif for market so that when changing OrderType the previous TIF
// selection for that type is used when switching back
const [lastTIF, setLastTIF] = useState({
[OrderType.TYPE_MARKET]: OrderTimeInForce.TIME_IN_FORCE_IOC,
[OrderType.TYPE_LIMIT]: OrderTimeInForce.TIME_IN_FORCE_GTC,
});
const {
control,
errors,
@@ -73,41 +67,20 @@ export const DealTicket = ({
update,
handleSubmit,
} = useOrderForm(market.id);
const asset = market.tradableInstrument.instrument.product.settlementAsset;
const { accountBalance: marginAccountBalance } = useMarketAccountBalance(
market.id
const marketStateError = validateMarketState(marketData.marketState);
const hasNoBalance = useHasNoBalance(
market.tradableInstrument.instrument.product.settlementAsset.id
);
const marketTradingModeError = validateMarketTradingMode(
marketData.marketTradingMode
);
const { accountBalance: generalAccountBalance } = useAccountBalance(asset.id);
const balance = (
BigInt(marginAccountBalance) + BigInt(generalAccountBalance)
).toString();
const { marketState, marketTradingMode } = marketData;
const normalizedOrder =
order &&
normalizeOrderSubmission(
order,
market.decimalPlaces,
market.positionDecimalPlaces
);
const { margin, totalMargin } = useInitialMargin(market.id, normalizedOrder);
useEffect(() => {
const checkForErrors = useCallback(() => {
if (!pubKey) {
setError('summary', {
message: t('No public key selected'),
type: SummaryValidationType.NoPubKey,
});
setError('summary', { message: t('No public key selected') });
return;
}
const marketStateError = validateMarketState(marketState);
if (marketStateError !== true) {
setError('summary', {
message: marketStateError,
@@ -116,7 +89,6 @@ export const DealTicket = ({
return;
}
const hasNoBalance = generalAccountBalance === '0';
if (hasNoBalance) {
setError('summary', {
message: SummaryValidationType.NoCollateral,
@@ -125,7 +97,6 @@ export const DealTicket = ({
return;
}
const marketTradingModeError = validateMarketTradingMode(marketTradingMode);
if (marketTradingModeError !== true) {
setError('summary', {
message: marketTradingModeError,
@@ -133,19 +104,39 @@ export const DealTicket = ({
});
return;
}
clearErrors('summary');
}, [
marketState,
marketTradingMode,
generalAccountBalance,
hasNoBalance,
marketStateError,
marketTradingModeError,
pubKey,
setError,
]);
useEffect(() => {
if (
(!hasNoBalance &&
errors.summary?.type === SummaryValidationType.NoCollateral) ||
(marketStateError === true &&
errors.summary?.type === SummaryValidationType.MarketState) ||
(marketTradingModeError === true &&
errors.summary?.type === SummaryValidationType.TradingMode)
) {
clearErrors('summary');
}
checkForErrors();
}, [
hasNoBalance,
marketStateError,
marketTradingModeError,
clearErrors,
errors.summary,
errors.summary?.message,
errors.summary?.type,
checkForErrors,
]);
const onSubmit = useCallback(
(order: OrderSubmission) => {
checkForErrors();
submit(
normalizeOrderSubmission(
order,
@@ -154,11 +145,11 @@ export const DealTicket = ({
)
);
},
[submit, market.decimalPlaces, market.positionDecimalPlaces]
[checkForErrors, submit, market.decimalPlaces, market.positionDecimalPlaces]
);
// if an order doesn't exist one will be created by the store immediately
if (!order || !normalizedOrder) return null;
if (!order) return null;
return (
<form
@@ -184,9 +175,7 @@ export const DealTicket = ({
type,
// when changing type also update the tif to what was last used of new type
timeInForce: lastTIF[type] || order.timeInForce,
expiresAt: undefined,
});
clearErrors('expiresAt');
}}
market={market}
marketData={marketData}
@@ -234,12 +223,7 @@ export const DealTicket = ({
update({ timeInForce });
// Set tif value for the given order type, so that when switching
// types we know the last used TIF for the given order type
setLastTIF((curr) => ({
...curr,
[order.type]: timeInForce,
expiresAt: undefined,
}));
clearErrors('expiresAt');
setLastTIF((curr) => ({ ...curr, [order.type]: timeInForce }));
}}
market={market}
marketData={marketData}
@@ -270,10 +254,9 @@ export const DealTicket = ({
)}
<SummaryMessage
errorMessage={errors.summary?.message}
asset={asset}
marketTradingMode={marketData.marketTradingMode}
balance={balance}
margin={totalMargin}
market={market}
marketData={marketData}
order={order}
isReadOnly={isReadOnly}
pubKey={pubKey}
onClickCollateral={onClickCollateral}
@@ -283,12 +266,9 @@ export const DealTicket = ({
variant={order.side === Schema.Side.SIDE_BUY ? 'ternary' : 'secondary'}
/>
<DealTicketFeeDetails
order={normalizedOrder}
order={order}
market={market}
marketData={marketData}
margin={margin}
totalMargin={totalMargin}
balance={marginAccountBalance}
/>
</form>
);
@@ -300,10 +280,9 @@ export const DealTicket = ({
*/
interface SummaryMessageProps {
errorMessage?: string;
asset: { id: string; symbol: string; name: string; decimals: number };
marketTradingMode: MarketData['marketTradingMode'];
balance: string;
margin: string;
market: Market;
marketData: MarketData;
order: OrderObj;
isReadOnly: boolean;
pubKey: string | null;
onClickCollateral?: () => void;
@@ -311,17 +290,22 @@ interface SummaryMessageProps {
const SummaryMessage = memo(
({
errorMessage,
asset,
marketTradingMode,
balance,
margin,
market,
marketData,
order,
isReadOnly,
pubKey,
onClickCollateral,
}: SummaryMessageProps) => {
// Specific error UI for if balance is so we can
// render a deposit dialog
const asset = market.tradableInstrument.instrument.product.settlementAsset;
const assetSymbol = asset.symbol;
const { balanceError, balance, margin } = useOrderMarginValidation({
market,
marketData,
order,
});
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
@@ -365,7 +349,7 @@ const SummaryMessage = memo(
return (
<div className="mb-2">
<ZeroBalanceError
asset={asset}
asset={market.tradableInstrument.instrument.product.settlementAsset}
onClickCollateral={onClickCollateral}
/>
</div>
@@ -386,16 +370,21 @@ const SummaryMessage = memo(
// If there is no blocking error but user doesn't have enough
// balance render the margin warning, but still allow submission
if (BigInt(balance) < BigInt(margin)) {
return <MarginWarning balance={balance} margin={margin} asset={asset} />;
if (balanceError) {
return (
<div className="mb-2">
<MarginWarning balance={balance} margin={margin} asset={asset} />
</div>
);
}
// Show auction mode warning
if (
[
Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
].includes(marketTradingMode)
].includes(marketData.marketTradingMode)
) {
return (
<div className="mb-2">
@@ -55,7 +55,6 @@ export const MarketSelector = ({ market, setMarket, ItemRenderer }: Props) => {
const { data, loading, error } = useDataProvider({
dataProvider: marketsProvider,
variables: undefined,
skipUpdates: true,
});
-10
View File
@@ -7,15 +7,6 @@ export const EST_MARGIN_TOOLTIP_TEXT = (settlementAsset: string) =>
For example, for a notional size of $500, if the margin requirement is 10%, then the estimated margin would be approximately $50.`,
[settlementAsset]
);
export const EST_TOTAL_MARGIN_TOOLTIP_TEXT = t(
'Estimated total margin that will cover open position, active orders and this order.'
);
export const MARGIN_ACCOUNT_TOOLTIP_TEXT = t('Margin account balance');
export const MARGIN_DIFF_TOOLTIP_TEXT = (settlementAsset: string) =>
t(
"The additional margin required for your new position (taking into account volume and open orders), compared to your current margin. Measured in the market's settlement asset (%s).",
[settlementAsset]
);
export const CONTRACTS_MARGIN_TOOLTIP_TEXT = t(
'The number of contracts determines how many units of the futures contract to buy or sell. For example, this is similar to buying one share of a listed company. The value of 1 contract is equivalent to the price of the contract. For example, if the current price is $50, then one contract is worth $50.'
);
@@ -50,7 +41,6 @@ export enum MarketModeValidationType {
}
export enum SummaryValidationType {
NoPubKey = 'NoPubKey',
NoCollateral = 'NoCollateral',
TradingMode = 'MarketTradingMode',
MarketState = 'MarketState',
+2
View File
@@ -4,3 +4,5 @@ export * from './use-fee-deal-ticket-details';
export * from './use-market-positions';
export * from './use-maximum-position-size';
export * from './use-order-closeout';
export * from './use-order-margin';
export * from './use-order-margin-validation';
@@ -1,3 +1,4 @@
import { useMemo } from 'react';
import { marketDepthProvider } from '@vegaprotocol/market-depth';
import * as Schema from '@vegaprotocol/types';
import type { Market } from '@vegaprotocol/market-list';
@@ -12,10 +13,11 @@ interface Props {
}
export const useCalculateSlippage = ({ market, order }: Props) => {
const variables = useMemo(() => ({ marketId: market.id }), [market.id]);
const { data } = useThrottledDataProvider(
{
dataProvider: marketDepthProvider,
variables: { marketId: market.id },
variables,
},
1000
);
@@ -3,24 +3,24 @@ import {
addDecimal,
addDecimalsFormatNumber,
formatNumber,
toBigNum,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import { useVegaWallet } from '@vegaprotocol/wallet';
import BigNumber from 'bignumber.js';
import { useMemo } from 'react';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import {
EST_TOTAL_MARGIN_TOOLTIP_TEXT,
EST_CLOSEOUT_TOOLTIP_TEXT,
EST_MARGIN_TOOLTIP_TEXT,
NOTIONAL_SIZE_TOOLTIP_TEXT,
MARGIN_ACCOUNT_TOOLTIP_TEXT,
MARGIN_DIFF_TOOLTIP_TEXT,
} from '../constants';
import { useCalculateSlippage } from './use-calculate-slippage';
import { useOrderCloseOut } from './use-order-closeout';
import { useMarketAccountBalance } from '@vegaprotocol/accounts';
import { useOrderMargin } from './use-order-margin';
import type { OrderMargin } from './use-order-margin';
import { getDerivedPrice } from '../utils/get-price';
import { useEstimateOrderQuery } from './__generated__/EstimateOrder';
import type { EstimateOrderQuery } from './__generated__/EstimateOrder';
export const useFeeDealTicketDetails = (
order: OrderSubmissionBody['orderSubmission'],
@@ -28,23 +28,33 @@ export const useFeeDealTicketDetails = (
marketData: MarketData
) => {
const { pubKey } = useVegaWallet();
const { accountBalance } = useMarketAccountBalance(market.id);
const slippage = useCalculateSlippage({ market, order });
const price = useMemo(() => {
return getDerivedPrice(order, marketData);
}, [order, marketData]);
const derivedPrice = useMemo(() => {
return getDerivedPrice(order, market, marketData);
}, [order, market, marketData]);
const { data: estMargin } = useEstimateOrderQuery({
variables: {
marketId: market.id,
partyId: pubKey || '',
price,
size: order.size,
side: order.side,
timeInForce: order.timeInForce,
type: order.type,
},
skip: !pubKey || !market || !order.size || !price,
// Note this isn't currently used anywhere
const slippageAdjustedPrice = useMemo(() => {
if (derivedPrice) {
if (slippage && parseFloat(slippage) !== 0) {
const isLong = order.side === Schema.Side.SIDE_BUY;
const multiplier = new BigNumber(1)[isLong ? 'plus' : 'minus'](
parseFloat(slippage) / 100
);
return new BigNumber(derivedPrice).multipliedBy(multiplier).toNumber();
}
return derivedPrice;
}
return null;
}, [derivedPrice, order.side, slippage]);
const estMargin = useOrderMargin({
order,
market,
marketData,
partyId: pubKey || '',
derivedPrice,
});
const estCloseOut = useOrderCloseOut({
@@ -54,13 +64,13 @@ export const useFeeDealTicketDetails = (
});
const notionalSize = useMemo(() => {
if (price && order.size) {
return toBigNum(order.size, market.positionDecimalPlaces)
.multipliedBy(addDecimal(price, market.decimalPlaces))
if (derivedPrice && order.size) {
return new BigNumber(order.size)
.multipliedBy(addDecimal(derivedPrice, market.decimalPlaces))
.toString();
}
return null;
}, [price, order.size, market.decimalPlaces, market.positionDecimalPlaces]);
}, [derivedPrice, order.size, market.decimalPlaces]);
const assetSymbol =
market.tradableInstrument.instrument.product.settlementAsset.symbol;
@@ -70,41 +80,41 @@ export const useFeeDealTicketDetails = (
market,
assetSymbol,
notionalSize,
accountBalance,
estimateOrder: estMargin?.estimateOrder,
estMargin,
estCloseOut,
slippage,
slippageAdjustedPrice,
};
}, [
market,
assetSymbol,
notionalSize,
accountBalance,
estMargin,
estCloseOut,
slippage,
slippageAdjustedPrice,
]);
};
export interface FeeDetails {
balance: string;
market: Market;
assetSymbol: string;
notionalSize: string | null;
estMargin: OrderMargin | null;
estCloseOut: string | null;
estimateOrder: EstimateOrderQuery['estimateOrder'] | undefined;
margin: string;
totalMargin: string;
slippage: string | null;
}
export const getFeeDetailsValues = ({
balance,
assetSymbol,
estimateOrder,
market,
notionalSize,
totalMargin,
estMargin,
estCloseOut,
market,
}: FeeDetails) => {
const assetDecimals =
market.tradableInstrument.instrument.product.settlementAsset.decimals;
const quoteName = market.tradableInstrument.instrument.product.quoteName;
const formatValueWithMarketDp = (
value: string | number | null | undefined
): string => {
@@ -119,12 +129,7 @@ export const getFeeDetailsValues = ({
? addDecimalsFormatNumber(value, assetDecimals)
: '-';
};
const details: {
label: string;
value?: string | null;
symbol: string;
labelDescription: React.ReactNode;
}[] = [
return [
{
label: t('Notional'),
value: formatValueWithMarketDp(notionalSize),
@@ -134,8 +139,8 @@ export const getFeeDetailsValues = ({
{
label: t('Fees'),
value:
estimateOrder?.totalFeeAmount &&
`~${formatValueWithAssetDp(estimateOrder?.totalFeeAmount)}`,
estMargin?.totalFees &&
`~${formatValueWithAssetDp(estMargin?.totalFees)}`,
labelDescription: (
<>
<span>
@@ -144,7 +149,7 @@ export const getFeeDetailsValues = ({
)}
</span>
<FeesBreakdown
fees={estimateOrder?.fee}
fees={estMargin?.fees}
feeFactors={market.fees.factors}
symbol={assetSymbol}
decimals={assetDecimals}
@@ -153,40 +158,18 @@ export const getFeeDetailsValues = ({
),
symbol: assetSymbol,
},
/*
{
label: t('Initial margin'),
value: margin && `~${formatValueWithAssetDp(margin)}`,
label: t('Margin'),
value:
estMargin?.margin && `~${formatValueWithAssetDp(estMargin?.margin)}`,
symbol: assetSymbol,
labelDescription: EST_MARGIN_TOOLTIP_TEXT(assetSymbol),
},
*/
{
label: t('Margin required'),
value: `~${formatValueWithAssetDp(
balance
? (BigInt(totalMargin) - BigInt(balance)).toString()
: totalMargin
)}`,
symbol: assetSymbol,
labelDescription: MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol),
label: t('Liquidation'),
value: estCloseOut && `~${formatValueWithMarketDp(estCloseOut)}`,
symbol: market.tradableInstrument.instrument.product.quoteName,
labelDescription: EST_CLOSEOUT_TOOLTIP_TEXT(quoteName),
},
];
if (balance) {
details.push({
label: t('Projected margin'),
value: `~${formatValueWithAssetDp(totalMargin)}`,
symbol: assetSymbol,
labelDescription: EST_TOTAL_MARGIN_TOOLTIP_TEXT,
});
}
details.push({
label: t('Current margin allocation'),
value: balance
? `~${formatValueWithAssetDp(balance)}`
: `${formatValueWithAssetDp(balance)}`,
symbol: assetSymbol,
labelDescription: MARGIN_ACCOUNT_TOOLTIP_TEXT,
});
return details;
};
@@ -0,0 +1,11 @@
import { useAccountBalance } from '@vegaprotocol/accounts';
import { toBigNum } from '@vegaprotocol/utils';
export const useHasNoBalance = (assetId: string) => {
const { accountBalance, accountDecimals } = useAccountBalance(assetId);
const balance =
accountBalance && accountDecimals !== null
? toBigNum(accountBalance, accountDecimals)
: toBigNum('0', 0);
return balance.isZero();
};
@@ -1,69 +0,0 @@
import { useMemo } from 'react';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { marketDataProvider } from '@vegaprotocol/market-list';
import {
calculateMargins,
// getDerivedPrice,
volumeAndMarginProvider,
} from '@vegaprotocol/positions';
import { Side } from '@vegaprotocol/types';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { marketInfoProvider } from '@vegaprotocol/market-info';
export const useInitialMargin = (
marketId: OrderSubmissionBody['orderSubmission']['marketId'],
order?: OrderSubmissionBody['orderSubmission']
) => {
const { pubKey: partyId } = useVegaWallet();
const commonVariables = { marketId, partyId: partyId || '' };
const { data: marketData } = useDataProvider({
dataProvider: marketDataProvider,
variables: { marketId },
});
const { data: activeVolumeAndMargin } = useDataProvider({
dataProvider: volumeAndMarginProvider,
variables: commonVariables,
skip: !partyId,
});
const { data: marketInfo } = useDataProvider({
dataProvider: marketInfoProvider,
variables: commonVariables,
});
let totalMargin = '0';
let margin = '0';
if (marketInfo?.riskFactors && marketData && order) {
const {
positionDecimalPlaces,
decimalPlaces,
tradableInstrument,
riskFactors,
} = marketInfo;
const { marginCalculator, instrument } = tradableInstrument;
const { decimals } = instrument.product.settlementAsset;
margin = totalMargin = calculateMargins({
side: order.side,
size: order.size,
price: marketData.markPrice, // getDerivedPrice(order, marketData), same in positions-data-providers
positionDecimalPlaces,
decimalPlaces,
decimals,
scalingFactors: marginCalculator?.scalingFactors,
riskFactors,
}).initialMargin;
}
if (activeVolumeAndMargin) {
let sellMargin = BigInt(activeVolumeAndMargin.sellInitialMargin);
let buyMargin = BigInt(activeVolumeAndMargin.buyInitialMargin);
if (order?.side === Side.SIDE_SELL) {
sellMargin += BigInt(totalMargin);
} else {
buyMargin += BigInt(totalMargin);
}
totalMargin =
sellMargin > buyMargin ? sellMargin.toString() : buyMargin.toString();
}
return useMemo(() => ({ totalMargin, margin }), [totalMargin, margin]);
};
@@ -0,0 +1,48 @@
import { useMemo } from 'react';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { toBigNum } from '@vegaprotocol/utils';
import { useAccountBalance } from '@vegaprotocol/accounts';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { useOrderMargin } from './use-order-margin';
import type { Market, MarketData } from '@vegaprotocol/market-list';
interface Props {
market: Market;
marketData: MarketData;
order: OrderSubmissionBody['orderSubmission'];
}
export const useOrderMarginValidation = ({
market,
marketData,
order,
}: Props) => {
const { pubKey } = useVegaWallet();
const estMargin = useOrderMargin({
order,
market,
marketData,
partyId: pubKey || '',
});
const { id: assetId, decimals: assetDecimals } =
market.tradableInstrument.instrument.product.settlementAsset;
const { accountBalance, accountDecimals } = useAccountBalance(assetId);
const balance =
accountBalance && accountDecimals !== null
? toBigNum(accountBalance, accountDecimals)
: toBigNum('0', assetDecimals);
const margin = toBigNum(estMargin?.margin || 0, assetDecimals);
// return only simple types (bool, string) for make memo sensible
const balanceError = balance.isGreaterThan(0) && balance.isLessThan(margin);
const balanceAsString = balance.toString();
const marginAsString = margin.toString();
return useMemo(() => {
return {
balance: balanceAsString,
margin: marginAsString,
balanceError,
};
}, [balanceAsString, marginAsString, balanceError]);
};
@@ -0,0 +1,116 @@
import { renderHook } from '@testing-library/react';
import { useQuery } from '@apollo/client';
import { BigNumber } from 'bignumber.js';
import type { PositionMargin } from './use-market-positions';
import type { Props } from './use-order-margin';
import { useOrderMargin } from './use-order-margin';
import * as Schema from '@vegaprotocol/types';
import type { Market, MarketData } from '@vegaprotocol/market-list';
let mockEstimateData = {
estimateOrder: {
fee: {
makerFee: '100000.000',
infrastructureFee: '100000.000',
liquidityFee: '100000.000',
},
marginLevels: {
initialLevel: '200000',
},
},
};
jest.mock('@apollo/client', () => ({
...jest.requireActual('@apollo/client'),
useQuery: jest.fn(() => ({ data: mockEstimateData })),
}));
let mockMarketPositions: PositionMargin = {
openVolume: '1',
balance: '100000',
};
jest.mock('./use-market-positions', () => ({
useMarketPositions: ({
marketId,
partyId,
}: {
marketId: string;
partyId: string;
}) => mockMarketPositions,
}));
describe('useOrderMargin', () => {
const marketId = 'marketId';
const args: Props = {
order: {
marketId,
size: '2',
side: Schema.Side.SIDE_BUY,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_IOC,
type: Schema.OrderType.TYPE_MARKET,
},
market: {
id: marketId,
decimalPlaces: 2,
positionDecimalPlaces: 0,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
} as unknown as Market,
marketData: {
indicativePrice: '100',
markPrice: '200',
} as unknown as MarketData,
partyId: 'partyId',
};
afterEach(() => {
jest.clearAllMocks();
});
it('should calculate margin correctly', () => {
const { result } = renderHook(() => useOrderMargin(args));
expect(result.current?.margin).toEqual('100000');
expect((useQuery as jest.Mock).mock.calls[0][1].variables.size).toEqual(
args.order.size
);
});
it('should calculate fees correctly', () => {
const { result } = renderHook(() => useOrderMargin(args));
expect(result.current?.totalFees).toEqual('300000');
});
it('should not subtract initialMargin if there is no position', () => {
mockMarketPositions = null;
const { result } = renderHook(() => useOrderMargin(args));
expect(result.current?.margin).toEqual('200000');
expect((useQuery as jest.Mock).mock.calls[0][1].variables.size).toEqual(
args.order.size
);
});
it('should return empty value if API fails', () => {
mockEstimateData = {
estimateOrder: {
fee: {
makerFee: '100000.000',
infrastructureFee: '100000.000',
liquidityFee: '100000.000',
},
marginLevels: {
initialLevel: '',
},
},
};
const { result } = renderHook(() => useOrderMargin(args));
expect(result.current).toEqual(null);
const calledSize = new BigNumber(mockMarketPositions?.openVolume || 0)
.plus(args.order.size)
.toString();
expect((useQuery as jest.Mock).mock.calls[0][1].variables.size).toEqual(
calledSize
);
});
});
@@ -0,0 +1,76 @@
import { useMemo } from 'react';
import { BigNumber } from 'bignumber.js';
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { removeDecimal } from '@vegaprotocol/utils';
import { useMarketPositions } from './use-market-positions';
import { useEstimateOrderQuery } from './__generated__/EstimateOrder';
import type { Market, MarketData } from '@vegaprotocol/market-list';
import { getDerivedPrice } from '../utils/get-price';
export interface Props {
order: OrderSubmissionBody['orderSubmission'];
market: Market;
marketData: MarketData;
partyId: string;
derivedPrice?: string;
}
export interface OrderMargin {
margin: string;
totalFees: string | null;
fees: {
makerFee: string;
liquidityFee: string;
infrastructureFee: string;
};
}
export const useOrderMargin = ({
order,
market,
marketData,
partyId,
derivedPrice,
}: Props): OrderMargin | null => {
const { balance } = useMarketPositions({ marketId: market.id }) || {};
const priceForEstimate =
derivedPrice || getDerivedPrice(order, market, marketData);
const { data } = useEstimateOrderQuery({
variables: {
marketId: market.id,
partyId,
price: priceForEstimate,
size: removeDecimal(order.size, market.positionDecimalPlaces),
side: order.side,
timeInForce: order.timeInForce,
type: order.type,
},
skip: !partyId || !market.id || !order.size || !priceForEstimate,
});
const { makerFee, liquidityFee, infrastructureFee } = data?.estimateOrder
.fee || { makerFee: '', liquidityFee: '', infrastructureFee: '' };
const { initialLevel } = data?.estimateOrder.marginLevels ?? {};
return useMemo(() => {
if (initialLevel) {
const margin = BigNumber.maximum(
0,
new BigNumber(initialLevel).minus(balance || 0)
).toString();
const fees = new BigNumber(makerFee)
.plus(liquidityFee)
.plus(infrastructureFee)
.toString();
return {
margin,
totalFees: fees,
fees: {
makerFee,
liquidityFee,
infrastructureFee,
},
};
}
return null;
}, [initialLevel, makerFee, liquidityFee, infrastructureFee, balance]);
};
+12 -19
View File
@@ -64,27 +64,20 @@ export function generateMarketData(
id: 'market-id',
__typename: 'Market',
},
auctionEnd: '2022-06-21T17:18:43.484055236Z',
auctionStart: '2022-06-21T17:18:43.484055236Z',
bestBidPrice: '0',
bestBidVolume: '0',
bestOfferPrice: '0',
bestOfferVolume: '0',
bestStaticBidPrice: '0',
bestStaticBidVolume: '0',
bestStaticOfferPrice: '0',
bestStaticOfferVolume: '0',
indicativePrice: '100',
indicativeVolume: '10',
marketState: Schema.MarketState.STATE_ACTIVE,
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketValueProxy: '',
markPrice: '200',
midPrice: '0',
openInterest: '',
staticMidPrice: '0',
suppliedStake: '1000',
auctionEnd: '2022-06-21T17:18:43.484055236Z',
targetStake: '1000000',
suppliedStake: '1000',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0',
indicativePrice: '100',
bestStaticBidPrice: '0',
bestStaticOfferPrice: '0',
indicativeVolume: '10',
bestBidPrice: '0',
bestOfferPrice: '0',
markPrice: '200',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_BATCH,
};
return merge(defaultMarketData, override);
+4 -2
View File
@@ -1,6 +1,7 @@
import { removeDecimal } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import { isMarketInAuction } from './is-market-in-auction';
import type { MarketData } from '@vegaprotocol/market-list';
import type { MarketData, Market } from '@vegaprotocol/market-list';
/**
* Get the market price based on market mode (auction or not auction)
@@ -33,6 +34,7 @@ export const getDerivedPrice = (
type: Schema.OrderType;
price?: string | undefined;
},
market: Market,
marketData: MarketData
) => {
// If order type is market we should use either the mark price
@@ -42,7 +44,7 @@ export const getDerivedPrice = (
// Use the market price if order is a market order
let price;
if (order.type === Schema.OrderType.TYPE_LIMIT && order.price) {
price = order.price;
price = removeDecimal(order.price, market.decimalPlaces);
} else {
price = getMarketPrice(marketData);
}
@@ -12,7 +12,6 @@ export const DepositContainer = ({ assetId }: { assetId?: string }) => {
const { VEGA_ENV } = useEnvironment();
const { data, loading, error } = useDataProvider({
dataProvider: enabledAssetsProvider,
variables: undefined,
});
return (
+1 -1
View File
@@ -15,7 +15,7 @@ interface Actions {
close: () => void;
}
export const useDepositDialog = create<State & Actions>()((set) => ({
export const useDepositDialog = create<State & Actions>((set) => ({
isOpen: false,
assetId: undefined,
open: (assetId) => set(() => ({ assetId, isOpen: true })),
+1 -1
View File
@@ -392,7 +392,7 @@ const FormButton = ({ approved, selectedAsset }: FormButtonProps) => {
data-testid="deposit-submit"
variant={isActive ? 'primary' : 'default'}
fill={true}
disabled={invalidChain}
disabled={invalidChain || (selectedAsset && !approved)}
>
{t('Deposit')}
</Button>
-2
View File
@@ -72,9 +72,7 @@ export const DepositsTable = forwardRef<
field="txHash"
cellRenderer={({
value,
data,
}: VegaICellRendererParams<DepositFieldsFragment, 'txHash'>) => {
if (!data) return null;
if (!value) return '-';
return (
<Link
+15 -30
View File
@@ -3,7 +3,6 @@ import { useEnvironment } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { ExternalLink, Intent, Notification } from '@vegaprotocol/ui-toolkit';
import { EthTxStatus, useEthTransactionStore } from '@vegaprotocol/web3';
import { getFaucetError } from './get-faucet-error';
interface FaucetNotificationProps {
isActive: boolean;
@@ -37,13 +36,13 @@ export const FaucetNotification = ({
}
if (tx.status === EthTxStatus.Error) {
const errorMessage = getFaucetError(tx.error, selectedAsset.symbol);
return (
<div className="mb-4">
<Notification
intent={Intent.Danger}
testId="faucet-error"
message={errorMessage}
// @ts-ignore tx.error not typed correctly
message={t(`Faucet failed: ${tx.error?.reason}`)}
/>
</div>
);
@@ -56,7 +55,7 @@ export const FaucetNotification = ({
intent={Intent.Warning}
testId="faucet-requested"
message={t(
`Confirm the transaction in your Ethereum wallet to use the ${selectedAsset?.symbol} faucet`
`Go to your Ethereum wallet and approve the faucet transaction for ${selectedAsset?.symbol}`
)}
/>
</div>
@@ -70,21 +69,14 @@ export const FaucetNotification = ({
intent={Intent.Primary}
testId="faucet-pending"
message={
<>
<p className="mb-2">
{t(
'Your request for funds from the %s faucet is being confirmed by the Ethereum network',
selectedAsset.symbol
)}{' '}
</p>
<p>
{t('Faucet pending...')}{' '}
{tx.txHash && (
<p>
<ExternalLink href={`${ETHERSCAN_URL}/tx/${tx.txHash}`}>
{t('View on Etherscan')}
</ExternalLink>
</p>
<ExternalLink href={`${ETHERSCAN_URL}/tx/${tx.txHash}`}>
{t('View on Etherscan')}
</ExternalLink>
)}
</>
</p>
}
/>
</div>
@@ -98,21 +90,14 @@ export const FaucetNotification = ({
intent={Intent.Success}
testId="faucet-confirmed"
message={
<>
<p className="mb-2">
{t(
'%s has been deposited in your Ethereum wallet',
selectedAsset.symbol
)}{' '}
</p>
<p>
{t('Faucet successful')}{' '}
{tx.txHash && (
<p>
<ExternalLink href={`${ETHERSCAN_URL}/tx/${tx.txHash}`}>
{t('View on Etherscan')}
</ExternalLink>
</p>
<ExternalLink href={`${ETHERSCAN_URL}/tx/${tx.txHash}`}>
{t('View on Etherscan')}
</ExternalLink>
)}
</>
</p>
}
/>
</div>
-24
View File
@@ -1,24 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { TxError } from '@vegaprotocol/web3';
export const getFaucetError = (error: TxError | null, symbol: string) => {
const reasonMap: {
[reason: string]: string;
} = {
'faucet not enabled': t(
'The %s faucet is not available at this time',
symbol
),
'must wait faucetCallLimit between faucet calls': t(
'You have exceeded the maximum number of faucet attempts allowed'
),
'user rejected transaction': t(
'The faucet transaction was rejected by the connected Ethereum wallet'
),
};
// render a customized failure message from the map above or fallback
// to a non generic error message
return error && 'reason' in error && reasonMap[error.reason]
? reasonMap[error.reason]
: t('Faucet of %s failed', symbol);
};
+18
View File
@@ -0,0 +1,18 @@
import { act } from 'react-dom/test-utils';
const zu = jest.requireActual('zustand'); // if using jest
// a variable to hold reset functions for all stores declared in the app
const storeResetFns = new Set();
// when creating a store, we get its initial state, create a reset function and add it in the set
export const create = (createState) => {
const store = zu.create(createState);
const initialState = store.getState();
storeResetFns.add(() => store.setState(initialState, true));
return store;
};
// Reset all stores after each test run
beforeEach(() => {
act(() => storeResetFns.forEach((resetFn) => resetFn()));
});
-21
View File
@@ -1,21 +0,0 @@
import type { StateCreator } from 'zustand';
import { act } from 'react-dom/test-utils';
const { create: actualCreate } = jest.requireActual('zustand'); // if using jest
// a variable to hold reset functions for all stores declared in the app
const storeResetFns = new Set<() => void>();
// when creating a store, we get its initial state, create a reset function and add it in the set
export const create =
() =>
<S>(createState: StateCreator<S>) => {
const store = actualCreate(createState);
const initialState = store.getState();
storeResetFns.add(() => store.setState(initialState, true));
return store;
};
// Reset all stores after each test run
beforeEach(() => {
act(() => storeResetFns.forEach((resetFn) => resetFn()));
});
@@ -34,7 +34,7 @@ export type EnvStore = Env & Actions;
export const STORAGE_KEY = 'vega_url';
const SUBSCRIPTION_TIMEOUT = 3000;
export const useEnvironment = create<EnvStore>()((set, get) => ({
export const useEnvironment = create<EnvStore>((set, get) => ({
...compileEnvVars(),
nodes: [],
status: 'default',
+1 -2
View File
@@ -17,8 +17,7 @@
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"jest.config.ts",
"__mocks__"
"jest.config.ts"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}
+6 -20
View File
@@ -13,7 +13,6 @@ import type { PageInfo, Edge } from '@vegaprotocol/utils';
import { FillsDocument, FillsEventDocument } from './__generated__/Fills';
import type {
FillsQuery,
FillsQueryVariables,
FillFieldsFragment,
FillEdgeFragment,
FillsEventSubscription,
@@ -57,28 +56,19 @@ const update = (
});
};
export type Trade = Omit<FillFieldsFragment, 'market'> & {
market?: Market;
isLastPlaceholder?: boolean;
};
export type Trade = Omit<FillFieldsFragment, 'market'> & { market?: Market };
export type TradeEdge = Edge<Trade>;
const getData = (responseData: FillsQuery | null): FillEdgeFragment[] =>
responseData?.party?.tradesConnection?.edges || [];
const getPageInfo = (responseData: FillsQuery | null): PageInfo | null =>
responseData?.party?.tradesConnection?.pageInfo || null;
const getPageInfo = (responseData: FillsQuery): PageInfo | null =>
responseData.party?.tradesConnection?.pageInfo || null;
const getDelta = (subscriptionData: FillsEventSubscription) =>
subscriptionData.trades || [];
export const fillsProvider = makeDataProvider<
Parameters<typeof getData>['0'],
ReturnType<typeof getData>,
Parameters<typeof getDelta>['0'],
ReturnType<typeof getDelta>,
FillsQueryVariables
>({
export const fillsProvider = makeDataProvider({
query: FillsDocument,
subscriptionQuery: FillsEventDocument,
update,
@@ -93,13 +83,9 @@ export const fillsProvider = makeDataProvider<
export const fillsWithMarketProvider = makeDerivedDataProvider<
(TradeEdge | null)[],
Trade[],
FillsQueryVariables
Trade[]
>(
[
fillsProvider,
(callback, client) => marketsProvider(callback, client, undefined),
],
[fillsProvider, marketsProvider],
(partsData): (TradeEdge | null)[] =>
(partsData[0] as ReturnType<typeof getData>)?.map(
(edge) =>
+8 -42
View File
@@ -1,12 +1,10 @@
import type { AgGridReact } from 'ag-grid-react';
import { useCallback, useRef } from 'react';
import { useRef } from 'react';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n';
import { FillsTable } from './fills-table';
import type { BodyScrollEvent, BodyScrollEndEvent } from 'ag-grid-community';
import { useFillsList } from './use-fills-list';
import type { Trade } from './fills-data-provider';
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
interface FillsManagerProps {
partyId: string;
@@ -21,51 +19,22 @@ export const FillsManager = ({
}: FillsManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const scrolledToTop = useRef(true);
const {
data,
error,
loading,
addNewRows,
getRows,
reload,
makeBottomPlaceholders,
} = useFillsList({
const { data, error, loading, addNewRows, getRows, reload } = useFillsList({
partyId,
marketId,
gridRef,
scrolledToTop,
});
const checkBottomPlaceholder = useCallback(() => {
const rowCont = gridRef.current?.api?.getModel().getRowCount() ?? 0;
const lastRowIndex = gridRef.current?.api?.getLastDisplayedRow();
if (lastRowIndex && rowCont - 1 === lastRowIndex) {
const lastrow = gridRef.current?.api.getDisplayedRowAtIndex(lastRowIndex);
lastrow?.setRowHeight(50);
makeBottomPlaceholders(lastrow?.data);
gridRef.current?.api.onRowHeightChanged();
gridRef.current?.api.refreshInfiniteCache();
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
if (event.top === 0) {
addNewRows();
}
}, [makeBottomPlaceholders]);
};
const onBodyScrollEnd = useCallback(
(event: BodyScrollEndEvent) => {
if (event.top === 0) {
addNewRows();
}
checkBottomPlaceholder();
},
[addNewRows, checkBottomPlaceholder]
);
const onBodyScroll = useCallback((event: BodyScrollEvent) => {
const onBodyScroll = (event: BodyScrollEvent) => {
scrolledToTop.current = event.top <= 0;
}, []);
const { isFullWidthRow, fullWidthCellRenderer, rowClassRules } =
useBottomPlaceholder<Trade>({
gridRef,
});
};
return (
<div className="h-full relative">
@@ -79,9 +48,6 @@ export const FillsManager = ({
onMarketClick={onMarketClick}
suppressLoadingOverlay
suppressNoRowsOverlay
isFullWidthRow={isFullWidthRow}
fullWidthCellRenderer={fullWidthCellRenderer}
rowClassRules={rowClassRules}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
+9 -27
View File
@@ -1,6 +1,6 @@
import type { RefObject } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useCallback, useRef } from 'react';
import { useCallback, useMemo, useRef } from 'react';
import { makeInfiniteScrollGetRows } from '@vegaprotocol/utils';
import { useDataProvider, updateGridData } from '@vegaprotocol/react-helpers';
import type { Trade, TradeEdge } from './fills-data-provider';
@@ -22,21 +22,6 @@ export const useFillsList = ({
const dataRef = useRef<(TradeEdge | null)[] | null>(null);
const totalCountRef = useRef<number | undefined>(undefined);
const newRows = useRef(0);
const placeholderAdded = useRef(-1);
const makeBottomPlaceholders = useCallback((trade?: Trade) => {
if (!trade) {
if (placeholderAdded.current >= 0) {
dataRef.current?.splice(placeholderAdded.current, 1);
}
placeholderAdded.current = -1;
} else if (placeholderAdded.current === -1) {
dataRef.current?.push({
node: { ...trade, id: `${trade?.id}-1`, isLastPlaceholder: true },
});
placeholderAdded.current = (dataRef.current?.length || 0) - 1;
}
}, []);
const addNewRows = useCallback(() => {
if (newRows.current === 0) {
@@ -88,11 +73,16 @@ export const useFillsList = ({
[gridRef]
);
const { data, error, loading, load, totalCount, reload } = useDataProvider({
const variables = useMemo(() => ({ partyId, marketId }), [partyId, marketId]);
const { data, error, loading, load, totalCount, reload } = useDataProvider<
(TradeEdge | null)[],
Trade[]
>({
dataProvider: fillsWithMarketProvider,
update,
insert,
variables: { partyId, marketId: marketId || '' },
variables,
});
totalCountRef.current = totalCount;
@@ -102,13 +92,5 @@ export const useFillsList = ({
load,
newRows
);
return {
data,
error,
loading,
addNewRows,
getRows,
reload,
makeBottomPlaceholders,
};
return { data, error, loading, addNewRows, getRows, reload };
};
@@ -42,7 +42,7 @@ export const update = (
data: ReturnType<typeof getData> | null,
delta: ReturnType<typeof getData>,
reload: () => void,
variables: LedgerEntriesQueryVariables
variables?: LedgerEntriesQueryVariables
) => {
if (!data) {
return data;
@@ -110,8 +110,8 @@ export const ledgerEntriesProvider = makeDerivedDataProvider<
>(
[
ledgerEntriesOnlyProvider,
(callback, client) => assetsProvider(callback, client, undefined),
(callback, client) => marketsProvider(callback, client, undefined),
(callback, client) => assetsProvider(callback, client),
marketsProvider,
],
([entries, assets, markets]) => {
return entries.map((edge: AggregatedLedgerEntriesEdge) => {
@@ -14,14 +14,11 @@ import {
import type {
MarketLpQuery,
MarketLpQueryVariables,
LiquidityProviderFeeShareFieldsFragment,
LiquidityProviderFeeShareQuery,
LiquidityProviderFeeShareQueryVariables,
LiquidityProviderFeeShareUpdateSubscription,
LiquidityProvisionFieldsFragment,
LiquidityProvisionsQuery,
LiquidityProvisionsQueryVariables,
LiquidityProvisionsUpdateSubscription,
} from './__generated__/MarketLiquidity';
import type { IterableElement } from 'type-fest';
@@ -30,8 +27,7 @@ export const liquidityProvisionsDataProvider = makeDataProvider<
LiquidityProvisionsQuery,
LiquidityProvisionFieldsFragment[],
LiquidityProvisionsUpdateSubscription,
LiquidityProvisionsUpdateSubscription['liquidityProvisions'],
LiquidityProvisionsQueryVariables
LiquidityProvisionsUpdateSubscription['liquidityProvisions']
>({
query: LiquidityProvisionsDocument,
subscriptionQuery: LiquidityProvisionsUpdateDocument,
@@ -103,8 +99,7 @@ export const marketLiquidityDataProvider = makeDataProvider<
MarketLpQuery,
MarketLpQuery,
never,
never,
MarketLpQueryVariables
never
>({
query: MarketLpDocument,
getData: (responseData: MarketLpQuery | null) => {
@@ -116,8 +111,7 @@ export const liquidityFeeShareDataProvider = makeDataProvider<
LiquidityProviderFeeShareQuery,
LiquidityProviderFeeShareFieldsFragment[],
LiquidityProviderFeeShareUpdateSubscription,
LiquidityProviderFeeShareUpdateSubscription['marketsData'][0]['liquidityProviderFeeShare'],
LiquidityProviderFeeShareQueryVariables
LiquidityProviderFeeShareUpdateSubscription['marketsData'][0]['liquidityProviderFeeShare']
>({
query: LiquidityProviderFeeShareDocument,
subscriptionQuery: LiquidityProviderFeeShareUpdateDocument,
@@ -153,11 +147,7 @@ export const liquidityFeeShareDataProvider = makeDataProvider<
},
});
export const lpAggregatedDataProvider = makeDerivedDataProvider<
ReturnType<typeof getLiquidityProvision>,
never,
MarketLpQueryVariables
>(
export const lpAggregatedDataProvider = makeDerivedDataProvider(
[
liquidityProvisionsDataProvider,
marketLiquidityDataProvider,
@@ -5,10 +5,12 @@ import { useDataProvider, useYesterday } from '@vegaprotocol/react-helpers';
import type {
MarketCandles,
MarketMaybeWithDataAndCandles,
MarketsCandlesQueryVariables,
} from '@vegaprotocol/market-list';
import { marketListProvider } from '@vegaprotocol/market-list';
import {
marketsCandlesProvider,
marketListProvider,
} from '@vegaprotocol/market-list';
import type { LiquidityProvisionMarketsQuery } from './__generated__/MarketsLiquidity';
import { LiquidityProvisionMarketsDocument } from './__generated__/MarketsLiquidity';
@@ -95,18 +97,15 @@ export const liquidityMarketsProvider = makeDataProvider<
getData,
});
const liquidityProvisionProvider = makeDerivedDataProvider<
Market[],
never,
Exclude<MarketsCandlesQueryVariables, 'interval'>
>(
const liquidityProvisionProvider = makeDerivedDataProvider<Market[], never>(
[
marketListProvider,
(callback, client, variables) =>
marketListProvider(callback, client, {
since: variables.since,
marketsCandlesProvider(callback, client, {
...variables,
interval: Schema.Interval.INTERVAL_I1D,
}),
(callback, client) => liquidityMarketsProvider(callback, client, undefined),
liquidityMarketsProvider,
],
(parts) => {
return addData(

Some files were not shown because too many files have changed in this diff Show More