Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ea341a818 | ||
|
|
86ce4d28fe | ||
|
|
ba2341ea57 | ||
|
|
ad8eb7dd79 | ||
|
|
e07fce3bb2 | ||
|
|
43d6c787bd | ||
|
|
4c6a46ad40 | ||
|
|
f299aa161b | ||
|
|
c35e85ef6a | ||
|
|
0fa124ed2c | ||
|
|
6fae87d380 | ||
|
|
a2feff77dc | ||
|
|
d13d67d9c3 | ||
|
|
3592971b2b | ||
|
|
20dfd7c22a | ||
|
|
bf959c5c4e | ||
|
|
594546bb06 | ||
|
|
288bea44db | ||
|
|
9fe2a7f55c | ||
|
|
32cb10e82e | ||
|
|
ba82a52855 | ||
|
|
fd9992189f | ||
|
|
180649e2a8 | ||
|
|
740237f355 | ||
|
|
74e814c5fb | ||
|
|
beadef6aec | ||
|
|
26afa3210d | ||
|
|
1212645d87 | ||
|
|
6ffc22a940 | ||
|
|
a563a87daa |
@@ -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 =
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 || [];
|
||||
|
||||
+10
-7
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) => ({
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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[])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 }));
|
||||
|
||||
@@ -9,4 +9,4 @@ type HeaderStore = {
|
||||
[url: string]: HeaderEntry | undefined;
|
||||
};
|
||||
|
||||
export const useHeaderStore = create<HeaderStore>()(() => ({}));
|
||||
export const useHeaderStore = create<HeaderStore>(() => ({}));
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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]);
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 })),
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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()));
|
||||
});
|
||||
@@ -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',
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"jest.config.ts",
|
||||
"__mocks__"
|
||||
"jest.config.ts"
|
||||
],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user