Compare commits

..
107 changed files with 801 additions and 1140 deletions
+29
View File
@@ -0,0 +1,29 @@
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#managing-caches
name: cleanup caches by a branch
on:
pull_request:
types:
- closed
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache
echo "Fetching list of cache key"
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
## Setting this to not fail the workflow while deleting cache keys.
set +e
echo "Deleting caches..."
for cacheKey in $cacheKeysForPR
do
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
done
echo "Done"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge
@@ -40,7 +40,7 @@ context.skip('Node switcher', { tags: '@regression' }, function () {
const errorTypeTxt = 'Error: invalid url';
const nodeErrorTxt = 'fakeUrl is not a valid url.';
cy.getByTestId('node-url-custom').click();
cy.getByTestId('node-url-custom').click({ force: true });
cy.getByTestId(customNodeBtn).within(() => {
cy.get('input').clear().type('fakeUrl');
@@ -310,7 +310,9 @@ context(
closeStakingDialog();
navigateTo(navigation.validators);
clickOnValidatorFromList(0);
cy.getByTestId(stakeRemoveStakeRadioButton, txTimeout).click();
cy.getByTestId(stakeRemoveStakeRadioButton, txTimeout).click({
force: true,
});
cy.getByTestId(stakeTokenAmountInputBox).type('-0.1');
cy.contains('Waiting for next epoch to start', epochTimeout);
cy.getByTestId(stakeTokenSubmitButton)
@@ -331,7 +333,7 @@ context(
closeStakingDialog();
navigateTo(navigation.validators);
clickOnValidatorFromList(0);
cy.getByTestId(stakeRemoveStakeRadioButton).click();
cy.getByTestId(stakeRemoveStakeRadioButton).click({ force: true });
cy.getByTestId(stakeTokenAmountInputBox).type('4');
cy.contains('Waiting for next epoch to start', epochTimeout);
cy.getByTestId(stakeTokenSubmitButton)
@@ -156,7 +156,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.getByTestId('subscription-cell').should('have.text', 'Yes');
});
cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('node-url-custom').click();
cy.getByTestId('node-url-custom').click({ force: true });
cy.get('input').should('exist');
cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('icon-cross').click();
@@ -11,7 +11,6 @@ import {
waitForBeginningOfEpoch,
} from '../../support/staking.functions';
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
const guideLink = 'staking-guide-link';
const validatorTitle = 'validator-node-title';
@@ -38,17 +37,11 @@ const txTimeout = Cypress.env('txTimeout');
context('Validators Page - verify elements on page', function () {
before('navigate to validators page', () => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
cy.mockChainId();
cy.visit('/validators');
});
beforeEach(() => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
cy.mockChainId();
});
describe('with wallets disconnected', { tags: '@smoke' }, function () {
@@ -189,10 +182,7 @@ context('Validators Page - verify elements on page', function () {
{ tags: '@smoke' },
function () {
before('connect wallets and click on validator', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
cy.mockChainId();
cy.visit('/validators');
cy.connectVegaWallet();
clickOnValidatorFromList(0);
@@ -4,8 +4,6 @@ import {
vegaWalletFaucetAssetsWithoutCheck,
vegaWalletTeardown,
} from '../../support/wallet-functions';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
const walletContainer = 'aside [data-testid="vega-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
@@ -88,10 +86,7 @@ context(
describe('when vega wallet connected', function () {
before('connect vega wallet', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
cy.mockChainId();
cy.visit('/');
cy.wait('@ChainId');
cy.connectVegaWallet();
@@ -276,10 +271,7 @@ context(
];
before('faucet assets to connected vega wallet', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
cy.mockChainId();
for (const { id, amount } of assets) {
vegaWalletFaucetAssetsWithoutCheck(id, amount, vegaWalletPublicKey);
}
+1 -6
View File
@@ -9,8 +9,6 @@ import './wallet-functions.ts';
import './proposal.functions.ts';
import 'cypress-mochawesome-reporter/register';
import registerCypressGrep from '@cypress/grep';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
import { turnTelemetryOff } from './common.functions.ts';
registerCypressGrep();
@@ -29,10 +27,7 @@ before(() => {
// // Ensuring the telemetry modal doesn't disrupt the tests
turnTelemetryOff();
// Mock chainId fetch which happens on every page for wallet connection
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
});
cy.mockChainId();
// Self stake validators so they are displayed
cy.validatorsSelfDelegate();
});
@@ -46,7 +46,7 @@ export function stakingValidatorPageAddStake(stake: string) {
export function stakingValidatorPageRemoveStake(stake: string) {
cy.highlight(`Removing a stake of ${stake}`);
cy.get(removeStakeRadioButton, epochTimeout).click();
cy.get(removeStakeRadioButton, epochTimeout).click({ force: true });
cy.get(tokenAmountInputBox).type(stake);
waitForBeginningOfEpoch();
cy.get(tokenSubmitButton)
@@ -70,9 +70,13 @@ export function stakingPageAssociateTokens(
cy.highlight(`Associating ${amount} tokens from ${type}`);
cy.get(ethWalletAssociateButton).first().click();
if (type === 'wallet') {
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
cy.get(associateWalletRadioButton, { timeout: 30000 }).click({
force: true,
});
} else if (type === 'contract') {
cy.get(associateContractRadioButton, { timeout: 30000 }).click();
cy.get(associateContractRadioButton, { timeout: 30000 }).click({
force: true,
});
} else {
cy.highlight(`${type} is not association option`);
}
+2 -2
View File
@@ -26,11 +26,11 @@ import {
} from '@vegaprotocol/web3';
import { Web3Provider } from '@vegaprotocol/web3';
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
import {
useVegaTransactionManager,
useVegaTransactionUpdater,
VegaWalletProvider,
} from '@vegaprotocol/wallet';
} from '@vegaprotocol/web3';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useEthereumConfig } from '@vegaprotocol/web3';
import {
@@ -4,7 +4,7 @@ import {
getProposalDialogTitle,
} from '@vegaprotocol/proposals';
import type { ProposalEventFieldsFragment } from '@vegaprotocol/proposals';
import type { DialogProps } from '@vegaprotocol/wallet';
import type { DialogProps } from '@vegaprotocol/proposals';
interface ProposalFormTransactionDialogProps {
finalizedProposal: ProposalEventFieldsFragment | null;
@@ -6,7 +6,7 @@ import { ConnectToVega } from '../../../../components/connect-to-vega';
import { VoteButtonsContainer } from './vote-buttons';
import { SubHeading } from '../../../../components/heading';
import type { VoteValue } from '@vegaprotocol/types';
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { VoteState } from './use-user-vote';
@@ -1,7 +1,7 @@
import { render, screen } from '@testing-library/react';
import { VoteTransactionDialog } from './vote-transaction-dialog';
import { VoteState } from './use-user-vote';
import { VegaTxStatus } from '@vegaprotocol/wallet';
import { VegaTxStatus } from '@vegaprotocol/proposals';
describe('VoteTransactionDialog', () => {
const mockTransactionDialog = jest.fn(({ title, content }) => (
@@ -17,7 +17,7 @@ import { VoteState } from './use-user-vote';
import { ProposalMinRequirements, ProposalUserAction } from '../shared';
import { VoteTransactionDialog } from './vote-transaction-dialog';
import { useVoteButtonsQuery } from './__generated__/Stake';
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
interface VoteButtonsContainerProps {
voteState: VoteState | null;
@@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/i18n';
import { VoteState } from './use-user-vote';
import type { DialogProps, VegaTxState } from '@vegaprotocol/wallet';
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
interface VoteTransactionDialogProps {
voteState: VoteState;
@@ -61,7 +61,7 @@ describe('home', { tags: '@regression' }, () => {
// 0006-NETW-020
cy.getByTestId(nodeHealthTrigger).click();
cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('node-url-custom').click();
cy.getByTestId('node-url-custom').click({ force: true });
cy.getByTestId('connect').should('be.disabled');
cy.get("input[placeholder='https://']")
.focus()
@@ -1,92 +1,11 @@
import * as Schema from '@vegaprotocol/types';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import {
accountsQuery,
amendGeneralAccountBalance,
amendMarginAccountBalance,
} from '@vegaprotocol/mock';
import type { OrderSubmission } from '@vegaprotocol/wallet';
import { createOrder } from '../support/create-order';
describe.skip(
'account validation',
describe(
'vega wallet - prompt',
{ tags: '@regression', testIsolation: true },
() => {
describe.skip('zero balance error', () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
let accounts = accountsQuery();
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
accounts = amendGeneralAccountBalance(accounts, 'market-0', '0');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
});
it('should show an error if your balance is zero', () => {
const accounts = accountsQuery();
amendMarginAccountBalance(accounts, 'market-0', '0');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
// 7002-SORD-060
cy.getByTestId('place-order').should('be.enabled');
// 7002-SORD-003
cy.getByTestId('deal-ticket-error-message-zero-balance').should(
'have.text',
'You need ' +
'tDAI' +
' in your wallet to trade in this market. See all your collateral.Make a deposit'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
});
});
describe('not enough balance warning', () => {
beforeEach(() => {
cy.setVegaWallet();
cy.mockTradingPage();
let accounts = accountsQuery();
accounts = amendMarginAccountBalance(accounts, 'market-0', '1000');
accounts = amendGeneralAccountBalance(accounts, 'market-0', '1');
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Accounts', accounts);
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
if (!$form.length) {
cy.getByTestId('Order').click();
}
});
});
it('should display info and button for deposit', () => {
// 7002-SORD-003
// warning should show immediately
cy.getByTestId('deal-ticket-warning-margin').should(
'contain.text',
'You may not have enough margin available to open this position'
);
cy.getByTestId('deal-ticket-warning-margin').should(
'contain.text',
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
cy.getByTestId('sidebar-content')
.find('h2')
.eq(0)
.should('have.text', 'Deposit');
});
});
describe('must submit order', { tags: '@smoke' }, () => {
// 7002-SORD-039
beforeEach(() => {
+1 -4
View File
@@ -7,7 +7,6 @@ import {
assetQuery,
assetsQuery,
candlesQuery,
chainIdQuery,
chartQuery,
depositsQuery,
estimateFeesQuery,
@@ -25,7 +24,6 @@ import {
estimatePositionQuery,
positionsQuery,
proposalListQuery,
statisticsQuery,
tradesQuery,
withdrawalsQuery,
protocolUpgradeProposalsQuery,
@@ -91,8 +89,6 @@ const mockTradingPage = (
tradingMode?: Schema.MarketTradingMode,
trigger?: Schema.AuctionTrigger
) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'NodeCheck', statisticsQuery());
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
aliasGQLQuery(
req,
@@ -218,6 +214,7 @@ export const addMockTradingPage = () => {
trigger,
oracleStatus
) => {
cy.mockChainId();
cy.mockGQL((req) => {
mockTradingPage(req, state, tradingMode, trigger);
});
@@ -10,12 +10,10 @@ import classNames from 'classnames';
import { Navigate, useSearchParams } from 'react-router-dom';
import { useEffect, useRef, useState } from 'react';
import { Button } from './buttons';
import {
useTransactionEventSubscription,
useVegaWallet,
} from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useReferral } from './hooks/use-referral';
import { Routes } from '../../lib/links';
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
export const ApplyCodeForm = () => {
const [status, setStatus] = useState<
@@ -1,6 +1,6 @@
import { useVegaWallet } from '@vegaprotocol/wallet';
import { WithdrawFormContainer } from '@vegaprotocol/withdraws';
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from '@vegaprotocol/web3';
export const WithdrawContainer = ({ assetId }: { assetId?: string }) => {
const { pubKey } = useVegaWallet();
+4 -2
View File
@@ -4,10 +4,12 @@ import type { AppProps } from 'next/app';
import { t } from '@vegaprotocol/i18n';
import {
useEagerConnect as useVegaEagerConnect,
useVegaTransactionManager,
useVegaTransactionUpdater,
useVegaWallet,
} from '@vegaprotocol/wallet';
import {
useVegaTransactionManager,
useVegaTransactionUpdater,
} from '@vegaprotocol/web3';
import {
useEagerConnect as useEthereumEagerConnect,
useEthTransactionManager,
@@ -37,6 +37,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
headerName: t('Market'),
field: 'market.tradableInstrument.instrument.code',
minWidth: 200,
sort: 'desc',
cellRenderer: ({
value,
data,
@@ -140,6 +141,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
tooltipShowDelay={500}
defaultColDef={defaultColDef}
columnDefs={coldefs}
domLayout="autoHeight"
/>
);
}
+2 -1
View File
@@ -7,7 +7,8 @@ import {
} from '@vegaprotocol/network-parameters';
import { useDataProvider } from '@vegaprotocol/data-provider';
import type { Transfer } from '@vegaprotocol/wallet';
import { useVegaTransactionStore, useVegaWallet } from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from '@vegaprotocol/web3';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useCallback, useMemo } from 'react';
import { accountsDataProvider } from './accounts-data-provider';
import { TransferForm } from './transfer-form';
+15 -15
View File
@@ -258,21 +258,21 @@ export const TransferForm = ({
)}
</TradingFormGroup>
<div className="mb-4">
<TradingCheckbox
name="include-transfer-fee"
disabled={!transferAmount}
label={
<Tooltip
description={t(
`The fee will be taken from the amount you are transferring.`
)}
>
<div>{t('Include transfer fee')}</div>
</Tooltip>
}
checked={includeFee}
onCheckedChange={() => setIncludeFee(!includeFee)}
/>
<Tooltip
description={t(
`The fee will be taken from the amount you are transferring.`
)}
>
<div>
<TradingCheckbox
name="include-transfer-fee"
disabled={!transferAmount}
label={t('Include transfer fee')}
checked={includeFee}
onCheckedChange={() => setIncludeFee(!includeFee)}
/>
</div>
</Tooltip>
</div>
{transferAmount && fee && (
<TransferFee
-1
View File
@@ -22,7 +22,6 @@ export * from '../markets/src/lib/oracle-spec-data-connection.mock';
export * from '../orders/src/lib/components/order-data-provider/orders.mock';
export * from '../positions/src/lib/positions.mock';
export * from '../network-parameters/src/network-params.mock';
export * from '../wallet/src/connect-dialog/chain-id.mock';
export * from '../positions/src/lib/estimate-position.mock';
export * from '../trades/src/lib/trades.mock';
export * from '../withdraws/src/lib/withdrawal.mock';
+2 -1
View File
@@ -1,5 +1,5 @@
import { addGetTestIdcommand } from './lib/commands/get-by-test-id';
import { addMockGQLCommand } from './lib/mock-gql';
import { addMockGQLCommand, addMockStatistics } from './lib/mock-gql';
import { addMockSubscription } from './lib/mock-ws';
import { addMockWalletCommand } from './lib/mock-rest';
import { addMockWeb3ProviderCommand } from './lib/commands/mock-web3-provider';
@@ -28,6 +28,7 @@ import { addMockChainId } from './lib/commands/mock-chain-id';
addGetTestIdcommand();
addMockGQLCommand();
addMockStatistics();
addMockSubscription();
addMockWalletCommand();
addMockWeb3ProviderCommand();
+18 -3
View File
@@ -1,6 +1,6 @@
import { aliasGQLQuery } from '../mock-gql';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { chainIdQuery, statisticsQuery } from '@vegaprotocol/mock';
import { statisticsQuery } from '@vegaprotocol/mock';
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
@@ -12,11 +12,26 @@ declare global {
}
}
const chainId = 'test-id';
export function addMockChainId() {
Cypress.Commands.add('mockChainId', () => {
const result = {
statistics: {
chainId,
},
};
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(req, 'NodeCheck', statisticsQuery(result));
});
cy.mockStatistics((req) => {
req.reply({
statusCode: 200,
body: result,
headers: {
'Content-Type': 'application/json',
},
});
});
});
}
+11
View File
@@ -8,6 +8,7 @@ declare global {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
mockGQL(handler: RouteHandler): void;
mockStatistics(handler: RouteHandler): void;
}
}
}
@@ -40,6 +41,16 @@ const extractVariables = (req: CyHttpMessages.IncomingHttpRequest): object => {
);
};
export function addMockStatistics() {
Cypress.Commands.add('mockStatistics', (handler: RouteHandler) => {
cy.intercept(
'GET',
Cypress.env('VEGA_URL').replace('graphql', 'statistics'),
handler
).as('ChainId');
});
}
export function addMockGQLCommand() {
Cypress.Commands.add('mockGQL', (handler: RouteHandler) => {
cy.intercept('POST', Cypress.env('VEGA_URL'), handler).as('GQL');
@@ -1,4 +1,4 @@
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from '@vegaprotocol/web3';
import {
isStopOrderType,
useDealTicketFormValues,
@@ -273,7 +273,7 @@ export const DealTicketMarginDetails = ({
key={'value-dropdown'}
className="flex items-center justify-between w-full gap-2"
>
<div className="flex items-center gap-1">
<div className="flex items-center gap-1 text-left">
<Tooltip description={MARGIN_DIFF_TOOLTIP_TEXT(assetSymbol)}>
<span className="text-muted">{t('Margin required')}</span>
</Tooltip>
@@ -494,16 +494,16 @@ const TimeInForce = ({
);
const ReduceOnly = () => (
<Checkbox
name="reduce-only"
checked={true}
disabled={true}
label={
<Tooltip description={<span>{t(REDUCE_ONLY_TOOLTIP)}</span>}>
<>{t('Reduce only')}</>
</Tooltip>
}
/>
<Tooltip description={<span>{t(REDUCE_ONLY_TOOLTIP)}</span>}>
<div>
<Checkbox
name="reduce-only"
checked={true}
disabled={true}
label={t('Reduce only')}
/>
</div>
</Tooltip>
);
const NotionalAndFees = ({
@@ -548,62 +548,62 @@ export const DealTicket = ({
name="postOnly"
control={control}
render={({ field }) => (
<Checkbox
name="post-only"
checked={!disablePostOnlyCheckbox && field.value}
disabled={disablePostOnlyCheckbox}
onCheckedChange={(postOnly) => {
field.onChange(postOnly);
setValue('reduceOnly', false);
}}
label={
<Tooltip
description={
<span>
{disablePostOnlyCheckbox
? t(
'"Post only" can not be used on "Fill or Kill" or "Immediate or Cancel" orders.'
)
: t(
'"Post only" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.'
)}
</span>
}
>
<span className="text-xs">{t('Post only')}</span>
</Tooltip>
<Tooltip
description={
<span>
{disablePostOnlyCheckbox
? t(
'"Post only" can not be used on "Fill or Kill" or "Immediate or Cancel" orders.'
)
: t(
'"Post only" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.'
)}
</span>
}
/>
>
<div>
<Checkbox
name="post-only"
checked={!disablePostOnlyCheckbox && field.value}
disabled={disablePostOnlyCheckbox}
onCheckedChange={(postOnly) => {
field.onChange(postOnly);
setValue('reduceOnly', false);
}}
label={t('Post only')}
/>
</div>
</Tooltip>
)}
/>
<Controller
name="reduceOnly"
control={control}
render={({ field }) => (
<Checkbox
name="reduce-only"
checked={!disableReduceOnlyCheckbox && field.value}
disabled={disableReduceOnlyCheckbox}
onCheckedChange={(reduceOnly) => {
field.onChange(reduceOnly);
setValue('postOnly', false);
}}
label={
<Tooltip
description={
<span>
{disableReduceOnlyCheckbox
? t(
'"Reduce only" can be used only with non-persistent orders, such as "Fill or Kill" or "Immediate or Cancel".'
)
: t(REDUCE_ONLY_TOOLTIP)}
</span>
}
>
<span className="text-xs">{t('Reduce only')}</span>
</Tooltip>
<Tooltip
description={
<span>
{disableReduceOnlyCheckbox
? t(
'"Reduce only" can be used only with non-persistent orders, such as "Fill or Kill" or "Immediate or Cancel".'
)
: t(REDUCE_ONLY_TOOLTIP)}
</span>
}
/>
>
<div>
<Checkbox
name="reduce-only"
checked={!disableReduceOnlyCheckbox && field.value}
disabled={disableReduceOnlyCheckbox}
onCheckedChange={(reduceOnly) => {
field.onChange(reduceOnly);
setValue('postOnly', false);
}}
label={t('Reduce only')}
/>
</div>
</Tooltip>
)}
/>
</div>
@@ -614,26 +614,26 @@ export const DealTicket = ({
name="iceberg"
control={control}
render={({ field }) => (
<Checkbox
name="iceberg"
checked={field.value}
onCheckedChange={field.onChange}
disabled={disableIcebergCheckbox}
label={
<Tooltip
description={
<p>
{t(`Trade only a fraction of the order size at once.
<Tooltip
description={
<p>
{t(`Trade only a fraction of the order size at once.
After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away.
For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each.
Note that the full volume of the order is not hidden and is still reflected in the order book.`)}
</p>
}
>
<span className="text-xs">{t('Iceberg')}</span>
</Tooltip>
</p>
}
/>
>
<div>
<Checkbox
name="iceberg"
checked={field.value}
onCheckedChange={field.onChange}
disabled={disableIcebergCheckbox}
label={t('Iceberg')}
/>
</div>
</Tooltip>
)}
/>
</div>
@@ -25,11 +25,11 @@ export const KeyValue = ({
}: KeyValuePros) => {
const displayValue = `${formattedValue ?? '-'} ${symbol || ''}`;
const valueElement = onClick ? (
<button onClick={onClick} className="font-mono">
<button onClick={onClick} className="font-mono ml-auto">
{displayValue}
</button>
) : (
<div className="font-mono">{displayValue}</div>
<div className="font-mono ml-auto">{displayValue}</div>
);
return (
<div
@@ -40,12 +40,12 @@ export const KeyValue = ({
}`}
key={typeof label === 'string' ? label : 'value-dropdown'}
className={classnames(
'text-xs flex justify-between items-center gap-4 flex-wrap',
'text-xs flex justify-between items-center gap-4 flex-wrap text-right',
{ 'ml-2': indent }
)}
>
<Tooltip description={labelDescription}>
<div className="text-muted">{label}</div>
<div className="text-muted text-left">{label}</div>
</Tooltip>
<Tooltip description={`${value ?? '-'} ${symbol || ''}`}>
{valueElement}
@@ -1,6 +1,7 @@
import { t } from '@vegaprotocol/i18n';
import { TradingButton } from '@vegaprotocol/ui-toolkit';
import { useVegaTransactionStore, useVegaWallet } from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from '@vegaprotocol/web3';
import { useHasAmendableOrder } from '../../order-hooks';
export const OpenOrdersMenu = ({ marketId }: { marketId: string }) => {
@@ -32,12 +32,6 @@ fragment OrderFields on Order {
}
}
query OrderById($orderId: ID!) {
orderByID(id: $orderId) {
...OrderFields
}
}
query Orders(
$partyId: ID!
$marketIds: [ID!]
@@ -153,9 +147,3 @@ query StopOrders($filter: StopOrderFilter) {
}
}
}
query StopOrderById($stopOrderId: ID!) {
stopOrder(id: $stopOrderId) {
...StopOrderFields
}
}
@@ -5,13 +5,6 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type OrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null };
export type OrderByIdQueryVariables = Types.Exact<{
orderId: Types.Scalars['ID'];
}>;
export type OrderByIdQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } };
export type OrdersQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
marketIds?: Types.InputMaybe<Array<Types.Scalars['ID']> | Types.Scalars['ID']>;
@@ -43,13 +36,6 @@ export type StopOrdersQueryVariables = Types.Exact<{
export type StopOrdersQuery = { __typename?: 'Query', stopOrders?: { __typename?: 'StopOrderConnection', edges?: Array<{ __typename?: 'StopOrderEdge', node?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null }> | null } | null };
export type StopOrderByIdQueryVariables = Types.Exact<{
stopOrderId: Types.Scalars['ID'];
}>;
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
export const OrderUpdateFieldsFragmentDoc = gql`
fragment OrderUpdateFields on OrderUpdate {
id
@@ -161,41 +147,6 @@ export const StopOrderFieldsFragmentDoc = gql`
}
${OrderFieldsFragmentDoc}
${OrderSubmissionFieldsFragmentDoc}`;
export const OrderByIdDocument = gql`
query OrderById($orderId: ID!) {
orderByID(id: $orderId) {
...OrderFields
}
}
${OrderFieldsFragmentDoc}`;
/**
* __useOrderByIdQuery__
*
* To run a query within a React component, call `useOrderByIdQuery` and pass it any options that fit your needs.
* When your component renders, `useOrderByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useOrderByIdQuery({
* variables: {
* orderId: // value for 'orderId'
* },
* });
*/
export function useOrderByIdQuery(baseOptions: Apollo.QueryHookOptions<OrderByIdQuery, OrderByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<OrderByIdQuery, OrderByIdQueryVariables>(OrderByIdDocument, options);
}
export function useOrderByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<OrderByIdQuery, OrderByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<OrderByIdQuery, OrderByIdQueryVariables>(OrderByIdDocument, options);
}
export type OrderByIdQueryHookResult = ReturnType<typeof useOrderByIdQuery>;
export type OrderByIdLazyQueryHookResult = ReturnType<typeof useOrderByIdLazyQuery>;
export type OrderByIdQueryResult = Apollo.QueryResult<OrderByIdQuery, OrderByIdQueryVariables>;
export const OrdersDocument = gql`
query Orders($partyId: ID!, $marketIds: [ID!], $pagination: Pagination, $filter: OrderFilter) {
party(id: $partyId) {
@@ -320,39 +271,4 @@ export function useStopOrdersLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions
}
export type StopOrdersQueryHookResult = ReturnType<typeof useStopOrdersQuery>;
export type StopOrdersLazyQueryHookResult = ReturnType<typeof useStopOrdersLazyQuery>;
export type StopOrdersQueryResult = Apollo.QueryResult<StopOrdersQuery, StopOrdersQueryVariables>;
export const StopOrderByIdDocument = gql`
query StopOrderById($stopOrderId: ID!) {
stopOrder(id: $stopOrderId) {
...StopOrderFields
}
}
${StopOrderFieldsFragmentDoc}`;
/**
* __useStopOrderByIdQuery__
*
* To run a query within a React component, call `useStopOrderByIdQuery` and pass it any options that fit your needs.
* When your component renders, `useStopOrderByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useStopOrderByIdQuery({
* variables: {
* stopOrderId: // value for 'stopOrderId'
* },
* });
*/
export function useStopOrderByIdQuery(baseOptions: Apollo.QueryHookOptions<StopOrderByIdQuery, StopOrderByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<StopOrderByIdQuery, StopOrderByIdQueryVariables>(StopOrderByIdDocument, options);
}
export function useStopOrderByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<StopOrderByIdQuery, StopOrderByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<StopOrderByIdQuery, StopOrderByIdQueryVariables>(StopOrderByIdDocument, options);
}
export type StopOrderByIdQueryHookResult = ReturnType<typeof useStopOrderByIdQuery>;
export type StopOrderByIdLazyQueryHookResult = ReturnType<typeof useStopOrderByIdLazyQuery>;
export type StopOrderByIdQueryResult = Apollo.QueryResult<StopOrderByIdQuery, StopOrderByIdQueryVariables>;
export type StopOrdersQueryResult = Apollo.QueryResult<StopOrdersQuery, StopOrdersQueryVariables>;
@@ -5,11 +5,9 @@ import { OrderListTable } from '../order-list/order-list';
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
import {
normalizeOrderAmendment,
useVegaTransactionStore,
} from '@vegaprotocol/wallet';
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
import { normalizeOrderAmendment } from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from '@vegaprotocol/web3';
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/web3';
import { OrderEditDialog } from '../order-list/order-edit-dialog';
import type { Order } from '../order-data-provider';
import { OrderViewDialog } from '../order-list/order-view-dialog';
@@ -1,8 +1,6 @@
import type { Story, Meta } from '@storybook/react';
import { OrderListTable } from './order-list';
import { useState } from 'react';
import type { VegaTxState } from '@vegaprotocol/wallet';
import { VegaTransactionDialog, VegaTxStatus } from '@vegaprotocol/wallet';
import { generateOrdersArray } from '../mocks';
import { OrderEditDialog } from './order-edit-dialog';
import type { Order } from '../order-data-provider';
@@ -32,25 +30,15 @@ const Template: Story = (args) => {
};
const Template2: Story = (args) => {
const [open, setOpen] = useState(false);
const [editOrder, setEditOrder] = useState<Order>();
const cancel = () => {
setOpen(!open);
return Promise.resolve();
};
const transaction: VegaTxState = {
status: VegaTxStatus.Requested,
error: null,
txHash: null,
signature: null,
dialogOpen: false,
};
return (
<>
<div style={{ height: 1000 }}>
<OrderListTable
rowData={args.data}
onCancel={cancel}
onCancel={() => {
return;
}}
onEdit={setEditOrder}
onView={() => {
return;
@@ -58,11 +46,6 @@ const Template2: Story = (args) => {
isReadOnly={false}
/>
</div>
<VegaTransactionDialog
isOpen={open}
onChange={setOpen}
transaction={transaction}
/>
{editOrder && (
<OrderEditDialog
isOpen={Boolean(editOrder)}
@@ -2,12 +2,12 @@ import { t } from '@vegaprotocol/i18n';
import { useCallback, useEffect, useState } from 'react';
import { StopOrdersTable } from '../stop-orders-table/stop-orders-table';
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
import type { StopOrder } from '../order-data-provider/stop-orders-data-provider';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { stopOrdersWithMarketProvider } from '../order-data-provider/stop-orders-data-provider';
import { OrderViewDialog } from '../order-list/order-view-dialog';
import type { Order, StopOrdersQueryVariables } from '../order-data-provider';
import { useVegaTransactionStore } from '@vegaprotocol/web3';
export interface StopOrdersManagerProps {
partyId: string;
-1
View File
@@ -1,3 +1,2 @@
export * from './__generated__/OrdersSubscription';
export * from './use-has-amendable-order';
export * from './use-order-update';
@@ -1,60 +0,0 @@
import { useApolloClient } from '@apollo/client';
import { useCallback, useEffect, useRef } from 'react';
import { OrderSubDocument } from './__generated__/OrdersSubscription';
import type {
OrderSubSubscription,
OrderSubSubscriptionVariables,
OrderSubFieldsFragment,
} from './__generated__/OrdersSubscription';
import type { Subscription } from 'zen-observable-ts';
import type { VegaTxState } from '@vegaprotocol/wallet';
type WaitFunc = (
orderId: string,
partyId: string
) => Promise<OrderSubFieldsFragment>;
export const useOrderUpdate = (transaction: VegaTxState) => {
const client = useApolloClient();
const subRef = useRef<Subscription | null>(null);
const waitForOrderUpdate = useCallback<WaitFunc>(
(id: string, partyId: string) => {
return new Promise((resolve) => {
subRef.current = client
.subscribe<OrderSubSubscription, OrderSubSubscriptionVariables>({
query: OrderSubDocument,
variables: { partyId },
})
.subscribe(({ data }) => {
if (!data?.orders?.length) {
return;
}
// No types available for the subscription result
const matchingOrder = data.orders.find((order) => {
return order.id === id;
});
if (matchingOrder) {
resolve(matchingOrder);
subRef.current?.unsubscribe();
}
});
});
},
[client]
);
useEffect(() => {
if (!transaction.dialogOpen) {
subRef.current?.unsubscribe();
}
return () => {
subRef.current?.unsubscribe();
};
}, [transaction.dialogOpen]);
return waitForOrderUpdate;
};
+2 -105
View File
@@ -1,110 +1,7 @@
import { Intent } from '@vegaprotocol/ui-toolkit';
import {
getOrderToastIntent,
getOrderToastTitle,
getRejectionReason,
timeInForceLabel,
} from './utils';
import { timeInForceLabel } from './utils';
import * as Types from '@vegaprotocol/types';
describe('WithdrawalsTable', () => {
describe('getOrderToastTitle', () => {
it('should return the correct title', () => {
expect(getOrderToastTitle(Types.OrderStatus.STATUS_ACTIVE)).toBe(
'Order submitted'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_FILLED)).toBe(
'Order filled'
);
expect(
getOrderToastTitle(Types.OrderStatus.STATUS_PARTIALLY_FILLED)
).toBe('Order partially filled');
expect(getOrderToastTitle(Types.OrderStatus.STATUS_PARKED)).toBe(
'Order parked'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_STOPPED)).toBe(
'Order stopped'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_CANCELLED)).toBe(
'Order cancelled'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_EXPIRED)).toBe(
'Order expired'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_REJECTED)).toBe(
'Order rejected'
);
expect(getOrderToastTitle(undefined)).toBe(undefined);
});
});
describe('getOrderToastIntent', () => {
it('should return the correct intent', () => {
expect(getOrderToastIntent(Types.OrderStatus.STATUS_PARKED)).toBe(
Intent.Warning
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_EXPIRED)).toBe(
Intent.Warning
);
expect(
getOrderToastIntent(Types.OrderStatus.STATUS_PARTIALLY_FILLED)
).toBe(Intent.Warning);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_REJECTED)).toBe(
Intent.Danger
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_STOPPED)).toBe(
Intent.Warning
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_FILLED)).toBe(
Intent.Success
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_ACTIVE)).toBe(
Intent.Success
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_CANCELLED)).toBe(
Intent.Success
);
expect(getOrderToastIntent(undefined)).toBe(undefined);
});
});
describe('getRejectionReason', () => {
it('should return the correct rejection reason for insufficient asset balance', () => {
expect(
getRejectionReason({
rejectionReason:
Types.OrderRejectionReason.ORDER_ERROR_INSUFFICIENT_ASSET_BALANCE,
status: Types.OrderStatus.STATUS_REJECTED,
id: '',
createdAt: undefined,
size: '',
price: '',
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
side: Types.Side.SIDE_BUY,
marketId: '',
})
).toBe('Insufficient asset balance');
});
it('should return the correct rejection reason when order is stopped', () => {
expect(
getRejectionReason({
rejectionReason: null,
status: Types.OrderStatus.STATUS_STOPPED,
id: '',
createdAt: undefined,
size: '',
price: '',
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
side: Types.Side.SIDE_BUY,
marketId: '',
})
).toBe(
'Your Fill or Kill (FOK) order was not filled and it has been stopped'
);
});
});
describe('utils', () => {
describe('timeInForceLabel', () => {
it('should return the correct label for time in force', () => {
expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_FOK)).toBe(
-71
View File
@@ -1,7 +1,5 @@
import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types';
import type { OrderSubFieldsFragment } from './order-hooks';
import { Intent } from '@vegaprotocol/ui-toolkit';
// More detail in https://docs.vega.xyz/mainnet/graphql/enums/order-time-in-force
export const timeInForceLabel = (tif: string) => {
@@ -22,72 +20,3 @@ export const timeInForceLabel = (tif: string) => {
return t(tif);
}
};
export const getRejectionReason = (
order: OrderSubFieldsFragment
): string | null => {
switch (order.status) {
case Schema.OrderStatus.STATUS_STOPPED:
return t(
`Your ${
Schema.OrderTimeInForceMapping[order.timeInForce]
} order was not filled and it has been stopped`
);
default:
return order.rejectionReason
? t(Schema.OrderRejectionReasonMapping[order.rejectionReason])
: '';
}
};
export const getOrderToastTitle = (
status?: Schema.OrderStatus
): string | undefined => {
if (!status) {
return;
}
switch (status) {
case Schema.OrderStatus.STATUS_ACTIVE:
return t('Order submitted');
case Schema.OrderStatus.STATUS_FILLED:
return t('Order filled');
case Schema.OrderStatus.STATUS_PARTIALLY_FILLED:
return t('Order partially filled');
case Schema.OrderStatus.STATUS_PARKED:
return t('Order parked');
case Schema.OrderStatus.STATUS_STOPPED:
return t('Order stopped');
case Schema.OrderStatus.STATUS_CANCELLED:
return t('Order cancelled');
case Schema.OrderStatus.STATUS_EXPIRED:
return t('Order expired');
case Schema.OrderStatus.STATUS_REJECTED:
return t('Order rejected');
default:
return t('Submission failed');
}
};
export const getOrderToastIntent = (
status?: Schema.OrderStatus
): Intent | undefined => {
if (!status) {
return;
}
switch (status) {
case Schema.OrderStatus.STATUS_PARKED:
case Schema.OrderStatus.STATUS_EXPIRED:
case Schema.OrderStatus.STATUS_PARTIALLY_FILLED:
case Schema.OrderStatus.STATUS_STOPPED:
return Intent.Warning;
case Schema.OrderStatus.STATUS_REJECTED:
return Intent.Danger;
case Schema.OrderStatus.STATUS_FILLED:
case Schema.OrderStatus.STATUS_ACTIVE:
case Schema.OrderStatus.STATUS_CANCELLED:
return Intent.Success;
default:
return;
}
};
@@ -9,6 +9,9 @@ const mockCreate = jest.fn();
jest.mock('@vegaprotocol/wallet', () => ({
...jest.requireActual('@vegaprotocol/wallet'),
useVegaWallet: jest.fn(() => ({ pubKey: 'partyId' })),
}));
jest.mock('@vegaprotocol/web3', () => ({
...jest.requireActual('@vegaprotocol/web3'),
useVegaTransactionStore: jest.fn(() => mockCreate),
}));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
+2 -1
View File
@@ -1,7 +1,8 @@
import { useCallback } from 'react';
import { PositionsTable } from './positions-table';
import * as Schema from '@vegaprotocol/types';
import { useVegaTransactionStore, useVegaWallet } from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from '@vegaprotocol/web3';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider';
import {
+1
View File
@@ -5,3 +5,4 @@ export * from './protocol-upgrade-countdown';
export * from './protocol-upgrade-in-progress-notification';
export * from './protocol-upgrade-proposal-notification';
export * from './voting-progress';
export * from './vega-transaction-dialog';
@@ -1,8 +1,8 @@
import { render, screen } from '@testing-library/react';
import { WalletError } from '../connectors';
import type { VegaWalletContextShape } from '../context';
import { VegaWalletContext } from '../context';
import { VegaTxStatus } from '../types';
import { WalletError } from '@vegaprotocol/wallet';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { VegaTxStatus } from '../../lib/proposals-hooks/use-vega-transaction';
import type { VegaTransactionDialogProps } from './vega-transaction-dialog';
import { VegaTransactionDialog } from './vega-transaction-dialog';
@@ -2,9 +2,9 @@ import type { ReactNode } from 'react';
import { t } from '@vegaprotocol/i18n';
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
import { WalletClientError } from '@vegaprotocol/wallet-client';
import type { VegaTxState } from '../types';
import { VegaTxStatus } from '../types';
import { useVegaWallet } from '../use-vega-wallet';
import type { VegaTxState } from '../../lib/proposals-hooks/use-vega-transaction';
import { VegaTxStatus } from '../../lib/proposals-hooks/use-vega-transaction';
import { useVegaWallet } from '@vegaprotocol/wallet';
export type VegaTransactionContentMap = {
[C in VegaTxStatus]?: JSX.Element;
@@ -1,5 +1,6 @@
export * from './__generated__/Proposal';
export * from './use-proposal-event';
export * from './use-vega-transaction';
export * from './use-proposal-submit';
export * from './use-update-proposal';
export * from './use-update-network-paramaters-toasts';
@@ -7,7 +7,7 @@ import type {
ProposalEventFieldsFragment,
} from './__generated__/Proposal';
import type { Subscription } from 'zen-observable-ts';
import type { VegaTxState } from '@vegaprotocol/wallet';
import type { VegaTxState } from '../proposals-hooks/use-vega-transaction';
export const useProposalEvent = (transaction: VegaTxState) => {
const client = useApolloClient();
@@ -1,10 +1,7 @@
import { useCallback, useState } from 'react';
import * as Sentry from '@sentry/react';
import {
useVegaWallet,
useVegaTransaction,
determineId,
} from '@vegaprotocol/wallet';
import { useVegaWallet, determineId } from '@vegaprotocol/wallet';
import { useVegaTransaction } from './use-vega-transaction';
import { useProposalEvent } from './use-proposal-event';
import type { ProposalSubmission } from '@vegaprotocol/wallet';
import type { ProposalEventFieldsFragment } from './__generated__/Proposal';
@@ -1,11 +1,12 @@
import { act, renderHook } from '@testing-library/react';
import type { VegaWalletContextShape } from './context';
import { VegaWalletContext } from './context';
import type { VegaWalletContextShape, Transaction } from '@vegaprotocol/wallet';
import { VegaWalletContext, WalletError } from '@vegaprotocol/wallet';
import type { ReactNode } from 'react';
import { initialState, useVegaTransaction } from './use-vega-transaction';
import type { Transaction } from './connectors';
import { WalletError } from './connectors';
import { VegaTxStatus } from './types';
import {
initialState,
useVegaTransaction,
VegaTxStatus,
} from './use-vega-transaction';
const mockPubKey = '0x123';
@@ -4,15 +4,27 @@ import {
WalletClientError,
WalletHttpError,
} from '@vegaprotocol/wallet-client';
import { useVegaWallet } from './use-vega-wallet';
import type { VegaTransactionContentMap } from './vega-transaction-dialog';
import { VegaTransactionDialog } from './vega-transaction-dialog';
import { useVegaWallet, WalletError, ClientErrors } from '@vegaprotocol/wallet';
import type { Transaction } from '@vegaprotocol/wallet';
import type { VegaTransactionContentMap } from '../../components/vega-transaction-dialog';
import { VegaTransactionDialog } from '../../components/vega-transaction-dialog';
import type { Intent } from '@vegaprotocol/ui-toolkit';
import type { Transaction } from './connectors';
import { WalletError } from './connectors';
import { ClientErrors } from './connectors';
import type { VegaTxState } from './types';
import { VegaTxStatus } from './types';
export enum VegaTxStatus {
Default = 'Default',
Requested = 'Requested',
Pending = 'Pending',
Error = 'Error',
Complete = 'Complete',
}
export interface VegaTxState {
status: VegaTxStatus;
error: Error | null;
txHash: string | null;
signature: string | null;
dialogOpen: boolean;
}
export interface DialogProps {
intent?: Intent;
@@ -2,7 +2,7 @@ import { useApolloClient } from '@apollo/client';
import { useCallback, useEffect, useRef } from 'react';
import { VoteEventDocument } from './__generated__/VoteSubsciption';
import type { Subscription } from 'zen-observable-ts';
import type { VegaTxState } from '@vegaprotocol/wallet';
import type { VegaTxState } from '../proposals-hooks/use-vega-transaction';
import type {
VoteEventFieldsFragment,
VoteEventSubscription,
@@ -1,6 +1,7 @@
import { useCallback, useState } from 'react';
import * as Sentry from '@sentry/react';
import { useVegaTransaction, useVegaWallet } from '@vegaprotocol/wallet';
import { useVegaTransaction } from '../proposals-hooks/use-vega-transaction';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useVoteEvent } from './use-vote-event';
import type { VoteValue } from '@vegaprotocol/types';
import type { VoteEventFieldsFragment } from './__generated__/VoteSubsciption';
@@ -4,6 +4,8 @@ import classNames from 'classnames';
import type { ReactNode } from 'react';
type CheckedState = boolean | 'indeterminate';
export const labelClasses =
"relative before:content-[''] before:block before:absolute before:top-1/2 before:left-[0] before:right-[0] before:-translate-y-1/2 before:h-6";
export interface CheckboxProps {
checked?: CheckedState;
label?: ReactNode;
@@ -30,7 +32,7 @@ export const Checkbox = ({
);
return (
<div className="flex gap-1">
<label className={`flex gap-1 ${labelClasses}`} htmlFor={name}>
<CheckboxPrimitive.Root
name={name}
id={name}
@@ -55,14 +57,13 @@ export const Checkbox = ({
)}
</CheckboxPrimitive.CheckboxIndicator>
</CheckboxPrimitive.Root>
<label
htmlFor={name}
<span
className={classNames('text-sm flex-1', {
'dark:text-neutral-400 text-neutral-600': disabled,
})}
>
{label}
</label>
</div>
</span>
</label>
);
};
@@ -2,6 +2,7 @@ import { forwardRef } from 'react';
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import { labelClasses } from '../checkbox';
export interface RadioGroupProps {
name?: string;
@@ -56,7 +57,7 @@ interface RadioProps {
}
export const Radio = ({ id, value, label, disabled }: RadioProps) => {
const wrapperClasses = classNames('flex items-center gap-2', {
const wrapperClasses = classNames('flex items-center gap-2', labelClasses, {
'opacity-40': disabled,
});
const itemClasses = classNames(
@@ -71,7 +72,7 @@ export const Radio = ({ id, value, label, disabled }: RadioProps) => {
'border-black dark:border-white'
);
return (
<div className={wrapperClasses}>
<label className={wrapperClasses} htmlFor={id}>
<RadioGroupPrimitive.Item
value={value}
className={itemClasses}
@@ -81,9 +82,7 @@ export const Radio = ({ id, value, label, disabled }: RadioProps) => {
>
<RadioGroupPrimitive.Indicator className={indicatorClasses} />
</RadioGroupPrimitive.Item>
<label htmlFor={id} className={disabled ? '' : 'cursor-pointer'}>
{label}
</label>
</div>
<span className={disabled ? '' : 'cursor-pointer'}>{label}</span>
</label>
);
};
@@ -2,6 +2,7 @@ import { VegaIcon, VegaIconNames } from '../icon';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import { labelClasses } from '../checkbox';
type CheckedState = boolean | 'indeterminate';
export interface TradingCheckboxProps {
@@ -29,7 +30,10 @@ export const TradingCheckbox = ({
);
return (
<div className="flex gap-1.5 items-center">
<label
htmlFor={name}
className={`flex gap-1.5 items-center ${labelClasses}`}
>
<CheckboxPrimitive.Root
name={name}
id={name}
@@ -50,14 +54,13 @@ export const TradingCheckbox = ({
)}
</CheckboxPrimitive.CheckboxIndicator>
</CheckboxPrimitive.Root>
<label
htmlFor={name}
<span
className={classNames('text-xs flex-1', {
'text-vega-clight-200 dark:text-vega-cdark-200': disabled,
})}
>
{label}
</label>
</div>
</span>
</label>
);
};
@@ -2,6 +2,7 @@ import { forwardRef } from 'react';
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import { labelClasses } from '../checkbox';
export interface TradingRadioGroupProps {
name?: string;
@@ -59,7 +60,10 @@ interface RadioProps {
}
export const TradingRadio = ({ id, value, label, disabled }: RadioProps) => {
const wrapperClasses = classNames('flex items-center gap-1.5 text-xs');
const wrapperClasses = classNames(
'flex items-center gap-1.5 text-xs',
labelClasses
);
const itemClasses = classNames(
'flex justify-center items-center',
'w-3 h-3 rounded-full border',
@@ -74,7 +78,7 @@ export const TradingRadio = ({ id, value, label, disabled }: RadioProps) => {
'border-vega-clight-700 dark:border-vega-cdark-700'
);
return (
<div className={wrapperClasses}>
<label className={wrapperClasses} htmlFor={id}>
<RadioGroupPrimitive.Item
value={value}
className={itemClasses}
@@ -84,8 +88,7 @@ export const TradingRadio = ({ id, value, label, disabled }: RadioProps) => {
>
<RadioGroupPrimitive.Indicator className={indicatorClasses} />
</RadioGroupPrimitive.Item>
<label
htmlFor={id}
<span
className={
disabled
? 'text-vega-clight-200 dark:text-vega-cdark-200'
@@ -93,7 +96,7 @@ export const TradingRadio = ({ id, value, label, disabled }: RadioProps) => {
}
>
{label}
</label>
</div>
</span>
</label>
);
};
@@ -1,5 +0,0 @@
query ChainId {
statistics {
chainId
}
}
-45
View File
@@ -1,45 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ChainIdQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ChainIdQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string } };
export const ChainIdDocument = gql`
query ChainId {
statistics {
chainId
}
}
`;
/**
* __useChainIdQuery__
*
* To run a query within a React component, call `useChainIdQuery` and pass it any options that fit your needs.
* When your component renders, `useChainIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useChainIdQuery({
* variables: {
* },
* });
*/
export function useChainIdQuery(baseOptions?: Apollo.QueryHookOptions<ChainIdQuery, ChainIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ChainIdQuery, ChainIdQueryVariables>(ChainIdDocument, options);
}
export function useChainIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ChainIdQuery, ChainIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ChainIdQuery, ChainIdQueryVariables>(ChainIdDocument, options);
}
export type ChainIdQueryHookResult = ReturnType<typeof useChainIdQuery>;
export type ChainIdLazyQueryHookResult = ReturnType<typeof useChainIdLazyQuery>;
export type ChainIdQueryResult = Apollo.QueryResult<ChainIdQuery, ChainIdQueryVariables>;
@@ -1,16 +0,0 @@
import type { ChainIdQuery } from './__generated__/ChainId';
import merge from 'lodash/merge';
import type { PartialDeep } from 'type-fest';
export const chainIdQuery = (
override?: PartialDeep<ChainIdQuery>
): ChainIdQuery => {
const defaultResult = {
statistics: {
__typename: 'Statistics',
chainId: 'test-id',
},
};
return merge(defaultResult, override);
};
@@ -1,6 +1,4 @@
import { act, fireEvent, render, screen } from '@testing-library/react';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type { VegaWalletConfig } from '../provider';
import { VegaWalletProvider } from '../provider';
import { VegaConnectDialog, CLOSE_DELAY } from './connect-dialog';
@@ -13,8 +11,6 @@ import {
ViewConnector,
WalletError,
} from '../connectors';
import type { ChainIdQuery } from './__generated__/ChainId';
import { ChainIdDocument } from './__generated__/ChainId';
import {
mockBrowserWallet,
clearBrowserWallet,
@@ -26,6 +22,7 @@ const mockUpdateDialogOpen = jest.fn();
const mockCloseVegaDialog = jest.fn();
let mockIsDesktopRunning = true;
const mockChainId = 'chain-id';
jest.mock('../use-is-wallet-service-running', () => ({
useIsWalletServiceRunning: jest
@@ -33,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';
@@ -60,8 +61,6 @@ beforeEach(() => {
});
});
const mockChainId = 'chain-id';
const defaultConfig: VegaWalletConfig = {
network: 'TESTNET',
vegaUrl: 'https://vega.xyz',
@@ -78,24 +77,10 @@ function generateJSX(
props?: Partial<VegaConnectDialogProps>,
config?: Partial<VegaWalletConfig>
) {
const chainIdMock: MockedResponse<ChainIdQuery> = {
request: {
query: ChainIdDocument,
},
result: {
data: {
statistics: {
chainId: mockChainId,
},
},
},
};
return (
<MockedProvider mocks={[chainIdMock]}>
<VegaWalletProvider config={{ ...defaultConfig, ...config }}>
<VegaConnectDialog {...defaultProps} {...props} />
</VegaWalletProvider>
</MockedProvider>
<VegaWalletProvider config={{ ...defaultConfig, ...config }}>
<VegaConnectDialog {...defaultProps} {...props} />
</VegaWalletProvider>
);
}
@@ -35,13 +35,13 @@ import type { Status as JsonRpcStatus } from '../use-json-rpc-connect';
import { useJsonRpcConnect } from '../use-json-rpc-connect';
import type { Status as InjectedStatus } from '../use-injected-connector';
import { useInjectedConnector } from '../use-injected-connector';
import { useChainIdQuery } from './__generated__/ChainId';
import { useVegaWallet } from '../use-vega-wallet';
import { InjectedConnectorForm } from './injected-connector-form';
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';
export const CLOSE_DELAY = 1700;
@@ -82,12 +82,12 @@ export const VegaConnectDialog = ({
// 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 { data } = useChainIdQuery();
const chainId = useChainId();
const content = data && (
const content = chainId && (
<ConnectDialogContainer
connectors={connectors}
appChainId={data.statistics.chainId}
appChainId={chainId}
riskMessage={riskMessage}
onClose={onClose}
/>
@@ -0,0 +1,37 @@
import { useEffect, useState } from 'react';
import { useVegaWallet } from '../use-vega-wallet';
const cache: Record<string, string> = {};
export const useChainId = () => {
const { vegaUrl } = useVegaWallet();
const [chainId, setChainId] = useState<undefined | string>(cache[vegaUrl]);
const [fetchAttempt, setFetchAttempt] = useState(1);
useEffect(() => {
let isCancelled = false;
if (cache[vegaUrl]) {
setChainId(cache[vegaUrl]);
return;
}
fetch(vegaUrl.replace('graphql', 'statistics'))
.then((response) => response.json())
.then((response) => {
if (isCancelled) {
return;
}
if (!response?.statistics?.chainId) {
throw new Error('statistics.chainId not present in fetched response');
}
setChainId(response?.statistics?.chainId);
})
.catch(() => {
if (fetchAttempt < 3) {
setFetchAttempt((value) => (value += 1));
}
});
return () => {
isCancelled = true;
};
}, [fetchAttempt, vegaUrl]);
return chainId;
};
-9
View File
@@ -1,18 +1,9 @@
export * from './connectors';
export * from './context';
export * from './use-vega-transaction';
export * from './use-vega-wallet';
export * from './use-vega-transaction-manager';
export * from './use-vega-transaction-store';
export * from './use-vega-transaction-updater';
export * from './use-transaction-result';
export * from './use-eager-connect';
export * from './manage-dialog';
export * from './provider';
export * from './connect-dialog';
export * from './utils';
export * from './storage';
export * from './types';
export * from './vega-transaction-dialog';
export * from './__generated__/TransactionResult';
export * from './__generated__/WithdrawalApproval';
+7
View File
@@ -28,3 +28,10 @@ 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';
+74
View File
@@ -0,0 +1,74 @@
query OrderById($orderId: ID!) {
orderByID(id: $orderId) {
id
market {
id
}
type
side
size
status
rejectionReason
price
timeInForce
remaining
expiresAt
createdAt
updatedAt
postOnly
reduceOnly
liquidityProvision {
__typename
}
peggedOrder {
__typename
reference
offset
}
icebergOrder {
__typename
peakSize
minimumVisibleSize
reservedRemaining
}
}
}
query StopOrderById($stopOrderId: ID!) {
stopOrder(id: $stopOrderId) {
id
ocoLinkId
expiresAt
expiryStrategy
triggerDirection
status
createdAt
updatedAt
partyId
marketId
trigger {
... on StopOrderPrice {
price
}
... on StopOrderTrailingPercentOffset {
trailingPercentOffset
}
}
submission {
marketId
price
size
side
timeInForce
expiresAt
type
reference
peggedOrder {
reference
offset
}
postOnly
reduceOnly
}
}
}
+153
View File
@@ -0,0 +1,153 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type OrderByIdQueryVariables = Types.Exact<{
orderId: Types.Scalars['ID'];
}>;
export type OrderByIdQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } };
export type StopOrderByIdQueryVariables = Types.Exact<{
stopOrderId: Types.Scalars['ID'];
}>;
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
export const OrderByIdDocument = gql`
query OrderById($orderId: ID!) {
orderByID(id: $orderId) {
id
market {
id
}
type
side
size
status
rejectionReason
price
timeInForce
remaining
expiresAt
createdAt
updatedAt
postOnly
reduceOnly
liquidityProvision {
__typename
}
peggedOrder {
__typename
reference
offset
}
icebergOrder {
__typename
peakSize
minimumVisibleSize
reservedRemaining
}
}
}
`;
/**
* __useOrderByIdQuery__
*
* To run a query within a React component, call `useOrderByIdQuery` and pass it any options that fit your needs.
* When your component renders, `useOrderByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useOrderByIdQuery({
* variables: {
* orderId: // value for 'orderId'
* },
* });
*/
export function useOrderByIdQuery(baseOptions: Apollo.QueryHookOptions<OrderByIdQuery, OrderByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<OrderByIdQuery, OrderByIdQueryVariables>(OrderByIdDocument, options);
}
export function useOrderByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<OrderByIdQuery, OrderByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<OrderByIdQuery, OrderByIdQueryVariables>(OrderByIdDocument, options);
}
export type OrderByIdQueryHookResult = ReturnType<typeof useOrderByIdQuery>;
export type OrderByIdLazyQueryHookResult = ReturnType<typeof useOrderByIdLazyQuery>;
export type OrderByIdQueryResult = Apollo.QueryResult<OrderByIdQuery, OrderByIdQueryVariables>;
export const StopOrderByIdDocument = gql`
query StopOrderById($stopOrderId: ID!) {
stopOrder(id: $stopOrderId) {
id
ocoLinkId
expiresAt
expiryStrategy
triggerDirection
status
createdAt
updatedAt
partyId
marketId
trigger {
... on StopOrderPrice {
price
}
... on StopOrderTrailingPercentOffset {
trailingPercentOffset
}
}
submission {
marketId
price
size
side
timeInForce
expiresAt
type
reference
peggedOrder {
reference
offset
}
postOnly
reduceOnly
}
}
}
`;
/**
* __useStopOrderByIdQuery__
*
* To run a query within a React component, call `useStopOrderByIdQuery` and pass it any options that fit your needs.
* When your component renders, `useStopOrderByIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useStopOrderByIdQuery({
* variables: {
* stopOrderId: // value for 'stopOrderId'
* },
* });
*/
export function useStopOrderByIdQuery(baseOptions: Apollo.QueryHookOptions<StopOrderByIdQuery, StopOrderByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<StopOrderByIdQuery, StopOrderByIdQueryVariables>(StopOrderByIdDocument, options);
}
export function useStopOrderByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<StopOrderByIdQuery, StopOrderByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<StopOrderByIdQuery, StopOrderByIdQueryVariables>(StopOrderByIdDocument, options);
}
export type StopOrderByIdQueryHookResult = ReturnType<typeof useStopOrderByIdQuery>;
export type StopOrderByIdLazyQueryHookResult = ReturnType<typeof useStopOrderByIdLazyQuery>;
export type StopOrderByIdQueryResult = Apollo.QueryResult<StopOrderByIdQuery, StopOrderByIdQueryVariables>;
+4
View File
@@ -0,0 +1,4 @@
import '@testing-library/jest-dom';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;
@@ -7,7 +7,7 @@ import type { Token } from '@vegaprotocol/smart-contracts';
import type {
DepositBusEventFieldsFragment,
WithdrawalBusEventFieldsFragment,
} from '@vegaprotocol/wallet';
} from './__generated__/TransactionResult';
import type { EthTxState } from './use-ethereum-transaction';
import { EthTxStatus } from './use-ethereum-transaction';
@@ -3,15 +3,13 @@ import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type { ReactNode } from 'react';
import { useEthTransactionUpdater } from './use-ethereum-transaction-updater';
import {
DepositBusEventDocument,
VegaWalletContext,
} from '@vegaprotocol/wallet';
import { DepositBusEventDocument } from './__generated__/TransactionResult';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import type {
DepositBusEventSubscription,
DepositBusEventFieldsFragment,
VegaWalletContextShape,
} from '@vegaprotocol/wallet';
} from './__generated__/TransactionResult';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import type { EthTransactionStore } from './use-ethereum-transaction-store';
import { DepositStatus } from '@vegaprotocol/types';
@@ -1,8 +1,6 @@
import { DepositStatus } from '@vegaprotocol/types';
import {
useDepositBusEventSubscription,
useVegaWallet,
} from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useDepositBusEventSubscription } from './__generated__/TransactionResult';
import { useEthTransactionStore } from './use-ethereum-transaction-store';
export const useEthTransactionUpdater = () => {
@@ -16,8 +16,8 @@ import type {
} from './use-ethereum-withdraw-approvals-store';
import type { EthTransactionStore } from './use-ethereum-transaction-store';
import { WithdrawalApprovalDocument } from '@vegaprotocol/wallet';
import type { WithdrawalApprovalQuery } from '@vegaprotocol/wallet';
import { WithdrawalApprovalDocument } from './__generated__/WithdrawalApproval';
import type { WithdrawalApprovalQuery } from './__generated__/WithdrawalApproval';
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
@@ -15,9 +15,9 @@ import { useWeb3React } from '@web3-react/core';
import type {
WithdrawalApprovalQuery,
WithdrawalApprovalQueryVariables,
} from '@vegaprotocol/wallet';
} from './__generated__/WithdrawalApproval';
import { WithdrawalApprovalDocument } from '@vegaprotocol/wallet';
import { WithdrawalApprovalDocument } from './__generated__/WithdrawalApproval';
import { useEthTransactionStore } from './use-ethereum-transaction-store';
import {
@@ -1,12 +1,12 @@
import { useEthWithdrawApprovalsStore } from './use-ethereum-withdraw-approvals-store';
import type { VegaStoredTxState } from '@vegaprotocol/wallet';
import type { VegaStoredTxState } from './use-vega-transaction-store';
import { ApprovalStatus } from './use-ethereum-withdraw-approvals-store';
import type { EthWithdrawalApprovalState } from './use-ethereum-withdraw-approvals-store';
const mockFindVegaTransaction = jest.fn<VegaStoredTxState, []>();
const mockDismissVegaTransaction = jest.fn();
jest.mock('@vegaprotocol/wallet', () => ({
jest.mock('./use-vega-transaction-store', () => ({
useVegaTransactionStore: {
getState: () => ({
transactions: {
@@ -1,10 +1,10 @@
import { create } from 'zustand';
import produce from 'immer';
import type BigNumber from 'bignumber.js';
import type { WithdrawalBusEventFieldsFragment } from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
import type { WithdrawalBusEventFieldsFragment } from './__generated__/TransactionResult';
import { useVegaTransactionStore } from './use-vega-transaction-store';
import type { WithdrawalApprovalQuery } from '@vegaprotocol/wallet';
import type { WithdrawalApprovalQuery } from './__generated__/WithdrawalApproval';
import { subscribeWithSelector } from 'zustand/middleware';
export enum ApprovalStatus {
@@ -2,7 +2,7 @@ import { useCallback } from 'react';
import { useBridgeContract } from './use-bridge-contract';
import BigNumber from 'bignumber.js';
import { addDecimal } from '@vegaprotocol/utils';
import type { WithdrawalBusEventFieldsFragment } from '@vegaprotocol/wallet';
import type { WithdrawalBusEventFieldsFragment } from './__generated__/TransactionResult';
import { localLoggerFactory } from '@vegaprotocol/logger';
type Asset = Pick<
@@ -1,19 +1,20 @@
import { useVegaTransactionManager } from './use-vega-transaction-manager';
import { renderHook } from '@testing-library/react';
import waitForNextTick from 'flush-promises';
import type { TransactionResponse } from './connectors/vega-connector';
import type { TransactionResponse } from '@vegaprotocol/wallet';
import { VegaTxStatus } from './types';
import type {
VegaTransactionStore,
VegaStoredTxState,
} from './use-vega-transaction-store';
const mockSendTx = jest.fn<Promise<Partial<TransactionResponse> | null>, []>();
const mockSendTx = jest.fn();
const pubKey = 'pubKey';
const mockDisconnect = jest.fn();
jest.mock('./use-vega-wallet', () => ({
jest.mock('@vegaprotocol/wallet', () => ({
...jest.requireActual('@vegaprotocol/wallet'),
useVegaWallet: () => ({
sendTx: mockSendTx,
pubKey,
@@ -1,7 +1,5 @@
import { useVegaWallet } from './use-vega-wallet';
import { useVegaWallet, WalletError, ClientErrors } from '@vegaprotocol/wallet';
import { useEffect, useRef } from 'react';
import { WalletError } from './connectors';
import { ClientErrors } from './connectors';
import { useVegaTransactionStore } from './use-vega-transaction-store';
import {
WalletClientError,
@@ -4,7 +4,7 @@ import type {
OrderAmendmentBody,
OrderCancellationBody,
WithdrawSubmissionBody,
} from './connectors/vega-connector';
} from '@vegaprotocol/wallet';
import {
OrderStatus,
OrderTimeInForce,
@@ -13,8 +13,8 @@ import {
} from '@vegaprotocol/types';
import { VegaTxStatus } from './types';
jest.mock('./utils', () => ({
...jest.requireActual('./utils'),
jest.mock('@vegaprotocol/wallet', () => ({
...jest.requireActual('@vegaprotocol/wallet'),
determineId: jest.fn((v) => v),
}));
@@ -1,5 +1,5 @@
import produce from 'immer';
import type { Transaction } from './connectors';
import type { Transaction } from '@vegaprotocol/wallet';
import {
isWithdrawTransaction,
isOrderSubmissionTransaction,
@@ -9,8 +9,8 @@ import {
isTransferTransaction,
isStopOrdersSubmissionTransaction,
isStopOrdersCancellationTransaction,
} from './connectors';
import { determineId } from './utils';
determineId,
} from '@vegaprotocol/wallet';
import { create } from 'zustand';
import type {
@@ -1,18 +1,26 @@
import { render } from '@testing-library/react';
import * as Types from '@vegaprotocol/types';
import {
OrderStatus,
OrderTimeInForce,
OrderType,
Side,
} from '@vegaprotocol/types';
import type { VegaStoredTxState } from '@vegaprotocol/wallet';
import { VegaTxStatus } from '@vegaprotocol/wallet';
import {
VegaTransactionDetails,
getVegaTransactionContentIntent,
getOrderToastIntent,
getOrderToastTitle,
getRejectionReason,
} from './use-vega-transaction-toasts';
import { Intent } from '@vegaprotocol/ui-toolkit';
import type { OrderByIdQuery, StopOrderByIdQuery } from '@vegaprotocol/orders';
import type {
OrderByIdQuery,
StopOrderByIdQuery,
} from './__generated__/Orders';
import type { VegaStoredTxState } from './use-vega-transaction-store';
import { VegaTxStatus } from './types';
jest.mock('@vegaprotocol/assets', () => {
const A1 = {
@@ -56,9 +64,8 @@ jest.mock('@vegaprotocol/markets', () => {
};
});
jest.mock('@vegaprotocol/orders', () => {
jest.mock('./__generated__/Orders', () => {
return {
...jest.requireActual('@vegaprotocol/orders'),
useOrderByIdQuery: jest.fn(({ variables: { orderId } }) => {
if (orderId === '0') {
const data: OrderByIdQuery = {
@@ -408,3 +415,99 @@ describe('getVegaTransactionContentIntent', () => {
expect(getVegaTransactionContentIntent(batch).intent).toBe(Intent.Primary);
});
});
describe('getOrderToastTitle', () => {
it('should return the correct title', () => {
expect(getOrderToastTitle(Types.OrderStatus.STATUS_ACTIVE)).toBe(
'Order submitted'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_FILLED)).toBe(
'Order filled'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_PARTIALLY_FILLED)).toBe(
'Order partially filled'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_PARKED)).toBe(
'Order parked'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_STOPPED)).toBe(
'Order stopped'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_CANCELLED)).toBe(
'Order cancelled'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_EXPIRED)).toBe(
'Order expired'
);
expect(getOrderToastTitle(Types.OrderStatus.STATUS_REJECTED)).toBe(
'Order rejected'
);
expect(getOrderToastTitle(undefined)).toBe(undefined);
});
});
describe('getOrderToastIntent', () => {
it('should return the correct intent', () => {
expect(getOrderToastIntent(Types.OrderStatus.STATUS_PARKED)).toBe(
Intent.Warning
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_EXPIRED)).toBe(
Intent.Warning
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_PARTIALLY_FILLED)).toBe(
Intent.Warning
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_REJECTED)).toBe(
Intent.Danger
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_STOPPED)).toBe(
Intent.Warning
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_FILLED)).toBe(
Intent.Success
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_ACTIVE)).toBe(
Intent.Success
);
expect(getOrderToastIntent(Types.OrderStatus.STATUS_CANCELLED)).toBe(
Intent.Success
);
expect(getOrderToastIntent(undefined)).toBe(undefined);
});
});
describe('getRejectionReason', () => {
it('should return the correct rejection reason for insufficient asset balance', () => {
expect(
getRejectionReason({
rejectionReason:
Types.OrderRejectionReason.ORDER_ERROR_INSUFFICIENT_ASSET_BALANCE,
status: Types.OrderStatus.STATUS_REJECTED,
id: '',
createdAt: undefined,
size: '',
price: '',
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
side: Types.Side.SIDE_BUY,
marketId: '',
})
).toBe('Insufficient asset balance');
});
it('should return the correct rejection reason when order is stopped', () => {
expect(
getRejectionReason({
rejectionReason: null,
status: Types.OrderStatus.STATUS_STOPPED,
id: '',
createdAt: undefined,
size: '',
price: '',
timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK,
side: Types.Side.SIDE_BUY,
marketId: '',
})
).toBe(
'Your Fill or Kill (FOK) order was not filled and it has been stopped'
);
});
});
@@ -4,13 +4,15 @@ import compact from 'lodash/compact';
import type {
BatchMarketInstructionSubmissionBody,
OrderAmendment,
OrderTxUpdateFieldsFragment,
OrderSubmission,
VegaStoredTxState,
WithdrawalBusEventFieldsFragment,
StopOrdersSubmission,
StopOrderSetup,
} from '@vegaprotocol/wallet';
import type {
OrderTxUpdateFieldsFragment,
WithdrawalBusEventFieldsFragment,
} from './__generated__/TransactionResult';
import type { VegaStoredTxState } from './use-vega-transaction-store';
import {
isTransferTransaction,
isBatchMarketInstructionsTransaction,
@@ -21,12 +23,12 @@ import {
isOrderCancellationTransaction,
isOrderSubmissionTransaction,
isWithdrawTransaction,
useVegaTransactionStore,
VegaTxStatus,
isStopOrdersSubmissionTransaction,
isStopOrdersCancellationTransaction,
isReferralRelatedTransaction,
} from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from './use-vega-transaction-store';
import { VegaTxStatus } from './types';
import type { Toast, ToastContent } from '@vegaprotocol/ui-toolkit';
import { ToastHeading } from '@vegaprotocol/ui-toolkit';
import { Panel } from '@vegaprotocol/ui-toolkit';
@@ -45,12 +47,9 @@ import { useAssetsMapProvider } from '@vegaprotocol/assets';
import { useEthWithdrawApprovalsStore } from './use-ethereum-withdraw-approvals-store';
import { DApp, EXPLORER_TX, useLinks } from '@vegaprotocol/environment';
import {
getOrderToastIntent,
getOrderToastTitle,
getRejectionReason,
useOrderByIdQuery,
useStopOrderByIdQuery,
} from '@vegaprotocol/orders';
} from './__generated__/Orders';
import { getAsset, useMarketsMapProvider } from '@vegaprotocol/markets';
import type { Market } from '@vegaprotocol/markets';
import type { Side } from '@vegaprotocol/types';
@@ -59,6 +58,75 @@ import { Size } from '@vegaprotocol/datagrid';
import { useWithdrawalApprovalDialog } from './withdrawal-approval-dialog';
import * as Schema from '@vegaprotocol/types';
export const getRejectionReason = (
order: OrderTxUpdateFieldsFragment
): string | null => {
switch (order.status) {
case Schema.OrderStatus.STATUS_STOPPED:
return t(
`Your ${
Schema.OrderTimeInForceMapping[order.timeInForce]
} order was not filled and it has been stopped`
);
default:
return order.rejectionReason
? t(Schema.OrderRejectionReasonMapping[order.rejectionReason])
: '';
}
};
export const getOrderToastTitle = (
status?: Schema.OrderStatus
): string | undefined => {
if (!status) {
return;
}
switch (status) {
case Schema.OrderStatus.STATUS_ACTIVE:
return t('Order submitted');
case Schema.OrderStatus.STATUS_FILLED:
return t('Order filled');
case Schema.OrderStatus.STATUS_PARTIALLY_FILLED:
return t('Order partially filled');
case Schema.OrderStatus.STATUS_PARKED:
return t('Order parked');
case Schema.OrderStatus.STATUS_STOPPED:
return t('Order stopped');
case Schema.OrderStatus.STATUS_CANCELLED:
return t('Order cancelled');
case Schema.OrderStatus.STATUS_EXPIRED:
return t('Order expired');
case Schema.OrderStatus.STATUS_REJECTED:
return t('Order rejected');
default:
return t('Submission failed');
}
};
export const getOrderToastIntent = (
status?: Schema.OrderStatus
): Intent | undefined => {
if (!status) {
return;
}
switch (status) {
case Schema.OrderStatus.STATUS_PARKED:
case Schema.OrderStatus.STATUS_EXPIRED:
case Schema.OrderStatus.STATUS_PARTIALLY_FILLED:
case Schema.OrderStatus.STATUS_STOPPED:
return Intent.Warning;
case Schema.OrderStatus.STATUS_REJECTED:
return Intent.Danger;
case Schema.OrderStatus.STATUS_FILLED:
case Schema.OrderStatus.STATUS_ACTIVE:
case Schema.OrderStatus.STATUS_CANCELLED:
return Intent.Success;
default:
return;
}
};
const intentMap: { [s in VegaTxStatus]: Intent } = {
Default: Intent.Primary,
Requested: Intent.Warning,
@@ -38,7 +38,7 @@ const render = (mocks?: MockedResponse[]) => {
const pubKey = 'pubKey';
jest.mock('./use-vega-wallet', () => ({
jest.mock('@vegaprotocol/wallet', () => ({
useVegaWallet: () => ({
pubKey,
}),
@@ -1,5 +1,5 @@
import { useApolloClient } from '@apollo/client';
import { useVegaWallet } from './use-vega-wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
useOrderTxUpdateSubscription,
useWithdrawalBusEventSubscription,
@@ -10,7 +10,7 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { useWithdrawalApprovalQuery } from '@vegaprotocol/wallet';
import { useWithdrawalApprovalQuery } from './__generated__/WithdrawalApproval';
import omit from 'lodash/omit';
import { create } from 'zustand';
-2
View File
@@ -1,8 +1,6 @@
export * from './lib/__generated__/Erc20Approval';
export * from './lib/__generated__/Withdrawal';
export * from './lib/create-withdrawal-dialog';
export * from './lib/use-complete-withdraw';
export * from './lib/use-create-withdraw';
export * from './lib/use-verify-withdrawal';
export * from './lib/withdraw-form-container';
export * from './lib/withdraw-form';
@@ -4,7 +4,7 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
import { WithdrawFormContainer } from './withdraw-form-container';
import { useWeb3ConnectStore } from '@vegaprotocol/web3';
import { useWithdrawalDialog } from './withdrawal-dialog';
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
import { useVegaTransactionStore } from '@vegaprotocol/web3';
export const CreateWithdrawalDialog = () => {
const { assetId, isOpen, open, close } = useWithdrawalDialog();
@@ -1,192 +0,0 @@
import { act, renderHook } from '@testing-library/react';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type { ReactNode } from 'react';
import type { WithdrawalArgs } from './use-create-withdraw';
import { useCreateWithdraw } from './use-create-withdraw';
import { Erc20ApprovalDocument } from './__generated__/Erc20Approval';
import type { Erc20ApprovalQuery } from './__generated__/Erc20Approval';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import {
initialState,
VegaTxStatus,
VegaWalletContext,
determineId,
} from '@vegaprotocol/wallet';
import { waitFor } from '@testing-library/react';
import type {
WithdrawalEventSubscription,
WithdrawalFieldsFragment,
} from './__generated__/Withdrawal';
import { WithdrawalEventDocument } from './__generated__/Withdrawal';
import * as Schema from '@vegaprotocol/types';
function setup(
vegaWalletContext: Partial<VegaWalletContextShape>,
mocks?: MockedResponse[]
) {
const wrapper = ({ children }: { children: ReactNode }) => (
<MockedProvider mocks={mocks}>
<VegaWalletContext.Provider
value={vegaWalletContext as VegaWalletContextShape}
>
{children}
</VegaWalletContext.Provider>
</MockedProvider>
);
return renderHook(() => useCreateWithdraw(), { wrapper });
}
const txHash = 'tx-hash';
const signature =
'cfe592d169f87d0671dd447751036d0dddc165b9c4b65e5a5060e2bbadd1aa726d4cbe9d3c3b327bcb0bff4f83999592619a2493f9bbd251fae99ce7ce766909';
const derivedWithdrawalId = determineId(signature);
beforeAll(() => {
jest.useFakeTimers();
});
afterAll(() => {
jest.useRealTimers();
});
const pubKey = '0x123';
let mockSend: jest.Mock;
let withdrawalInput: WithdrawalArgs;
let withdrawalEvent: WithdrawalFieldsFragment;
let mockERC20Approval: MockedResponse<Erc20ApprovalQuery>;
let mockWithdrawalEvent: MockedResponse<WithdrawalEventSubscription>;
beforeEach(() => {
mockSend = jest
.fn()
.mockReturnValue(Promise.resolve({ transactionHash: txHash, signature }));
withdrawalEvent = {
id: '2fca514cebf9f465ae31ecb4c5721e3a6f5f260425ded887ca50ba15b81a5d50',
status: Schema.WithdrawalStatus.STATUS_OPEN,
amount: '100',
asset: {
__typename: 'Asset',
id: 'asset-id',
name: 'asset-name',
symbol: 'asset-symbol',
decimals: 2,
status: Schema.AssetStatus.STATUS_ENABLED,
source: {
__typename: 'ERC20',
contractAddress: '0x123',
},
},
createdTimestamp: '2022-07-05T14:25:47.815283706Z',
withdrawnTimestamp: '2022-07-05T14:25:47.815283706Z',
txHash: '0x123',
details: {
__typename: 'Erc20WithdrawalDetails',
receiverAddress: '0x123',
},
pendingOnForeignChain: false,
__typename: 'Withdrawal',
};
withdrawalInput = {
amount: '100',
asset: 'asset-id',
receiverAddress: 'receiver-address',
availableTimestamp: null,
};
mockERC20Approval = {
request: {
query: Erc20ApprovalDocument,
variables: { withdrawalId: derivedWithdrawalId },
},
result: {
data: {
erc20WithdrawalApproval: {
__typename: 'Erc20WithdrawalApproval',
assetSource: 'asset-source',
amount: '100',
nonce: '1',
signatures: 'signatures',
targetAddress: 'targetAddress',
creation: '1',
},
},
},
delay: 2000,
};
mockWithdrawalEvent = {
request: {
query: WithdrawalEventDocument,
variables: { partyId: pubKey },
},
result: {
data: {
busEvents: [
{
event: withdrawalEvent,
__typename: 'BusEvent',
},
],
},
},
delay: 1000,
};
});
it('creates withdrawal and waits for approval creation', async () => {
const { result } = setup({ sendTx: mockSend, pubKey }, [
mockWithdrawalEvent,
mockERC20Approval,
]);
expect(result.current.transaction).toEqual(initialState);
expect(result.current.submit).toEqual(expect.any(Function));
expect(result.current.reset).toEqual(expect.any(Function));
expect(result.current.approval).toEqual(null);
act(() => {
result.current.submit(withdrawalInput);
});
expect(mockSend).toHaveBeenCalledWith(pubKey, {
withdrawSubmission: {
amount: withdrawalInput.amount,
asset: withdrawalInput.asset,
ext: {
erc20: {
receiverAddress: withdrawalInput.receiverAddress,
},
},
},
});
expect(result.current.transaction.status).toEqual(VegaTxStatus.Requested);
await waitFor(() => {
expect(result.current.transaction.status).toEqual(VegaTxStatus.Pending);
expect(result.current.transaction.dialogOpen).toBe(true);
// Withdrawal event should not be found yet
expect(result.current.withdrawal).toEqual(null);
// Poll for erc20Approval should not be complete yet
expect(result.current.approval).toEqual(null);
});
await act(async () => {
// Advance time by delay plus interval length to ensure mock result is triggered
// eslint-disable-next-line
jest.advanceTimersByTime(mockWithdrawalEvent.delay! + 1000);
});
expect(result.current.withdrawal).toEqual(withdrawalEvent);
await act(async () => {
// Advance time by delay plus interval length to ensure mock result is triggered
// eslint-disable-next-line
jest.advanceTimersByTime(mockERC20Approval.delay! + 1000);
});
expect(result.current.transaction.status).toEqual(VegaTxStatus.Complete);
expect(result.current.approval).toEqual(
// @ts-ignore MockedRespones types inteferring
mockERC20Approval.result.data.erc20WithdrawalApproval
);
});
@@ -1,79 +0,0 @@
import {
useVegaTransaction,
useVegaWallet,
determineId,
} from '@vegaprotocol/wallet';
import { useCallback, useState } from 'react';
import { useWithdrawalApproval } from './use-withdrawal-approval';
import { useWithdrawalEvent } from './use-withdrawal-event';
import type { Erc20ApprovalQuery } from './__generated__/Erc20Approval';
import type { WithdrawalFieldsFragment } from './__generated__/Withdrawal';
export interface WithdrawalArgs {
amount: string;
asset: string;
receiverAddress: string;
availableTimestamp: number | null;
}
export const useCreateWithdraw = () => {
const waitForWithdrawalApproval = useWithdrawalApproval();
const [approval, setApproval] = useState<
Erc20ApprovalQuery['erc20WithdrawalApproval'] | null
>(null);
const [withdrawal, setWithdrawal] = useState<WithdrawalFieldsFragment | null>(
null
);
const [availableTimestamp, setAvailableTimestamp] = useState<number | null>(
null
);
const { pubKey } = useVegaWallet();
const { transaction, send, setComplete, reset, Dialog } =
useVegaTransaction();
const waitForWithdrawal = useWithdrawalEvent(transaction);
const submit = useCallback(
async (withdrawal: WithdrawalArgs) => {
if (!pubKey) {
return;
}
setAvailableTimestamp(withdrawal.availableTimestamp);
const res = await send(pubKey, {
withdrawSubmission: {
amount: withdrawal.amount,
asset: withdrawal.asset,
ext: {
erc20: {
receiverAddress: withdrawal.receiverAddress,
},
},
},
});
if (res) {
const withdrawalId = determineId(res.signature);
const withdrawal = await waitForWithdrawal(withdrawalId, pubKey);
setWithdrawal(withdrawal);
const approval = await waitForWithdrawalApproval(withdrawal.id);
setApproval(approval);
setComplete();
}
},
[pubKey, send, waitForWithdrawal, waitForWithdrawalApproval, setComplete]
);
return {
transaction,
submit,
reset,
approval,
withdrawal,
availableTimestamp,
Dialog,
};
};

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