Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ef9614630 | ||
|
|
bc9d87fe30 | ||
|
|
6a21862378 | ||
|
|
0fb4348e9c | ||
|
|
b751bcf17d |
@@ -227,3 +227,31 @@ jobs:
|
||||
name: worker-logs
|
||||
path: ./logs/
|
||||
retention-days: 15
|
||||
#----------------------------------------------
|
||||
# ----- upload market-sim logs -----
|
||||
#----------------------------------------------
|
||||
- name: Find Directory
|
||||
id: find-dir
|
||||
run: |
|
||||
DIR=$(find /tmp -type d -name "vega-sim-*" -print -quit)
|
||||
if [[ -d "$DIR" ]]; then
|
||||
echo "Found directory: $DIR"
|
||||
echo "DIR=$DIR" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Directory not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Compress Files
|
||||
if: env.DIR
|
||||
run: |
|
||||
tar -czvf ${{ github.workspace }}/market-sim-logs.tar.gz -C "$DIR" .
|
||||
echo "Compressed files at ${{ github.workspace }}/market-sim-logs.tar.gz"
|
||||
|
||||
- name: Upload Compressed market-sim-logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: env.DIR
|
||||
with:
|
||||
name: market-sim-logs
|
||||
path: ${{ github.workspace }}/market-sim-logs.tar.gz
|
||||
retention-days: 15
|
||||
|
||||
@@ -44,7 +44,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('icon-cross').click();
|
||||
});
|
||||
|
||||
it.skip('Proposal page displayed on mobile', function () {
|
||||
it('Proposal page displayed on mobile', function () {
|
||||
const proposalTitle = 'Add Lorem Ipsum market';
|
||||
|
||||
cy.common_switch_to_mobile_and_click_toggle();
|
||||
@@ -55,7 +55,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to view new asset proposal', function () {
|
||||
it.skip('Able to view new asset proposal', function () {
|
||||
const proposalTitle = 'Test new asset proposal';
|
||||
const newAssetProposalBody = getNewAssetTxBody();
|
||||
cy.VegaWalletSubmitProposal(newAssetProposalBody);
|
||||
|
||||
@@ -196,6 +196,7 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
|
||||
timeWindow: '3600',
|
||||
scalingFactor: 10,
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
triggeringRatio: '0.7',
|
||||
auctionExtension: '1',
|
||||
},
|
||||
@@ -334,6 +335,7 @@ export function createSuccessorMarketProposalTxBody(
|
||||
timeWindow: '3600',
|
||||
scalingFactor: 10,
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
triggeringRatio: '0.7',
|
||||
auctionExtension: '1',
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from '@vegaprotocol/web3';
|
||||
import { Web3Provider } from '@vegaprotocol/web3';
|
||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||
import { VegaWalletProvider, useChainId } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useVegaTransactionManager,
|
||||
useVegaTransactionUpdater,
|
||||
@@ -96,9 +96,7 @@ const cache: InMemoryCacheConfig = {
|
||||
const Web3Container = ({
|
||||
chainId,
|
||||
}: {
|
||||
/** Ethereum chain id */
|
||||
chainId: number;
|
||||
/** Ethereum provider url */
|
||||
providerUrl: string;
|
||||
}) => {
|
||||
const InitializeHandlers = () => {
|
||||
@@ -125,9 +123,6 @@ const Web3Container = ({
|
||||
MOZILLA_EXTENSION_URL,
|
||||
VEGA_WALLET_URL,
|
||||
} = useEnvironment();
|
||||
|
||||
const vegaChainId = useChainId(VEGA_URL);
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId) {
|
||||
return initializeConnectors(
|
||||
@@ -162,8 +157,7 @@ const Web3Container = ({
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!DocsLinks ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL ||
|
||||
!vegaChainId
|
||||
!MOZILLA_EXTENSION_URL
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -175,7 +169,6 @@ const Web3Container = ({
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
chainId: vegaChainId,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
|
||||
@@ -48,7 +48,6 @@ const vegaWalletConfig: VegaWalletConfig = {
|
||||
chromeExtensionUrl: 'chrome',
|
||||
mozillaExtensionUrl: 'mozilla',
|
||||
},
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
|
||||
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
||||
|
||||
@@ -205,7 +205,6 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
@@ -366,7 +365,6 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,50 +1,14 @@
|
||||
import {
|
||||
Intent,
|
||||
ToastsContainer,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
useToasts,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { ToastsContainer, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
useEthereumTransactionToasts,
|
||||
useEthereumWithdrawApprovalsToasts,
|
||||
useVegaTransactionToasts,
|
||||
useWalletDisconnectToastActions,
|
||||
useWalletDisconnectedToasts,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const WalletDisconnectAdditionalContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const { hideToast } = useWalletDisconnectToastActions();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
return (
|
||||
<p className="mt-2">
|
||||
<TradingButton
|
||||
data-testid="connect-vega-wallet"
|
||||
onClick={() => {
|
||||
hideToast();
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
size="small"
|
||||
intent={Intent.Danger}
|
||||
icon={<VegaIcon name={VegaIconNames.ARROW_RIGHT} size={14} />}
|
||||
>
|
||||
<span className="whitespace-nowrap uppercase">{t('Connect')}</span>
|
||||
</TradingButton>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export const ToastsManager = () => {
|
||||
useVegaTransactionToasts();
|
||||
useEthereumTransactionToasts();
|
||||
useEthereumWithdrawApprovalsToasts();
|
||||
useWalletDisconnectedToasts(<WalletDisconnectAdditionalContent />);
|
||||
|
||||
const toasts = useToasts((store) => store.toasts);
|
||||
return <ToastsContainer order="desc" toasts={toasts} />;
|
||||
|
||||
@@ -28,3 +28,6 @@ NX_REFERRALS=true
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -314,7 +314,7 @@ describe('Closed', () => {
|
||||
});
|
||||
|
||||
it('display market actions', async () => {
|
||||
// Use market with a succcessor Id as the actions dropdown will optionally
|
||||
// Use market with a successor Id as the actions dropdown will optionally
|
||||
// show a link to the successor market
|
||||
const marketsWithSuccessorAndParent = [
|
||||
{
|
||||
|
||||
@@ -137,6 +137,8 @@ const ClosedMarketsDataGrid = ({
|
||||
headerName: t('Market'),
|
||||
field: 'code',
|
||||
cellRenderer: 'MarketCodeCell',
|
||||
width: 150,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Status'),
|
||||
@@ -280,6 +282,7 @@ const ClosedMarketsDataGrid = ({
|
||||
return (
|
||||
<AgGrid
|
||||
rowData={rowData}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
columnDefs={colDefs}
|
||||
getRowId={({ data }) => data.id}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
||||
|
||||
@@ -17,6 +17,7 @@ const defaultColDef = {
|
||||
filter: true,
|
||||
resizable: true,
|
||||
filterParams: { buttons: ['reset'] },
|
||||
minWidth: 120,
|
||||
};
|
||||
|
||||
const components = {
|
||||
|
||||
@@ -53,6 +53,7 @@ export const MarketsPage = () => {
|
||||
size="extra-small"
|
||||
data-testid="propose-new-market"
|
||||
href={externalLink}
|
||||
target="_blank"
|
||||
>
|
||||
{t('Propose a new market')}
|
||||
</TradingAnchorButton>
|
||||
|
||||
@@ -29,6 +29,7 @@ export const useColumnDefs = () => {
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'tradableInstrument.instrument.code',
|
||||
pinned: true,
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
|
||||
@@ -13,7 +13,6 @@ import type { ReactNode } from 'react';
|
||||
import { Web3Provider } from './web3-provider';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { DataLoader } from './data-loader';
|
||||
import { useChainId } from '@vegaprotocol/wallet';
|
||||
|
||||
export const Bootstrapper = ({ children }: { children: ReactNode }) => {
|
||||
const t = useT();
|
||||
@@ -27,16 +26,13 @@ export const Bootstrapper = ({ children }: { children: ReactNode }) => {
|
||||
CHROME_EXTENSION_URL,
|
||||
} = useEnvironment();
|
||||
|
||||
const chainId = useChainId(VEGA_URL);
|
||||
|
||||
if (
|
||||
!VEGA_URL ||
|
||||
!VEGA_WALLET_URL ||
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL ||
|
||||
!DocsLinks ||
|
||||
!chainId
|
||||
!DocsLinks
|
||||
) {
|
||||
return <AppLoader />;
|
||||
}
|
||||
@@ -76,7 +72,6 @@ export const Bootstrapper = ({ children }: { children: ReactNode }) => {
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
chainId,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
|
||||
@@ -42,13 +42,9 @@ export const LiquidityHeader = () => {
|
||||
const assetDecimalPlaces = asset?.decimals || 0;
|
||||
const symbol = asset?.symbol;
|
||||
|
||||
const triggeringRatio =
|
||||
market?.liquidityMonitoringParameters.triggeringRatio || '1';
|
||||
|
||||
const { percentage, status } = useCheckLiquidityStatus({
|
||||
suppliedStake: suppliedStake || 0,
|
||||
targetStake: targetStake || 0,
|
||||
triggeringRatio,
|
||||
});
|
||||
|
||||
const feesObject = feesPaidRes?.paidLiquidityFees?.edges?.find(
|
||||
|
||||
@@ -47,9 +47,6 @@ export const MarketLiquiditySupplied = ({
|
||||
]);
|
||||
|
||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||
const triggeringRatio = Number(
|
||||
params.market_liquidity_targetstake_triggering_ratio
|
||||
);
|
||||
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
@@ -94,7 +91,6 @@ export const MarketLiquiditySupplied = ({
|
||||
const { percentage, status } = useCheckLiquidityStatus({
|
||||
suppliedStake: market?.suppliedStake || 0,
|
||||
targetStake: market?.targetStake || 0,
|
||||
triggeringRatio,
|
||||
});
|
||||
|
||||
const showMessage =
|
||||
|
||||
@@ -131,7 +131,7 @@ def test_terminated_market_no_settlement_date(page: Page, vega: VegaServiceNull)
|
||||
row_selector = page.locator(
|
||||
'[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row'
|
||||
).first
|
||||
expect(row_selector.locator('[col-id="state"]')).to_have_text("Trading Terminated")
|
||||
expect(row_selector.locator('[col-id="state"]')).to_have_text("No trading")
|
||||
expect(row_selector.locator('[col-id="settlementDate"]')).to_have_text("Unknown")
|
||||
|
||||
# TODO Create test for terminated market with settlement date in future
|
||||
|
||||
@@ -174,9 +174,9 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
# market state should be changed to "Trading Terminated" because of the invalid oracle
|
||||
# market state should be changed to "No trading" because of the invalid oracle
|
||||
expect(trading_mode).to_have_text("No trading")
|
||||
expect(market_state).to_have_text("Trading Terminated")
|
||||
expect(market_state).to_have_text("No trading")
|
||||
|
||||
# settle market
|
||||
vega.submit_termination_and_settlement_data(
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import {
|
||||
Intent,
|
||||
useToasts,
|
||||
ToastHeading,
|
||||
CLOSE_AFTER,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useT } from '../use-t';
|
||||
import { VegaWalletConnectButton } from '../../components/vega-wallet-connect-button';
|
||||
|
||||
const WALLET_DISCONNECTED_TOAST_ID = 'WALLET_DISCONNECTED_TOAST_ID';
|
||||
|
||||
export const useWalletDisconnectedToasts = () => {
|
||||
const t = useT();
|
||||
const [hasToast, setToast, updateToast] = useToasts((state) => [
|
||||
state.hasToast,
|
||||
state.setToast,
|
||||
state.update,
|
||||
]);
|
||||
const { isAlive } = useVegaWallet();
|
||||
|
||||
const toast = useMemo(
|
||||
() => ({
|
||||
id: WALLET_DISCONNECTED_TOAST_ID,
|
||||
intent: Intent.Danger,
|
||||
content: (
|
||||
<>
|
||||
<ToastHeading>{t('Wallet connection lost')}</ToastHeading>
|
||||
<p>{t('The connection to the Vega wallet has been lost.')}</p>
|
||||
<p className="mt-2">
|
||||
<VegaWalletConnectButton
|
||||
intent={Intent.Danger}
|
||||
onClick={() => {
|
||||
updateToast(WALLET_DISCONNECTED_TOAST_ID, {
|
||||
hidden: true,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
onClose: () => {
|
||||
updateToast(WALLET_DISCONNECTED_TOAST_ID, {
|
||||
hidden: true,
|
||||
});
|
||||
},
|
||||
closeAfter: CLOSE_AFTER,
|
||||
}),
|
||||
[t, updateToast]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAlive === false) {
|
||||
if (hasToast(WALLET_DISCONNECTED_TOAST_ID)) {
|
||||
updateToast(WALLET_DISCONNECTED_TOAST_ID, { hidden: false });
|
||||
} else {
|
||||
setToast(toast);
|
||||
}
|
||||
}
|
||||
}, [hasToast, isAlive, setToast, t, toast, updateToast]);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Intent, ToastsContainer, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { ToastsContainer, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { useProposalToasts } from '@vegaprotocol/proposals';
|
||||
import { useVegaTransactionToasts } from '@vegaprotocol/web3';
|
||||
import { useEthereumTransactionToasts } from '@vegaprotocol/web3';
|
||||
@@ -6,26 +6,7 @@ import { useEthereumWithdrawApprovalsToasts } from '@vegaprotocol/web3';
|
||||
import { useReadyToWithdrawalToasts } from '@vegaprotocol/withdraws';
|
||||
import { Links } from '../lib/links';
|
||||
import { useReferralToasts } from '../client-pages/referrals/hooks/use-referral-toasts';
|
||||
import {
|
||||
useWalletDisconnectToastActions,
|
||||
useWalletDisconnectedToasts,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { VegaWalletConnectButton } from '../components/vega-wallet-connect-button';
|
||||
|
||||
const WalletDisconnectAdditionalContent = () => {
|
||||
const { hideToast } = useWalletDisconnectToastActions();
|
||||
return (
|
||||
<p className="mt-2">
|
||||
<VegaWalletConnectButton
|
||||
intent={Intent.Danger}
|
||||
onClick={() => {
|
||||
// hide toast when clicked on `Connect`
|
||||
hideToast();
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
import { useWalletDisconnectedToasts } from '../lib/hooks/use-wallet-disconnected-toasts';
|
||||
|
||||
export const ToastsManager = () => {
|
||||
useProposalToasts();
|
||||
@@ -36,7 +17,7 @@ export const ToastsManager = () => {
|
||||
withdrawalsLink: Links.PORTFOLIO(),
|
||||
});
|
||||
useReferralToasts();
|
||||
useWalletDisconnectedToasts(<WalletDisconnectAdditionalContent />);
|
||||
useWalletDisconnectedToasts();
|
||||
|
||||
const toasts = useToasts((store) => store.toasts);
|
||||
return <ToastsContainer order="desc" toasts={toasts} />;
|
||||
|
||||
@@ -67,6 +67,7 @@ const defaultColDef = {
|
||||
sortable: true,
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
comparator: accountValuesComparator,
|
||||
minWidth: 150,
|
||||
};
|
||||
export interface GetRowsParams extends Omit<IGetRowsParams, 'successCallback'> {
|
||||
successCallback(rowsThisBlock: AccountFields[], lastRow?: number): void;
|
||||
@@ -139,6 +140,8 @@ export const AccountTable = ({
|
||||
{
|
||||
headerName: t('Asset'),
|
||||
field: 'asset.symbol',
|
||||
pinned: true,
|
||||
minWidth: 75,
|
||||
headerTooltip: t(
|
||||
'Asset is the collateral that is deposited into the Vega protocol.'
|
||||
),
|
||||
@@ -253,8 +256,8 @@ export const AccountTable = ({
|
||||
colId: 'accounts-actions',
|
||||
field: 'asset.id',
|
||||
...COL_DEFS.actions,
|
||||
minWidth: showDepositButton ? 105 : COL_DEFS.actions.minWidth,
|
||||
maxWidth: showDepositButton ? 105 : COL_DEFS.actions.maxWidth,
|
||||
minWidth: showDepositButton ? 110 : COL_DEFS.actions.minWidth,
|
||||
maxWidth: showDepositButton ? 110 : COL_DEFS.actions.maxWidth,
|
||||
cellRenderer: ({
|
||||
value: assetId,
|
||||
node,
|
||||
|
||||
@@ -23,7 +23,9 @@ import { AccountType } from '@vegaprotocol/types';
|
||||
const defaultColDef = {
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
minWidth: 100,
|
||||
};
|
||||
|
||||
interface BreakdownTableProps extends AgGridReactProps {
|
||||
data: AccountFields[] | null;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
@@ -32,12 +34,13 @@ interface BreakdownTableProps extends AgGridReactProps {
|
||||
const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
({ data }, ref) => {
|
||||
const t = useT();
|
||||
const coldefs = useMemo(() => {
|
||||
const colDefs = useMemo(() => {
|
||||
const defs: ColDef[] = [
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
minWidth: 200,
|
||||
width: 90,
|
||||
pinned: true,
|
||||
sort: 'desc',
|
||||
cellRenderer: ({
|
||||
value,
|
||||
@@ -141,7 +144,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
|
||||
components={{ PriceCell, ProgressBarCell }}
|
||||
tooltipShowDelay={500}
|
||||
defaultColDef={defaultColDef}
|
||||
columnDefs={coldefs}
|
||||
columnDefs={colDefs}
|
||||
domLayout="autoHeight"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -15,7 +15,6 @@ export async function proposeMarket(publicKey: string) {
|
||||
log('sending proposal tx');
|
||||
const proposalTx = createNewMarketProposal();
|
||||
const result = await sendVegaTx(publicKey, proposalTx);
|
||||
|
||||
return result.result;
|
||||
}
|
||||
|
||||
@@ -119,6 +118,7 @@ function createNewMarketProposal(): ProposalSubmissionBody {
|
||||
timeWindow: '3600',
|
||||
scalingFactor: 10,
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
triggeringRatio: '0.7',
|
||||
auctionExtension: '1',
|
||||
},
|
||||
|
||||
@@ -9,4 +9,7 @@ export const COL_DEFS = {
|
||||
type: 'rightAligned',
|
||||
pinned: 'right' as const,
|
||||
},
|
||||
default: {
|
||||
minWidth: 100,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -329,8 +329,9 @@ export const DealTicket = ({
|
||||
|
||||
const marketTradingModeError = validateMarketTradingMode(
|
||||
marketTradingMode,
|
||||
t('Trading terminated')
|
||||
t('No trading')
|
||||
);
|
||||
|
||||
if (marketTradingModeError !== true) {
|
||||
return {
|
||||
message: marketTradingModeError,
|
||||
|
||||
@@ -11,9 +11,6 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
|
||||
positionDecimalPlaces: 1,
|
||||
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
state: Schema.MarketState.STATE_ACTIVE,
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
marketTimestamps: {
|
||||
__typename: 'MarketTimestamps',
|
||||
close: '',
|
||||
@@ -75,7 +72,6 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
|
||||
__typename: 'Instrument',
|
||||
},
|
||||
},
|
||||
|
||||
fees: {
|
||||
factors: {
|
||||
makerFee: '0.001',
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
isNumeric,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { type ColDef } from 'ag-grid-community';
|
||||
import { AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
|
||||
import {
|
||||
type VegaICellRendererParams,
|
||||
type VegaValueFormatterParams,
|
||||
@@ -21,7 +21,7 @@ export const DepositsTable = (
|
||||
) => {
|
||||
const columnDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{ headerName: 'Asset', field: 'asset.symbol' },
|
||||
{ headerName: 'Asset', field: 'asset.symbol', pinned: true },
|
||||
{
|
||||
headerName: 'Amount',
|
||||
field: 'amount',
|
||||
@@ -74,5 +74,11 @@ export const DepositsTable = (
|
||||
],
|
||||
[]
|
||||
);
|
||||
return <AgGrid columnDefs={columnDefs} {...props} />;
|
||||
return (
|
||||
<AgGrid
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,6 +48,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
cellRenderer: 'MarketNameCell',
|
||||
cellRendererParams: { idPath: 'market.id', onMarketClick },
|
||||
pinned: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Size'),
|
||||
@@ -143,6 +144,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
|
||||
<AgGrid
|
||||
ref={ref}
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
overlayNoRowsTemplate={t('No fills')}
|
||||
getRowId={({ data }) => data?.id}
|
||||
tooltipShowDelay={0}
|
||||
|
||||
@@ -54,9 +54,6 @@ export const generateFill = (override?: PartialDeep<Trade>) => {
|
||||
decimalPlaces: 5,
|
||||
state: MarketState.STATE_ACTIVE,
|
||||
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
fees: {
|
||||
__typename: 'Fees',
|
||||
factors: {
|
||||
|
||||
@@ -22,9 +22,7 @@ export const generateFundingPayment = (
|
||||
decimalPlaces: 5,
|
||||
state: MarketState.STATE_ACTIVE,
|
||||
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
|
||||
fees: {
|
||||
__typename: 'Fees',
|
||||
factors: {
|
||||
|
||||
@@ -116,7 +116,7 @@
|
||||
"Total fees": "Total fees",
|
||||
"Total margin available": "Total margin available",
|
||||
"TOTAL_MARGIN_AVAILABLE": "Total margin available = general {{assetSymbol}} balance ({{generalAccountBalance}} {{assetSymbol}}) + margin balance ({{marginAccountBalance}} {{assetSymbol}}) - maintenance level ({{marginMaintenance}} {{assetSymbol}}).",
|
||||
"Trading terminated": "Trading terminated",
|
||||
"No trading": "No trading",
|
||||
"Trailing percent offset cannot be higher than 99.9": "Trailing percent offset cannot be higher than 99.9",
|
||||
"Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}": "Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}",
|
||||
"Trailing percentage offset": "Trailing percentage offset",
|
||||
|
||||
@@ -124,27 +124,11 @@ describe('getChange', () => {
|
||||
});
|
||||
|
||||
describe('useCheckLiquidityStatus', () => {
|
||||
it('should return amber if liquidity is enough', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useCheckLiquidityStatus({
|
||||
suppliedStake: '60',
|
||||
targetStake: '100',
|
||||
triggeringRatio: '0.5',
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
status: Intent.Warning,
|
||||
percentage: new BigNumber('60'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should return red if liquidity is not enough', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useCheckLiquidityStatus({
|
||||
suppliedStake: '60',
|
||||
targetStake: '100',
|
||||
triggeringRatio: '1',
|
||||
})
|
||||
);
|
||||
|
||||
@@ -159,7 +143,6 @@ describe('useCheckLiquidityStatus', () => {
|
||||
useCheckLiquidityStatus({
|
||||
suppliedStake: '101',
|
||||
targetStake: '100',
|
||||
triggeringRatio: '1',
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -121,11 +121,9 @@ export const getTargetStake = (
|
||||
export const useCheckLiquidityStatus = ({
|
||||
suppliedStake,
|
||||
targetStake,
|
||||
triggeringRatio,
|
||||
}: {
|
||||
suppliedStake: string | number;
|
||||
targetStake: string | number;
|
||||
triggeringRatio: string | number;
|
||||
}): {
|
||||
status: Intent;
|
||||
percentage: BigNumber;
|
||||
@@ -142,23 +140,12 @@ export const useCheckLiquidityStatus = ({
|
||||
percentage,
|
||||
};
|
||||
}
|
||||
if (new BigNumber(suppliedStake).gte(new BigNumber(targetStake))) {
|
||||
if (new BigNumber(suppliedStake).gte(targetStake)) {
|
||||
// show a green status, e.g. "🟢 $13,666,999 liquidity supplied"
|
||||
return {
|
||||
status: Intent.Success,
|
||||
percentage,
|
||||
};
|
||||
// ELSE IF supplied_stake > NETPARAM[market.liquidity.targetstake.triggering.ratio] * target_stake THEN
|
||||
} else if (
|
||||
new BigNumber(suppliedStake).gte(
|
||||
new BigNumber(targetStake).multipliedBy(triggeringRatio)
|
||||
)
|
||||
) {
|
||||
// show an amber status, e.g. "🟠 $3,456,123 liquidity supplied"
|
||||
return {
|
||||
status: Intent.Warning,
|
||||
percentage,
|
||||
};
|
||||
// ELSE show a red status, e.g. "🔴 $600,002 liquidity supplied"
|
||||
} else {
|
||||
return {
|
||||
|
||||
+2
-5
File diff suppressed because one or more lines are too long
@@ -188,7 +188,6 @@ query MarketInfo($marketId: ID!) {
|
||||
long
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -745,7 +745,6 @@ export const LiquidityMonitoringParametersInfoPanel = ({
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => {
|
||||
const marketData = {
|
||||
triggeringRatio: market.liquidityMonitoringParameters.triggeringRatio,
|
||||
timeWindow:
|
||||
market.liquidityMonitoringParameters.targetStakeParameters.timeWindow,
|
||||
scalingFactor:
|
||||
@@ -754,8 +753,6 @@ export const LiquidityMonitoringParametersInfoPanel = ({
|
||||
|
||||
const parentMarketData = parentMarket
|
||||
? {
|
||||
triggeringRatio:
|
||||
parentMarket.liquidityMonitoringParameters.triggeringRatio,
|
||||
timeWindow:
|
||||
parentMarket.liquidityMonitoringParameters.targetStakeParameters
|
||||
.timeWindow,
|
||||
|
||||
@@ -95,7 +95,6 @@ export const marketInfoQuery = (
|
||||
long: '0.008508132993273576',
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '0.7',
|
||||
targetStakeParameters: {
|
||||
timeWindow: 3600,
|
||||
scalingFactor: 10,
|
||||
|
||||
@@ -101,8 +101,6 @@ export const useTooltipMapping: () => Record<string, ReactNode> = () => {
|
||||
auctionExtensionSecs: t(
|
||||
'Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.'
|
||||
),
|
||||
|
||||
triggeringRatio: t('The triggering ratio for entering liquidity auction.'),
|
||||
timeWindow: t('The length of time over which open interest is measured.'),
|
||||
scalingFactor: t(
|
||||
'The scaling between the liquidity demand estimate, based on open interest and target stake.'
|
||||
|
||||
@@ -35,9 +35,6 @@ fragment MarketFields on Market {
|
||||
open
|
||||
close
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
}
|
||||
}
|
||||
|
||||
query Markets {
|
||||
|
||||
@@ -53,9 +53,6 @@ export const createMarketFragment = (
|
||||
liquidityFee: '',
|
||||
},
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
id: '',
|
||||
|
||||
@@ -20,9 +20,6 @@ export const generateOrder = (partialOrder?: PartialDeep<Order>) => {
|
||||
makerFee: '0.1',
|
||||
},
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
marketTimestamps: {
|
||||
__typename: 'MarketTimestamps',
|
||||
close: '',
|
||||
|
||||
@@ -21,9 +21,6 @@ export const generateStopOrder = (
|
||||
__typename: 'Market',
|
||||
id: 'market-id',
|
||||
decimalPlaces: 1,
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '0.7',
|
||||
},
|
||||
fees: {
|
||||
__typename: 'Fees',
|
||||
factors: {
|
||||
|
||||
@@ -39,6 +39,7 @@ const defaultColDef = {
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterParams: { buttons: ['reset'] },
|
||||
minWidth: 100,
|
||||
};
|
||||
|
||||
export type OrderListTableProps = TypedDataAgGrid<Order> & {
|
||||
@@ -82,6 +83,9 @@ export const OrderListTable = memo<
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
cellRenderer: 'MarketNameCell',
|
||||
cellRendererParams: { idPath: 'market.id', onMarketClick },
|
||||
pinned: true,
|
||||
width: 130,
|
||||
resizable: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Filled'),
|
||||
|
||||
@@ -31,9 +31,6 @@ describe('OrderViewDialog', () => {
|
||||
liquidityFee: '0.001',
|
||||
},
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '1',
|
||||
},
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
|
||||
@@ -40,6 +40,7 @@ const defaultColDef = {
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filterParams: { buttons: ['reset'] },
|
||||
minWidth: 100,
|
||||
};
|
||||
|
||||
export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
|
||||
@@ -61,6 +62,7 @@ export const StopOrdersTable = memo(
|
||||
field: 'market.tradableInstrument.instrument.code',
|
||||
cellRenderer: 'MarketNameCell',
|
||||
cellRendererParams: { idPath: 'market.id', onMarketClick },
|
||||
pinned: true,
|
||||
},
|
||||
{
|
||||
headerName: t('Trigger'),
|
||||
|
||||
@@ -71,6 +71,7 @@ const defaultColDef = {
|
||||
filterParams: { buttons: ['reset'] },
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
resizable: true,
|
||||
minWidth: 110,
|
||||
};
|
||||
|
||||
export const PositionsTable = ({
|
||||
@@ -83,6 +84,330 @@ export const PositionsTable = ({
|
||||
...props
|
||||
}: Props) => {
|
||||
const t = useT();
|
||||
|
||||
const colDefs = useMemo<ColDef[]>(() => {
|
||||
const columnDefs: (ColDef | null)[] = [
|
||||
multipleKeys
|
||||
? {
|
||||
headerName: t('Vega key'),
|
||||
field: 'partyId',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
|
||||
(data?.partyId &&
|
||||
pubKeys &&
|
||||
pubKeys.find((key) => key.publicKey === data.partyId)?.name) ||
|
||||
data?.partyId,
|
||||
}
|
||||
: null,
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'marketCode',
|
||||
resizable: true,
|
||||
onCellClicked: ({ data }) => {
|
||||
if (!onMarketClick) return;
|
||||
onMarketClick(data.marketId);
|
||||
},
|
||||
pinned: true,
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'marketCode'>) => {
|
||||
if (!data || !value) return '-';
|
||||
return (
|
||||
<StackedCell
|
||||
primary={value}
|
||||
secondary={
|
||||
<>
|
||||
{data?.assetSymbol}
|
||||
<MarketProductPill productType={data.productType} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Size / Notional'),
|
||||
field: 'openVolume',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: { data: Position }) => {
|
||||
return data?.openVolume === undefined
|
||||
? undefined
|
||||
: toBigNum(data?.openVolume, data.positionDecimalPlaces).toNumber();
|
||||
},
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
|
||||
if (!data?.openVolume) return '-';
|
||||
|
||||
const vol = volumePrefix(
|
||||
addDecimalsFormatNumber(data.openVolume, data.positionDecimalPlaces)
|
||||
);
|
||||
|
||||
return vol;
|
||||
},
|
||||
cellRenderer: OpenVolumeCell,
|
||||
},
|
||||
{
|
||||
headerName: t('Entry / Mark'),
|
||||
field: 'markPrice',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'markPrice'>) => {
|
||||
if (
|
||||
!data?.averageEntryPrice ||
|
||||
!data?.markPrice ||
|
||||
!data?.marketDecimalPlaces
|
||||
) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
if (
|
||||
data.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
const entry = addDecimalsFormatNumber(
|
||||
data.averageEntryPrice,
|
||||
data.marketDecimalPlaces
|
||||
);
|
||||
const mark = addDecimalsFormatNumber(
|
||||
data.markPrice,
|
||||
data.marketDecimalPlaces
|
||||
);
|
||||
return (
|
||||
<StackedCell
|
||||
primary={entry}
|
||||
secondary={
|
||||
<PriceFlashCell
|
||||
value={Number(data.markPrice)}
|
||||
valueFormatted={mark}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
|
||||
return !data ||
|
||||
!data.markPrice ||
|
||||
data.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
? undefined
|
||||
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Margin / Leverage'),
|
||||
colId: 'margin',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
|
||||
return !data
|
||||
? undefined
|
||||
: toBigNum(
|
||||
data.marginAccountBalance,
|
||||
data.assetDecimals
|
||||
).toNumber();
|
||||
},
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
if (
|
||||
!data ||
|
||||
!data.marginAccountBalance ||
|
||||
!data.marketDecimalPlaces
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const margin = addDecimalsFormatNumberQuantum(
|
||||
data.marginAccountBalance,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
);
|
||||
|
||||
const lev = data?.currentLeverage ? data.currentLeverage : 1;
|
||||
const leverage = formatNumber(Math.max(1, lev), 1);
|
||||
return <StackedCell primary={margin} secondary={leverage + 'x'} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'liquidationPrice',
|
||||
headerName: 'Liquidation',
|
||||
headerTooltip: t('Worst case liquidation price'),
|
||||
cellClass: 'font-mono text-right',
|
||||
type: 'rightAligned',
|
||||
// Cannot be sortable as data is fetched within the cell
|
||||
sortable: false,
|
||||
filter: false,
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
if (!data) {
|
||||
return '-';
|
||||
}
|
||||
return (
|
||||
<div className="flex h-[45px] items-center">
|
||||
<LiquidationPrice
|
||||
className="block text-right grow"
|
||||
marketId={data.marketId}
|
||||
openVolume={data.openVolume}
|
||||
collateralAvailable={data.totalBalance}
|
||||
decimalPlaces={data.marketDecimalPlaces}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Realised PNL'),
|
||||
field: 'realisedPNL',
|
||||
type: 'rightAligned',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: realisedPNLValueGetter,
|
||||
cellRenderer: (
|
||||
args: VegaICellRendererParams<Position, 'realisedPNL'>
|
||||
) => {
|
||||
const LOSS_SOCIALIZATION_LINK = DocsLinks?.LOSS_SOCIALIZATION ?? '';
|
||||
|
||||
if (!args.data || args.value === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const losses = parseInt(args.data?.lossSocializationAmount ?? '0');
|
||||
|
||||
if (losses <= 0) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return (
|
||||
<Tooltip description={args.valueFormatted} align="end">
|
||||
<div>
|
||||
<PNLCell {...args} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
const lossesFormatted = addDecimalsFormatNumber(
|
||||
args.data.lossSocializationAmount,
|
||||
args.data.assetDecimals
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
align="end"
|
||||
description={
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t('Realised PNL: {{value}}', {
|
||||
nsSeparator: '*',
|
||||
replace: { value: args.value },
|
||||
})}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t('Lifetime loss socialisation deductions: {{losses}}', {
|
||||
nsSeparator: '*',
|
||||
replace: {
|
||||
losses: lossesFormatted,
|
||||
},
|
||||
})}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
`You received less {{assetSymbol}} in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
|
||||
{ assetSymbol: args.data.assetSymbol }
|
||||
)}
|
||||
</p>
|
||||
{LOSS_SOCIALIZATION_LINK && (
|
||||
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
|
||||
{t('Read more about loss socialisation')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<PNLCell {...args} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
|
||||
return !data
|
||||
? ''
|
||||
: addDecimalsFormatNumberQuantum(
|
||||
data.realisedPNL,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
);
|
||||
},
|
||||
headerTooltip: t(
|
||||
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Unrealised PNL'),
|
||||
field: 'unrealisedPNL',
|
||||
type: 'rightAligned',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: unrealisedPNLValueGetter,
|
||||
// @ts-ignore no type overlap but function can be identical
|
||||
tooltipValueGetter: unrealisedPNLValueGetter,
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
|
||||
!data
|
||||
? ''
|
||||
: addDecimalsFormatNumberQuantum(
|
||||
data.unrealisedPNL,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
),
|
||||
headerTooltip: t(
|
||||
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
|
||||
),
|
||||
},
|
||||
onClose && !isReadOnly
|
||||
? {
|
||||
...COL_DEFS.actions,
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
{data?.openVolume &&
|
||||
data?.openVolume !== '0' &&
|
||||
data.partyId === pubKey ? (
|
||||
<ButtonLink
|
||||
data-testid="close-position"
|
||||
onClick={() => data && onClose(data)}
|
||||
title={t('Close position')}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.CROSS} size={16} />
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
{data?.assetId && (
|
||||
<PositionActionsDropdown assetId={data?.assetId} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
minWidth: 55,
|
||||
maxWidth: 55,
|
||||
}
|
||||
: null,
|
||||
];
|
||||
return columnDefs.filter<ColDef>(
|
||||
(colDef: ColDef | null): colDef is ColDef => colDef !== null
|
||||
);
|
||||
}, [isReadOnly, multipleKeys, onClose, onMarketClick, pubKey, pubKeys, t]);
|
||||
|
||||
return (
|
||||
<AgGrid
|
||||
overlayNoRowsTemplate={t('No positions')}
|
||||
@@ -95,349 +420,7 @@ export const PositionsTable = ({
|
||||
MarketNameCell,
|
||||
}}
|
||||
rowHeight={45}
|
||||
columnDefs={useMemo<ColDef[]>(() => {
|
||||
const columnDefs: (ColDef | null)[] = [
|
||||
multipleKeys
|
||||
? {
|
||||
headerName: t('Vega key'),
|
||||
field: 'partyId',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
|
||||
(data?.partyId &&
|
||||
pubKeys &&
|
||||
pubKeys.find((key) => key.publicKey === data.partyId)
|
||||
?.name) ||
|
||||
data?.partyId,
|
||||
}
|
||||
: null,
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'marketCode',
|
||||
onCellClicked: ({ data }) => {
|
||||
if (!onMarketClick) return;
|
||||
onMarketClick(data.marketId);
|
||||
},
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'marketCode'>) => {
|
||||
if (!data || !value) return '-';
|
||||
return (
|
||||
<StackedCell
|
||||
primary={value}
|
||||
secondary={
|
||||
<>
|
||||
{data?.assetSymbol}
|
||||
<MarketProductPill productType={data.productType} />
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Size / Notional'),
|
||||
field: 'openVolume',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: { data: Position }) => {
|
||||
return data?.openVolume === undefined
|
||||
? undefined
|
||||
: toBigNum(
|
||||
data?.openVolume,
|
||||
data.positionDecimalPlaces
|
||||
).toNumber();
|
||||
},
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
|
||||
if (!data?.openVolume) return '-';
|
||||
|
||||
const vol = volumePrefix(
|
||||
addDecimalsFormatNumber(
|
||||
data.openVolume,
|
||||
data.positionDecimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
return vol;
|
||||
},
|
||||
cellRenderer: OpenVolumeCell,
|
||||
},
|
||||
{
|
||||
headerName: t('Entry / Mark'),
|
||||
field: 'markPrice',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<Position, 'markPrice'>) => {
|
||||
if (
|
||||
!data?.averageEntryPrice ||
|
||||
!data?.markPrice ||
|
||||
!data?.marketDecimalPlaces
|
||||
) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
if (
|
||||
data.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
) {
|
||||
return <>-</>;
|
||||
}
|
||||
|
||||
const entry = addDecimalsFormatNumber(
|
||||
data.averageEntryPrice,
|
||||
data.marketDecimalPlaces
|
||||
);
|
||||
const mark = addDecimalsFormatNumber(
|
||||
data.markPrice,
|
||||
data.marketDecimalPlaces
|
||||
);
|
||||
return (
|
||||
<StackedCell
|
||||
primary={entry}
|
||||
secondary={
|
||||
<PriceFlashCell
|
||||
value={Number(data.markPrice)}
|
||||
valueFormatted={mark}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
|
||||
return !data ||
|
||||
!data.markPrice ||
|
||||
data.marketTradingMode ===
|
||||
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
|
||||
? undefined
|
||||
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Margin / Leverage'),
|
||||
colId: 'margin',
|
||||
type: 'rightAligned',
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
|
||||
return !data
|
||||
? undefined
|
||||
: toBigNum(
|
||||
data.marginAccountBalance,
|
||||
data.assetDecimals
|
||||
).toNumber();
|
||||
},
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
if (
|
||||
!data ||
|
||||
!data.marginAccountBalance ||
|
||||
!data.marketDecimalPlaces
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const margin = addDecimalsFormatNumberQuantum(
|
||||
data.marginAccountBalance,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
);
|
||||
|
||||
const lev = data?.currentLeverage ? data.currentLeverage : 1;
|
||||
const leverage = formatNumber(Math.max(1, lev), 1);
|
||||
return (
|
||||
<StackedCell primary={margin} secondary={leverage + 'x'} />
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'liquidationPrice',
|
||||
headerName: 'Liquidation',
|
||||
headerTooltip: t('Worst case liquidation price'),
|
||||
cellClass: 'font-mono text-right',
|
||||
type: 'rightAligned',
|
||||
// Cannot be sortable as data is fetched within the cell
|
||||
sortable: false,
|
||||
filter: false,
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
if (!data) {
|
||||
return '-';
|
||||
}
|
||||
return (
|
||||
<div className="flex h-[45px] items-center">
|
||||
<LiquidationPrice
|
||||
className="block text-right grow"
|
||||
marketId={data.marketId}
|
||||
openVolume={data.openVolume}
|
||||
collateralAvailable={data.totalBalance}
|
||||
decimalPlaces={data.marketDecimalPlaces}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Realised PNL'),
|
||||
field: 'realisedPNL',
|
||||
type: 'rightAligned',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: realisedPNLValueGetter,
|
||||
cellRenderer: (
|
||||
args: VegaICellRendererParams<Position, 'realisedPNL'>
|
||||
) => {
|
||||
const LOSS_SOCIALIZATION_LINK =
|
||||
DocsLinks?.LOSS_SOCIALIZATION ?? '';
|
||||
|
||||
if (!args.data || args.value === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const losses = parseInt(
|
||||
args.data?.lossSocializationAmount ?? '0'
|
||||
);
|
||||
|
||||
if (losses <= 0) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return (
|
||||
<Tooltip description={args.valueFormatted} align="end">
|
||||
<div>
|
||||
<PNLCell {...args} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
const lossesFormatted = addDecimalsFormatNumber(
|
||||
args.data.lossSocializationAmount,
|
||||
args.data.assetDecimals
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
align="end"
|
||||
description={
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t('Realised PNL: {{value}}', {
|
||||
nsSeparator: '*',
|
||||
replace: { value: args.value },
|
||||
})}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
'Lifetime loss socialisation deductions: {{losses}}',
|
||||
{
|
||||
nsSeparator: '*',
|
||||
replace: {
|
||||
losses: lossesFormatted,
|
||||
},
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
{t(
|
||||
`You received less {{assetSymbol}} in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
|
||||
{ assetSymbol: args.data.assetSymbol }
|
||||
)}
|
||||
</p>
|
||||
{LOSS_SOCIALIZATION_LINK && (
|
||||
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
|
||||
{t('Read more about loss socialisation')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<PNLCell {...args} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
|
||||
return !data
|
||||
? ''
|
||||
: addDecimalsFormatNumberQuantum(
|
||||
data.realisedPNL,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
);
|
||||
},
|
||||
headerTooltip: t(
|
||||
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Unrealised PNL'),
|
||||
field: 'unrealisedPNL',
|
||||
type: 'rightAligned',
|
||||
cellClassRules: signedNumberCssClassRules,
|
||||
cellClass: 'font-mono text-right',
|
||||
filter: 'agNumberColumnFilter',
|
||||
valueGetter: unrealisedPNLValueGetter,
|
||||
// @ts-ignore no type overlap but function can be identical
|
||||
tooltipValueGetter: unrealisedPNLValueGetter,
|
||||
valueFormatter: ({
|
||||
data,
|
||||
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
|
||||
!data
|
||||
? ''
|
||||
: addDecimalsFormatNumberQuantum(
|
||||
data.unrealisedPNL,
|
||||
data.assetDecimals,
|
||||
data.quantum
|
||||
),
|
||||
headerTooltip: t(
|
||||
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
|
||||
),
|
||||
},
|
||||
onClose && !isReadOnly
|
||||
? {
|
||||
...COL_DEFS.actions,
|
||||
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
|
||||
return (
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
{data?.openVolume &&
|
||||
data?.openVolume !== '0' &&
|
||||
data.partyId === pubKey ? (
|
||||
<ButtonLink
|
||||
data-testid="close-position"
|
||||
onClick={() => data && onClose(data)}
|
||||
title={t('Close position')}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.CROSS} size={16} />
|
||||
</ButtonLink>
|
||||
) : null}
|
||||
{data?.assetId && (
|
||||
<PositionActionsDropdown assetId={data?.assetId} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
minWidth: 55,
|
||||
maxWidth: 55,
|
||||
}
|
||||
: null,
|
||||
];
|
||||
return columnDefs.filter<ColDef>(
|
||||
(colDef: ColDef | null): colDef is ColDef => colDef !== null
|
||||
);
|
||||
}, [
|
||||
isReadOnly,
|
||||
multipleKeys,
|
||||
onClose,
|
||||
onMarketClick,
|
||||
pubKey,
|
||||
pubKeys,
|
||||
t,
|
||||
])}
|
||||
columnDefs={colDefs}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -29,6 +29,7 @@ export const useColumnDefs = () => {
|
||||
colId: 'market',
|
||||
headerName: t('Market'),
|
||||
field: 'terms.change.instrument.code',
|
||||
pinned: true,
|
||||
cellStyle: { lineHeight: '14px' },
|
||||
cellRenderer: ({
|
||||
value,
|
||||
|
||||
@@ -134,7 +134,6 @@ fragment NewMarketFields on NewMarket {
|
||||
# timeWindow
|
||||
# scalingFactor
|
||||
# }
|
||||
# triggeringRatio
|
||||
# auctionExtensionSecs
|
||||
# }
|
||||
# linearSlippageFactor
|
||||
@@ -299,7 +298,6 @@ fragment UpdateMarketFields on UpdateMarket {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
triggeringRatio
|
||||
# auctionExtensionSecs
|
||||
}
|
||||
riskParameters {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -92,7 +92,6 @@ export const marketUpdateProposal: ProposalListFieldsFragment = {
|
||||
triggers: [],
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '0',
|
||||
targetStakeParameters: {
|
||||
scalingFactor: 0,
|
||||
timeWindow: 0,
|
||||
|
||||
@@ -162,7 +162,6 @@ const generateUpdateMarketProposal = (
|
||||
__typename: liquidityMonitoring
|
||||
? 'LiquidityMonitoringParameters'
|
||||
: undefined,
|
||||
triggeringRatio: '0',
|
||||
targetStakeParameters: {
|
||||
__typename: undefined,
|
||||
scalingFactor: 0,
|
||||
|
||||
Generated
+2
@@ -5585,6 +5585,8 @@ export enum StopOrderRejectionReason {
|
||||
REJECTION_REASON_MAX_STOP_ORDERS_PER_PARTY_REACHED = 'REJECTION_REASON_MAX_STOP_ORDERS_PER_PARTY_REACHED',
|
||||
/** Stop orders submission must be reduce only */
|
||||
REJECTION_REASON_MUST_BE_REDUCE_ONLY = 'REJECTION_REASON_MUST_BE_REDUCE_ONLY',
|
||||
/** Stop orders are not allowed during the opening auction */
|
||||
REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION = 'REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION',
|
||||
/** Stop orders are not allowed without a position */
|
||||
REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_WITHOUT_A_POSITION = 'REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_WITHOUT_A_POSITION',
|
||||
/** This stop order does not close the position */
|
||||
|
||||
@@ -85,19 +85,21 @@ export function Dialog({
|
||||
<VegaIcon name={VegaIconNames.CROSS} size={24} />
|
||||
</DialogPrimitives.Close>
|
||||
)}
|
||||
<div className="flex gap-4 max-w-full">
|
||||
{icon && <div className="fill-current">{icon}</div>}
|
||||
<div data-testid="dialog-content" className="flex-1 max-w-full">
|
||||
{title && (
|
||||
<h1
|
||||
className="text-xl uppercase mb-4 pr-2"
|
||||
data-testid="dialog-title"
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
)}
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
<div data-testid="dialog-content" className="flex-1 max-w-full">
|
||||
{title && (
|
||||
<span
|
||||
className="text-xl uppercase flex gap-4"
|
||||
data-testid="dialog-title"
|
||||
>
|
||||
{icon && (
|
||||
<span className="fill-current flex items-center">
|
||||
{icon}
|
||||
</span>
|
||||
)}
|
||||
{title}
|
||||
</span>
|
||||
)}
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,15 +5,16 @@ import { ToastPosition, useToastsConfiguration, useToasts } from './use-toasts';
|
||||
import { useCallback } from 'react';
|
||||
import { Intent } from '../../utils/intent';
|
||||
import { useT } from '../../use-t';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const ToastPositionSetter = () => {
|
||||
const t = useT();
|
||||
const setPostion = useToastsConfiguration((store) => store.setPosition);
|
||||
const setPosition = useToastsConfiguration((store) => store.setPosition);
|
||||
const position = useToastsConfiguration((store) => store.position);
|
||||
const setToast = useToasts((store) => store.setToast);
|
||||
const handleChange = useCallback(
|
||||
(position: ToastPosition) => {
|
||||
setPostion(position);
|
||||
setPosition(position);
|
||||
setToast({
|
||||
id: 'test-toast',
|
||||
intent: Intent.Primary,
|
||||
@@ -21,11 +22,49 @@ export const ToastPositionSetter = () => {
|
||||
onClose: () => useToasts.getState().remove('test-toast'),
|
||||
});
|
||||
},
|
||||
[setToast, setPostion, t]
|
||||
[setToast, setPosition, t]
|
||||
);
|
||||
const buttonCssClasses =
|
||||
'flex items-center px-1 py-1 relative rounded bg-vega-clight-400 dark:bg-vega-cdark-400';
|
||||
const activeIcon = 'fill-vega-clight-900 dark:fill-vega-cdark-900';
|
||||
|
||||
const { screenSize } = useScreenDimensions();
|
||||
|
||||
const isMobileScreen = screenSize === 'xs';
|
||||
|
||||
if (isMobileScreen) {
|
||||
return (
|
||||
<div className="flex justify-between">
|
||||
<div className={classNames('grid grid-cols-1 grid-rows-2 gap-1')}>
|
||||
<button
|
||||
className={buttonCssClasses}
|
||||
onClick={() => handleChange(ToastPosition.TopCenter)}
|
||||
>
|
||||
<Icon
|
||||
className={classNames(
|
||||
position === ToastPosition.TopCenter && activeIcon
|
||||
)}
|
||||
size={3}
|
||||
name={IconNames.ARROW_UP}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className={buttonCssClasses}
|
||||
onClick={() => handleChange(ToastPosition.BottomCenter)}
|
||||
>
|
||||
<Icon
|
||||
className={classNames(
|
||||
position === ToastPosition.BottomCenter && activeIcon
|
||||
)}
|
||||
size={3}
|
||||
name={IconNames.ARROW_DOWN}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-between">
|
||||
<div className={classNames('grid grid-cols-3 grid-rows-2 gap-1')}>
|
||||
|
||||
@@ -92,7 +92,7 @@ export const ToastsContainer = ({
|
||||
className={classNames(
|
||||
'absolute right-0 top-[-38px] z-20 w-full',
|
||||
'transition-opacity',
|
||||
'opacity-0 hover:!opacity-100 group-hover:opacity-50',
|
||||
'sm:opacity-0 sm:hover:!opacity-100 sm:group-hover:opacity-50',
|
||||
{
|
||||
hidden: validToasts.length === 0,
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const mockUpdateDialogOpen = jest.fn();
|
||||
const mockCloseVegaDialog = jest.fn();
|
||||
|
||||
let mockIsDesktopRunning = true;
|
||||
const mockChainId = 'VEGA_CHAIN_ID';
|
||||
const mockChainId = 'chain-id';
|
||||
|
||||
jest.mock('../use-is-wallet-service-running', () => ({
|
||||
useIsWalletServiceRunning: jest
|
||||
@@ -30,6 +30,10 @@ jest.mock('../use-is-wallet-service-running', () => ({
|
||||
.mockImplementation(() => mockIsDesktopRunning),
|
||||
}));
|
||||
|
||||
jest.mock('./use-chain-id', () => ({
|
||||
useChainId: jest.fn().mockImplementation(() => mockChainId),
|
||||
}));
|
||||
|
||||
let defaultProps: VegaConnectDialogProps;
|
||||
|
||||
const INITIAL_KEY = 'some-key';
|
||||
@@ -67,7 +71,6 @@ const defaultConfig: VegaWalletConfig = {
|
||||
chromeExtensionUrl: 'chrome-link',
|
||||
mozillaExtensionUrl: 'mozilla-link',
|
||||
},
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
|
||||
function generateJSX(
|
||||
@@ -206,12 +209,7 @@ describe('VegaConnectDialog', () => {
|
||||
.mockClear()
|
||||
.mockImplementation(() =>
|
||||
delayedReject(
|
||||
new WalletError(
|
||||
'User error',
|
||||
3001,
|
||||
'The user rejected the request'
|
||||
),
|
||||
delay
|
||||
new WalletError('User error', 3001, 'The user rejected the request')
|
||||
)
|
||||
);
|
||||
|
||||
@@ -316,6 +314,13 @@ describe('VegaConnectDialog', () => {
|
||||
render(generateJSX());
|
||||
await selectInjected();
|
||||
|
||||
// Chain check
|
||||
expect(screen.getByText('Verifying chain')).toBeInTheDocument();
|
||||
expect(vegaWindow.getChainId).toHaveBeenCalled();
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(delay);
|
||||
});
|
||||
|
||||
// Await user connect
|
||||
expect(screen.getByText('Connecting...')).toBeInTheDocument();
|
||||
expect(vegaWindow.connectWallet).toHaveBeenCalled();
|
||||
@@ -336,6 +341,43 @@ describe('VegaConnectDialog', () => {
|
||||
expect(mockCloseVegaDialog).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('handles invalid chain', async () => {
|
||||
const delay = 100;
|
||||
const invalidChain = 'invalid chain';
|
||||
const vegaWindow = {
|
||||
getChainId: jest.fn(() =>
|
||||
delayedResolve({ chainID: invalidChain }, delay)
|
||||
),
|
||||
connectWallet: jest.fn(() => delayedResolve(null, delay)),
|
||||
disconnectWallet: jest.fn(() => delayedResolve(undefined, delay)),
|
||||
listKeys: jest.fn(() =>
|
||||
delayedResolve(
|
||||
{
|
||||
keys: [{ name: 'test key', publicKey: '0x123' }],
|
||||
},
|
||||
100
|
||||
)
|
||||
),
|
||||
};
|
||||
mockBrowserWallet(vegaWindow);
|
||||
render(generateJSX());
|
||||
await selectInjected();
|
||||
|
||||
// Chain check
|
||||
expect(screen.getByText('Verifying chain')).toBeInTheDocument();
|
||||
expect(vegaWindow.getChainId).toHaveBeenCalled();
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(delay);
|
||||
});
|
||||
|
||||
expect(screen.getByText('Wrong network')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
new RegExp(`set your wallet network in your app to "${mockChainId}"`)
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
async function selectInjected() {
|
||||
expect(await screen.findByRole('dialog')).toBeInTheDocument();
|
||||
fireEvent.click(await screen.findByTestId('connector-injected'));
|
||||
|
||||
@@ -44,6 +44,7 @@ import { isBrowserWalletInstalled } from '../utils';
|
||||
import { useIsWalletServiceRunning } from '../use-is-wallet-service-running';
|
||||
import { SnapStatus, useSnapStatus } from '../use-snap-status';
|
||||
import { useVegaWalletDialogStore } from './vega-wallet-dialog-store';
|
||||
import { useChainId } from './use-chain-id';
|
||||
import { useT } from '../use-t';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
@@ -64,7 +65,7 @@ export const VegaConnectDialog = ({
|
||||
contentOnly,
|
||||
onClose,
|
||||
}: VegaConnectDialogProps) => {
|
||||
const { chainId, disconnect, acknowledgeNeeded } = useVegaWallet();
|
||||
const { disconnect, acknowledgeNeeded } = useVegaWallet();
|
||||
const vegaWalletDialogOpen = useVegaWalletDialogStore(
|
||||
(store) => store.vegaWalletDialogOpen
|
||||
);
|
||||
@@ -84,6 +85,10 @@ export const VegaConnectDialog = ({
|
||||
[updateVegaWalletDialog, acknowledgeNeeded, disconnect]
|
||||
);
|
||||
|
||||
// Ensure we have a chain Id so we can compare with wallet chain id.
|
||||
// This value will already be in the cache, if it failed the app wont render
|
||||
const chainId = useChainId();
|
||||
|
||||
const content = chainId && (
|
||||
<ConnectDialogContainer
|
||||
connectors={connectors}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useVegaWallet } from '../use-vega-wallet';
|
||||
import { useChainId } from './use-chain-id';
|
||||
|
||||
global.fetch = jest.fn();
|
||||
const mockFetch = global.fetch as jest.Mock;
|
||||
mockFetch.mockImplementation((url: string) => {
|
||||
return Promise.resolve({ ok: true });
|
||||
});
|
||||
|
||||
jest.mock('../use-vega-wallet', () => {
|
||||
const original = jest.requireActual('../use-vega-wallet');
|
||||
return {
|
||||
...original,
|
||||
useVegaWallet: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('useChainId', () => {
|
||||
it('does not call fetch when statistics url could not be determined', async () => {
|
||||
(useVegaWallet as jest.Mock).mockReturnValue({
|
||||
vegaUrl: '',
|
||||
});
|
||||
renderHook(() => useChainId());
|
||||
await waitFor(() => {
|
||||
expect(mockFetch).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls fetch with correct statistics url', async () => {
|
||||
(useVegaWallet as jest.Mock).mockReturnValue({
|
||||
vegaUrl: 'http://localhost:1234/graphql',
|
||||
});
|
||||
renderHook(() => useChainId());
|
||||
await waitFor(() => {
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
'http://localhost:1234/statistics'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not return chain id when chain id is not present in response', async () => {
|
||||
(useVegaWallet as jest.Mock).mockReturnValue({
|
||||
vegaUrl: 'http://localhost:1234/graphql',
|
||||
});
|
||||
const { result } = renderHook(() => useChainId());
|
||||
await waitFor(() => {
|
||||
expect(result.current).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns chain id when chain id is present in response', async () => {
|
||||
(useVegaWallet as jest.Mock).mockReturnValue({
|
||||
vegaUrl: 'http://localhost:1234/graphql',
|
||||
});
|
||||
mockFetch.mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => ({
|
||||
statistics: {
|
||||
chainId: '1234',
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
const { result } = renderHook(() => useChainId());
|
||||
await waitFor(() => {
|
||||
expect(result.current).not.toBeUndefined();
|
||||
expect(result.current).toEqual('1234');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const MAX_FETCH_ATTEMPTS = 3;
|
||||
import { useVegaWallet } from '../use-vega-wallet';
|
||||
|
||||
const cache: Record<string, string> = {};
|
||||
|
||||
@@ -19,17 +18,16 @@ const getNodeStatisticsUrl = (vegaUrl: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const useChainId = (vegaUrl: string | undefined) => {
|
||||
const [chainId, setChainId] = useState<undefined | string>(
|
||||
vegaUrl ? cache[vegaUrl] : undefined
|
||||
);
|
||||
const [fetchAttempts, setFetchAttempts] = useState(1);
|
||||
export const useChainId = () => {
|
||||
const { vegaUrl } = useVegaWallet();
|
||||
const [chainId, setChainId] = useState<undefined | string>(cache[vegaUrl]);
|
||||
const [fetchAttempt, setFetchAttempt] = useState(1);
|
||||
|
||||
const statisticsUrl = vegaUrl ? getNodeStatisticsUrl(vegaUrl) : undefined;
|
||||
const statisticsUrl = getNodeStatisticsUrl(vegaUrl);
|
||||
|
||||
useEffect(() => {
|
||||
// abort when `/statistics` URL could not be determined
|
||||
if (!statisticsUrl || !vegaUrl) return;
|
||||
if (!statisticsUrl) return;
|
||||
let isCancelled = false;
|
||||
if (cache[vegaUrl]) {
|
||||
setChainId(cache[vegaUrl]);
|
||||
@@ -44,19 +42,16 @@ export const useChainId = (vegaUrl: string | undefined) => {
|
||||
if (!response?.statistics?.chainId) {
|
||||
throw new Error('statistics.chainId not present in fetched response');
|
||||
}
|
||||
|
||||
const chainId = response.statistics.chainId;
|
||||
cache[vegaUrl] = chainId;
|
||||
setChainId(chainId);
|
||||
setChainId(response?.statistics?.chainId);
|
||||
})
|
||||
.catch(() => {
|
||||
if (fetchAttempts < MAX_FETCH_ATTEMPTS) {
|
||||
setFetchAttempts((value) => (value += 1));
|
||||
if (fetchAttempt < 3) {
|
||||
setFetchAttempt((value) => (value += 1));
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
isCancelled = true;
|
||||
};
|
||||
}, [fetchAttempts, statisticsUrl, vegaUrl]);
|
||||
}, [fetchAttempt, statisticsUrl, vegaUrl]);
|
||||
return chainId;
|
||||
};
|
||||
@@ -1,11 +1,10 @@
|
||||
import { clearConfig, setConfig } from '../storage';
|
||||
import type { Transaction, VegaConnector } from './vega-connector';
|
||||
|
||||
type VegaWalletEvent = 'client.disconnected';
|
||||
|
||||
declare global {
|
||||
interface Vega {
|
||||
connectWallet: (args: { chainId: string }) => Promise<null>;
|
||||
getChainId: () => Promise<{ chainID: string }>;
|
||||
connectWallet: () => Promise<null>;
|
||||
disconnectWallet: () => Promise<void>;
|
||||
listKeys: () => Promise<{
|
||||
keys: Array<{ name: string; publicKey: string }>;
|
||||
@@ -35,9 +34,6 @@ declare global {
|
||||
};
|
||||
transactionHash: string;
|
||||
}>;
|
||||
|
||||
on: (event: VegaWalletEvent, callback: () => void) => void;
|
||||
isConnected?: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
interface Window {
|
||||
@@ -51,60 +47,15 @@ export const InjectedConnectorErrors = {
|
||||
INVALID_CHAIN: new Error('Invalid chain'),
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const wait = (ms: number) =>
|
||||
new Promise<boolean>((_, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(false);
|
||||
}, ms);
|
||||
});
|
||||
|
||||
const INJECTED_CONNECTOR_TIMEOUT = 1000;
|
||||
|
||||
export class InjectedConnector implements VegaConnector {
|
||||
isConnected = false;
|
||||
chainId: string | null = null;
|
||||
description = 'Connects using the Vega wallet browser extension';
|
||||
alive: ReturnType<typeof setInterval> | undefined = undefined;
|
||||
|
||||
async connectWallet(chainId: string) {
|
||||
this.chainId = chainId;
|
||||
try {
|
||||
await window.vega.connectWallet({ chainId });
|
||||
this.isConnected = true;
|
||||
window.vega.on('client.disconnected', () => {
|
||||
this.isConnected = false;
|
||||
});
|
||||
async getChainId() {
|
||||
return window.vega.getChainId();
|
||||
}
|
||||
|
||||
this.alive = setInterval(async () => {
|
||||
try {
|
||||
const connected = await Promise.race([
|
||||
// FIXME: All of the `window.vega` initiated promises are `pending`
|
||||
// while waiting for the user action when transaction is sent.
|
||||
// (Probably due to the FIFO queue of the `PortServer`?)
|
||||
// Because of that we cannot `wait` here as while waiting for the
|
||||
// user action in wallet this will `reject`. It'd be cool if the
|
||||
// `window.vega` was not blocking the api calls.
|
||||
|
||||
// wait(INJECTED_CONNECTOR_TIMEOUT),
|
||||
|
||||
// `isConnected` is only available in the newer versions
|
||||
// of the browser wallet
|
||||
'isConnected' in window.vega &&
|
||||
typeof window.vega.isConnected === 'function'
|
||||
? window.vega.isConnected()
|
||||
: window.vega.listKeys(),
|
||||
]);
|
||||
this.isConnected = Boolean(connected);
|
||||
} catch {
|
||||
this.isConnected = false;
|
||||
}
|
||||
}, INJECTED_CONNECTOR_TIMEOUT * 2);
|
||||
} catch {
|
||||
throw new Error(
|
||||
`could not connect to the vega wallet on chain: ${chainId}`
|
||||
);
|
||||
}
|
||||
connectWallet() {
|
||||
return window.vega.connectWallet();
|
||||
}
|
||||
|
||||
async connect() {
|
||||
@@ -118,11 +69,19 @@ export class InjectedConnector implements VegaConnector {
|
||||
}
|
||||
|
||||
async isAlive() {
|
||||
return this.isConnected;
|
||||
try {
|
||||
const keys = await window.vega.listKeys();
|
||||
if (keys.keys.length > 0) {
|
||||
return true;
|
||||
}
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
clearInterval(this.alive);
|
||||
clearConfig();
|
||||
return window.vega.disconnectWallet();
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ interface ProposalNewMarketTerms {
|
||||
timeWindow: string;
|
||||
scalingFactor: number;
|
||||
};
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
triggeringRatio: string;
|
||||
auctionExtension: string;
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ export class ViewConnector implements VegaConnector {
|
||||
});
|
||||
return Promise.resolve([
|
||||
{
|
||||
name: 'View only pubkey',
|
||||
name: 'View only',
|
||||
publicKey: this.pubkey,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -13,9 +13,6 @@ export interface VegaWalletContextShape {
|
||||
/** Url of current connected node */
|
||||
vegaUrl: string;
|
||||
|
||||
/** Vega chain id */
|
||||
chainId: string;
|
||||
|
||||
/** Url of running wallet service */
|
||||
vegaWalletServiceUrl: string;
|
||||
|
||||
|
||||
@@ -7,4 +7,3 @@ export * from './provider';
|
||||
export * from './connect-dialog';
|
||||
export * from './utils';
|
||||
export * from './storage';
|
||||
export * from './use-chain-id';
|
||||
|
||||
@@ -22,7 +22,6 @@ const defaultConfig: VegaWalletConfig = {
|
||||
chromeExtensionUrl: 'chrome-link',
|
||||
mozillaExtensionUrl: 'mozilla-link',
|
||||
},
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
|
||||
const setup = (config?: Partial<VegaWalletConfig>) => {
|
||||
@@ -69,7 +68,6 @@ describe('VegaWalletProvider', () => {
|
||||
expect(result.current).toEqual({
|
||||
network: defaultConfig.network,
|
||||
vegaUrl: defaultConfig.vegaUrl,
|
||||
chainId: defaultConfig.chainId,
|
||||
vegaWalletServiceUrl: defaultConfig.vegaWalletServiceUrl,
|
||||
acknowledgeNeeded: false,
|
||||
pubKey: null,
|
||||
|
||||
@@ -39,7 +39,6 @@ interface VegaWalletLinks {
|
||||
export interface VegaWalletConfig {
|
||||
network: Networks;
|
||||
vegaUrl: string;
|
||||
chainId: string;
|
||||
vegaWalletServiceUrl: string;
|
||||
links: VegaWalletLinks;
|
||||
keepAlive?: number;
|
||||
@@ -169,7 +168,6 @@ export const VegaWalletProvider = ({
|
||||
const contextValue = useMemo<VegaWalletContextShape>(() => {
|
||||
return {
|
||||
vegaUrl: config.vegaUrl,
|
||||
chainId: config.chainId,
|
||||
vegaWalletServiceUrl: config.vegaWalletServiceUrl,
|
||||
network: config.network,
|
||||
links: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export function mockBrowserWallet(overrides?: Partial<Vega>) {
|
||||
const vega: Vega = {
|
||||
getChainId: jest.fn().mockReturnValue(Promise.resolve({ chainID: '1' })),
|
||||
connectWallet: jest.fn().mockReturnValue(Promise.resolve(null)),
|
||||
disconnectWallet: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
listKeys: jest
|
||||
@@ -13,8 +14,6 @@ export function mockBrowserWallet(overrides?: Partial<Vega>) {
|
||||
success: true,
|
||||
txHash: '0x123',
|
||||
}),
|
||||
on: jest.fn(),
|
||||
isConnected: jest.fn().mockRejectedValue(Promise.resolve(true)),
|
||||
...overrides,
|
||||
};
|
||||
// @ts-ignore globalThis has no index signature
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { MAX_FETCH_ATTEMPTS, useChainId } from './use-chain-id';
|
||||
|
||||
global.fetch = jest.fn();
|
||||
const mockFetch = global.fetch as jest.Mock;
|
||||
mockFetch.mockImplementation((url: string) => {
|
||||
return Promise.resolve({ ok: true });
|
||||
});
|
||||
|
||||
describe('useChainId', () => {
|
||||
it('does not call fetch when statistics url could not be determined', async () => {
|
||||
renderHook(() => useChainId(''));
|
||||
await waitFor(() => {
|
||||
expect(mockFetch).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls fetch with correct statistics url', async () => {
|
||||
renderHook(() => useChainId('http://localhost:1234/graphql'));
|
||||
await waitFor(() => {
|
||||
expect(mockFetch).toHaveBeenCalledWith(
|
||||
'http://localhost:1234/statistics'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not return chain id when chain id is not present in response', async () => {
|
||||
const { result } = renderHook(() =>
|
||||
useChainId('http://localhost:1234/graphql')
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('returns chain id when chain id is present in response', async () => {
|
||||
mockFetch.mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => ({
|
||||
statistics: {
|
||||
chainId: '1234',
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
const { result } = renderHook(() =>
|
||||
useChainId('http://localhost:1234/graphql')
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current).not.toBeUndefined();
|
||||
expect(result.current).toEqual('1234');
|
||||
});
|
||||
});
|
||||
|
||||
it('returns chain id when within max number of attempts', async () => {
|
||||
mockFetch.mockImplementation(() => {
|
||||
if (mockFetch.mock.calls.length < MAX_FETCH_ATTEMPTS) {
|
||||
return Promise.reject();
|
||||
}
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => ({
|
||||
statistics: {
|
||||
chainId: '1234',
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
const { result } = renderHook(() =>
|
||||
useChainId('http://localhost:1234/graphql')
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current).not.toBeUndefined();
|
||||
expect(result.current).toEqual('1234');
|
||||
});
|
||||
});
|
||||
|
||||
it('does not return chain id when max number of attempts exceeded', async () => {
|
||||
mockFetch.mockImplementation(() => {
|
||||
if (mockFetch.mock.calls.length < MAX_FETCH_ATTEMPTS + 10) {
|
||||
return Promise.reject();
|
||||
}
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => ({
|
||||
statistics: {
|
||||
chainId: '1234',
|
||||
},
|
||||
}),
|
||||
});
|
||||
});
|
||||
const { result } = renderHook(() =>
|
||||
useChainId('http://localhost:5678/graphql')
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,7 @@ import { useVegaWallet } from './use-vega-wallet';
|
||||
|
||||
export function useEagerConnect(connectors: Connectors) {
|
||||
const [connecting, setConnecting] = useState(true);
|
||||
const { vegaUrl, chainId, connect, acknowledgeNeeded } = useVegaWallet();
|
||||
const { vegaUrl, connect, acknowledgeNeeded } = useVegaWallet();
|
||||
|
||||
useEffect(() => {
|
||||
const attemptConnect = async () => {
|
||||
@@ -33,7 +33,7 @@ export function useEagerConnect(connectors: Connectors) {
|
||||
|
||||
try {
|
||||
if (connector instanceof InjectedConnector) {
|
||||
await connector.connectWallet(chainId);
|
||||
await connector.connectWallet();
|
||||
await connect(connector);
|
||||
} else if (connector instanceof SnapConnector) {
|
||||
connector.nodeAddress = new URL(vegaUrl).origin;
|
||||
@@ -51,7 +51,7 @@ export function useEagerConnect(connectors: Connectors) {
|
||||
if (typeof window !== 'undefined') {
|
||||
attemptConnect();
|
||||
}
|
||||
}, [connect, connectors, acknowledgeNeeded, vegaUrl, chainId]);
|
||||
}, [connect, connectors, acknowledgeNeeded, vegaUrl]);
|
||||
|
||||
return connecting;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ const defaultConfig: VegaWalletConfig = {
|
||||
chromeExtensionUrl: 'chrome-link',
|
||||
mozillaExtensionUrl: 'mozilla-link',
|
||||
},
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
|
||||
const setup = (callback = jest.fn(), config?: Partial<VegaWalletConfig>) => {
|
||||
@@ -47,10 +46,20 @@ describe('useInjectedConnector', () => {
|
||||
expect(result.current.status).toBe(Status.Error);
|
||||
});
|
||||
|
||||
it('errors if chain ids dont match', async () => {
|
||||
mockBrowserWallet();
|
||||
const { result } = setup();
|
||||
await act(async () => {
|
||||
result.current.connect(injected, '2'); // default mock chainId is '1'
|
||||
});
|
||||
expect(result.current.error?.message).toBe('Invalid chain');
|
||||
expect(result.current.status).toBe(Status.Error);
|
||||
});
|
||||
|
||||
it('errors if connection throws', async () => {
|
||||
const callback = jest.fn();
|
||||
mockBrowserWallet({
|
||||
connectWallet: jest.fn().mockReturnValue(Promise.reject()),
|
||||
getChainId: () => Promise.reject('failed'),
|
||||
});
|
||||
const { result } = setup(callback);
|
||||
|
||||
@@ -58,9 +67,7 @@ describe('useInjectedConnector', () => {
|
||||
result.current.connect(injected, '1'); // default mock chainId is '1'
|
||||
});
|
||||
expect(result.current.status).toBe(Status.Error);
|
||||
expect(result.current.error?.message).toBe(
|
||||
'could not connect to the vega wallet on chain: 1'
|
||||
);
|
||||
expect(result.current.error?.message).toBe('injected connection failed');
|
||||
});
|
||||
|
||||
it('connects', async () => {
|
||||
@@ -71,6 +78,7 @@ describe('useInjectedConnector', () => {
|
||||
act(() => {
|
||||
result.current.connect(injected, '1'); // default mock chainId is '1'
|
||||
});
|
||||
expect(result.current.status).toBe(Status.GettingChainId);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(vega.connectWallet).toHaveBeenCalled();
|
||||
|
||||
@@ -40,19 +40,17 @@ export const useInjectedConnector = (onConnect: () => void) => {
|
||||
connector.nodeAddress = new URL(vegaUrl).origin;
|
||||
}
|
||||
|
||||
// check the chain id for snap connector
|
||||
if (connector instanceof SnapConnector) {
|
||||
setStatus(Status.GettingChainId);
|
||||
const { chainID } = await connector.getChainId();
|
||||
if (chainID !== appChainId) {
|
||||
throw InjectedConnectorErrors.INVALID_CHAIN;
|
||||
}
|
||||
setStatus(Status.GettingChainId);
|
||||
|
||||
const { chainID } = await connector.getChainId();
|
||||
if (chainID !== appChainId) {
|
||||
throw InjectedConnectorErrors.INVALID_CHAIN;
|
||||
}
|
||||
|
||||
setStatus(Status.Connecting);
|
||||
if (connector instanceof InjectedConnector) {
|
||||
// extra step for injected connector - authorize wallet
|
||||
await connector.connectWallet(appChainId);
|
||||
await connector.connectWallet();
|
||||
}
|
||||
await connect(connector); // connect with keys
|
||||
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
export * from './lib/__generated__/TransactionResult';
|
||||
export * from './lib/__generated__/WithdrawalApproval';
|
||||
export * from './lib/constants';
|
||||
export * from './lib/default-web3-provider';
|
||||
export * from './lib/eip-1193-custom-bridge';
|
||||
export * from './lib/ethereum-error';
|
||||
export * from './lib/ethereum-transaction-dialog';
|
||||
export * from './lib/types';
|
||||
export * from './lib/url-connector';
|
||||
export * from './lib/use-bridge-contract';
|
||||
export * from './lib/use-eager-connect';
|
||||
@@ -23,12 +19,7 @@ export * from './lib/use-get-withdraw-delay';
|
||||
export * from './lib/use-get-withdraw-threshold';
|
||||
export * from './lib/use-token-contract';
|
||||
export * from './lib/use-token-decimals';
|
||||
export * from './lib/use-transaction-result';
|
||||
export * from './lib/use-vega-transaction-manager';
|
||||
export * from './lib/use-vega-transaction-store';
|
||||
export * from './lib/use-vega-transaction-toasts';
|
||||
export * from './lib/use-vega-transaction-updater';
|
||||
export * from './lib/use-wallet-disconnected-toasts';
|
||||
export * from './lib/use-web3-disconnect';
|
||||
export * from './lib/web3-connect-dialog';
|
||||
export * from './lib/web3-connect-store';
|
||||
@@ -36,3 +27,11 @@ export * from './lib/web3-connectors';
|
||||
export * from './lib/web3-provider';
|
||||
export * from './lib/withdrawal-approval-dialog';
|
||||
export * from './lib/withdrawal-approval-status';
|
||||
export * from './lib/default-web3-provider';
|
||||
export * from './lib/use-vega-transaction-manager';
|
||||
export * from './lib/use-vega-transaction-store';
|
||||
export * from './lib/use-vega-transaction-updater';
|
||||
export * from './lib/use-transaction-result';
|
||||
export * from './lib/types';
|
||||
export * from './lib/__generated__/TransactionResult';
|
||||
export * from './lib/__generated__/WithdrawalApproval';
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
import {
|
||||
Intent,
|
||||
useToasts,
|
||||
ToastHeading,
|
||||
CLOSE_AFTER,
|
||||
type Toast,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useT } from './use-t';
|
||||
import { usePrevious } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const WALLET_DISCONNECTED_TOAST_ID = 'WALLET_DISCONNECTED_TOAST_ID';
|
||||
|
||||
export const useWalletDisconnectToastActions = () => {
|
||||
const [hasToast, updateToast] = useToasts((state) => [
|
||||
state.hasToast,
|
||||
state.update,
|
||||
]);
|
||||
const hideToast = () => {
|
||||
if (!hasToast(WALLET_DISCONNECTED_TOAST_ID)) return;
|
||||
updateToast(WALLET_DISCONNECTED_TOAST_ID, {
|
||||
hidden: true,
|
||||
});
|
||||
};
|
||||
const showToast = () => {
|
||||
if (!hasToast(WALLET_DISCONNECTED_TOAST_ID)) return;
|
||||
updateToast(WALLET_DISCONNECTED_TOAST_ID, {
|
||||
hidden: false,
|
||||
});
|
||||
};
|
||||
return { showToast, hideToast };
|
||||
};
|
||||
|
||||
export const useWalletDisconnectedToasts = (
|
||||
additionalContent?: JSX.Element
|
||||
) => {
|
||||
const t = useT();
|
||||
const [hasToast, setToast] = useToasts((state) => [
|
||||
state.hasToast,
|
||||
state.setToast,
|
||||
state.update,
|
||||
]);
|
||||
const { showToast, hideToast } = useWalletDisconnectToastActions();
|
||||
|
||||
const { isAlive } = useVegaWallet();
|
||||
const wasAlive = usePrevious(isAlive);
|
||||
const disconnected = wasAlive && !isAlive;
|
||||
|
||||
const toast: Toast = useMemo(
|
||||
() => ({
|
||||
id: WALLET_DISCONNECTED_TOAST_ID,
|
||||
intent: Intent.Danger,
|
||||
content: (
|
||||
<>
|
||||
<ToastHeading>{t('Wallet connection lost')}</ToastHeading>
|
||||
<p>{t('The connection to the Vega wallet has been lost.')}</p>
|
||||
{additionalContent}
|
||||
</>
|
||||
),
|
||||
onClose: () => {
|
||||
hideToast();
|
||||
},
|
||||
closeAfter: CLOSE_AFTER,
|
||||
}),
|
||||
[additionalContent, hideToast, t]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (disconnected) {
|
||||
if (hasToast(WALLET_DISCONNECTED_TOAST_ID)) {
|
||||
showToast();
|
||||
} else {
|
||||
setToast(toast);
|
||||
}
|
||||
}
|
||||
}, [disconnected, hasToast, isAlive, setToast, showToast, t, toast]);
|
||||
};
|
||||
@@ -149,9 +149,6 @@ describe('WithdrawFormContainer', () => {
|
||||
liquidityFee: '0.001',
|
||||
},
|
||||
},
|
||||
liquidityMonitoringParameters: {
|
||||
triggeringRatio: '0.7',
|
||||
},
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
COL_DEFS,
|
||||
type TypedDataAgGrid,
|
||||
type VegaICellRendererParams,
|
||||
type VegaValueFormatterParams,
|
||||
@@ -46,7 +47,7 @@ export const WithdrawalsTable = ({
|
||||
|
||||
const columnDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
{ headerName: t('Asset'), field: 'asset.symbol' },
|
||||
{ headerName: t('Asset'), field: 'asset.symbol', pinned: true },
|
||||
{
|
||||
headerName: t('Amount'),
|
||||
field: 'amount',
|
||||
@@ -135,6 +136,7 @@ export const WithdrawalsTable = ({
|
||||
<AgGrid
|
||||
overlayNoRowsTemplate={t('No withdrawals')}
|
||||
columnDefs={columnDefs}
|
||||
defaultColDef={COL_DEFS.default}
|
||||
components={{
|
||||
RecipientCell,
|
||||
StatusCell,
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@
|
||||
"@radix-ui/react-slider": "^1.1.0",
|
||||
"@radix-ui/react-switch": "^1.0.2",
|
||||
"@radix-ui/react-tabs": "^1.0.2",
|
||||
"@radix-ui/react-tooltip": "^1.0.3",
|
||||
"@radix-ui/react-tooltip": "^1.0.7",
|
||||
"@sentry/nextjs": "^6.19.3",
|
||||
"@sentry/react": "^6.19.2",
|
||||
"@sentry/tracing": "^6.19.2",
|
||||
|
||||
@@ -4864,7 +4864,7 @@
|
||||
"@radix-ui/react-separator" "1.0.3"
|
||||
"@radix-ui/react-toggle-group" "1.0.4"
|
||||
|
||||
"@radix-ui/react-tooltip@^1.0.3":
|
||||
"@radix-ui/react-tooltip@^1.0.7":
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e"
|
||||
integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==
|
||||
|
||||
Reference in New Issue
Block a user