+ {data.tradableInstrument.instrument.product.settlementAsset.symbol}
+
+ ),
+ },
+ {
+ colId: 'change',
+ headerName: t('24h change'),
+ headerClass: 'uppercase',
+ field: 'percentChange',
+ minWidth: 100,
+ valueSetter: (params: ValueSetterParams): boolean => {
+ const { oldValue, newValue, api, data } = params;
+ if (oldValue !== newValue) {
+ const newdata = { percentChange: newValue, ...data };
+ api.applyTransaction({ update: [newdata] });
+ return true;
+ }
+ return false;
+ },
+ cellRenderer: ({
+ data,
+ setValue,
+ }: {
+ data: SimpleMarketsType;
+ setValue: (arg: unknown) => void;
+ }) => (
+
- Version: {ENV.commit || 'development'}
+ Version: {ENV.commit || 'development'}
{
return React.createElement(
tag,
- { className: 'mt-24 pt-8 pb-20 uppercase text-white', style },
+ {
+ className: 'mt-24 py-8 border-t border-white uppercase text-white',
+ style,
+ },
<>
{children}
diff --git a/apps/token/src/components/connected-vega-key/connected-vega-key.tsx.tsx b/apps/token/src/components/connected-vega-key/connected-vega-key.tsx.tsx
index 376f223e8..90506a9c8 100644
--- a/apps/token/src/components/connected-vega-key/connected-vega-key.tsx.tsx
+++ b/apps/token/src/components/connected-vega-key/connected-vega-key.tsx.tsx
@@ -9,9 +9,7 @@ export const ConnectedVegaKey = ({ pubKey }: { pubKey: string | null }) => {
{pubKey ? t('Connected Vega key') : }
-
- {pubKey}
-
+ {pubKey}
);
};
diff --git a/apps/token/src/components/eth-wallet/eth-wallet.tsx b/apps/token/src/components/eth-wallet/eth-wallet.tsx
index 59d54df21..63e97b8af 100644
--- a/apps/token/src/components/eth-wallet/eth-wallet.tsx
+++ b/apps/token/src/components/eth-wallet/eth-wallet.tsx
@@ -74,19 +74,24 @@ const AssociatedAmounts = ({
light={false}
/>
{vestingAssociationByVegaKey.length ? (
- <>
-
-
+
+
+
{vestingAssociationByVegaKey.map(([key, amount]) => {
return (
);
})}
- >
+
) : null}
>
);
diff --git a/apps/token/src/components/heading/heading.tsx b/apps/token/src/components/heading/heading.tsx
index 4c5b91950..c0e9fddd0 100644
--- a/apps/token/src/components/heading/heading.tsx
+++ b/apps/token/src/components/heading/heading.tsx
@@ -7,9 +7,7 @@ export const Heading = ({ title }: HeadingProps) => {
return (
);
};
diff --git a/apps/token/src/components/heading/index.ts b/apps/token/src/components/heading/index.ts
index 045dc4fd8..3de265cc7 100644
--- a/apps/token/src/components/heading/index.ts
+++ b/apps/token/src/components/heading/index.ts
@@ -1 +1 @@
-export { Heading } from './heading';
+export * from './heading';
diff --git a/apps/token/src/components/locked-progress/locked-progress.tsx b/apps/token/src/components/locked-progress/locked-progress.tsx
index ed9637b8a..2101e48ab 100644
--- a/apps/token/src/components/locked-progress/locked-progress.tsx
+++ b/apps/token/src/components/locked-progress/locked-progress.tsx
@@ -14,7 +14,7 @@ const ProgressContents = ({
children: React.ReactNode;
}) => (
+
{formatNumber(locked, decimals)}
{formatNumber(unlocked, decimals)}
- >
+
);
};
diff --git a/apps/token/src/components/nav/nav.tsx b/apps/token/src/components/nav/nav.tsx
index dda04475e..2278217ff 100644
--- a/apps/token/src/components/nav/nav.tsx
+++ b/apps/token/src/components/nav/nav.tsx
@@ -5,7 +5,8 @@ import debounce from 'lodash/debounce';
import React from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { useTranslation } from 'react-i18next';
-import { NavLink } from 'react-router-dom';
+import { Link, NavLink } from 'react-router-dom';
+import vegaWhite from '../../images/vega_white.png';
import { Flags } from '../../config';
import {
@@ -16,6 +17,69 @@ import { Routes } from '../../routes/router-config';
import { EthWallet } from '../eth-wallet';
import { VegaWallet } from '../vega-wallet';
+const Fish = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
export const Nav = () => {
const [windowWidth, setWindowWidth] = React.useState(window.innerWidth);
const isDesktop = windowWidth > 959;
@@ -35,7 +99,7 @@ export const Nav = () => {
return (
{
{!isDesktop &&
}
{isDesktop ? (
-
+
) : (
)}
@@ -60,15 +124,24 @@ const NavHeader = ({ fairground }: { fairground: boolean }) => {
const { t } = useTranslation();
return (
-
-
- {fairground ? t('fairgroundTitle') : t('title')}
-
+
+
+ {fairground ? (
+
+ ) : (
+
+ )}
+
+
+
+ {fairground ? t('fairgroundTitle') : t('title')}
+
+
);
};
@@ -135,7 +208,13 @@ const NavDrawer = ({ inverted }: { inverted: boolean }) => {
);
};
-const NavLinks = ({ isDesktop }: { isDesktop: boolean }) => {
+const NavLinks = ({
+ isDesktop,
+ isInverted,
+}: {
+ isDesktop: boolean;
+ isInverted?: boolean;
+}) => {
const { appDispatch } = useAppState();
const { t } = useTranslation();
const linkProps = {
@@ -163,18 +242,21 @@ const NavLinks = ({ isDesktop }: { isDesktop: boolean }) => {
{...linkProps}
to={route}
key={route}
- className={({ isActive }) => {
- const linkClasses = classNames(
- 'no-underline hover:no-underline',
+ className={({ isActive }) =>
+ classNames(
+ 'no-underline hover:no-underline focus-visible:outline-none focus-visible:border-none focus-visible:shadow-inset-white',
{
- 'bg-vega-yellow text-black hover:text-black': isActive,
- 'bg-black text-white': !isActive,
+ 'bg-vega-yellow text-black': !isInverted && isActive,
+ 'bg-transparent text-white hover:text-vega-yellow':
+ !isInverted && !isActive,
+ 'bg-black text-white': isInverted && isActive,
+ 'bg-transparent text-black hover:text-white':
+ isInverted && !isActive,
'py-2 px-12': isDesktop,
'border-t border-white p-20': !isDesktop,
}
- );
- return linkClasses;
- }}
+ )
+ }
>
{text}
diff --git a/apps/token/src/components/transaction-callout/transaction-complete.tsx b/apps/token/src/components/transaction-callout/transaction-complete.tsx
index 079ca8004..ebda2e978 100644
--- a/apps/token/src/components/transaction-callout/transaction-complete.tsx
+++ b/apps/token/src/components/transaction-callout/transaction-complete.tsx
@@ -23,12 +23,8 @@ export const TransactionComplete = ({
intent={Intent.Success}
title={heading || t('Complete')}
>
- {body && (
-
- {body}
-
- )}
-
+ {body &&
{body}
}
+
-
- {error ? error.message : t('Something went wrong')}
-
+
{error ? error.message : t('Something went wrong')}
{hash ? (
-
+
} title={title}>
- {body && (
-
- {body}
-
- )}
-
+ {body &&
{body}
}
+
{
const { t } = useTranslation();
return (
-
-
{t('getWallet')}
-
+ <>
+
{t('getWallet')}
+
{t('readGuide')}
-
+
{t('downloadWallet')}
-
+ >
);
};
diff --git a/apps/token/src/components/vega-wallet/vega-wallet.tsx b/apps/token/src/components/vega-wallet/vega-wallet.tsx
index a843bdbc0..8eac261b9 100644
--- a/apps/token/src/components/vega-wallet/vega-wallet.tsx
+++ b/apps/token/src/components/vega-wallet/vega-wallet.tsx
@@ -40,14 +40,16 @@ export const VegaWallet = () => {
-
-
{t('vegaWallet')}
- {keypair && `(${keypair.name})`}
-
+ {t('vegaWallet')}
{keypair && (
-
- {truncateMiddle(keypair.pub)}
-
+ <>
+
+ {keypair.name}
+
+
+ {truncateMiddle(keypair.pub)}
+
+ >
)}
{child}
@@ -128,6 +130,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY,
diff --git a/apps/token/src/components/wallet-card/wallet-card.tsx b/apps/token/src/components/wallet-card/wallet-card.tsx
index 2d4156e2b..0313decd3 100644
--- a/apps/token/src/components/wallet-card/wallet-card.tsx
+++ b/apps/token/src/components/wallet-card/wallet-card.tsx
@@ -29,10 +29,14 @@ interface WalletCardProps {
}
export const WalletCard = ({ dark, children }: WalletCardProps) => {
- const className = classNames('text-ui border border-white', 'p-8', {
- 'bg-black text-white': dark,
- 'bg-white text-black': !dark,
- });
+ const className = classNames(
+ 'text-ui border border-white',
+ 'pt-4 pl-8 pr-12 pb-12',
+ {
+ 'bg-black text-white': dark,
+ 'bg-white text-black': !dark,
+ }
+ );
return {children}
;
};
@@ -43,7 +47,9 @@ interface WalletCardHeaderProps {
export const WalletCardHeader = ({ children }: WalletCardHeaderProps) => {
return (
- {children}
+
+ {children}
+
);
};
@@ -145,23 +151,23 @@ export const WalletCardAsset = ({
}`}
/>
-
+
{name}
{subheading || symbol}
-
+
{integers}.
{decimalsPlaces}
diff --git a/apps/token/src/components/web3-connector/web3-connector.tsx b/apps/token/src/components/web3-connector/web3-connector.tsx
index a0aa80562..954bad4df 100644
--- a/apps/token/src/components/web3-connector/web3-connector.tsx
+++ b/apps/token/src/components/web3-connector/web3-connector.tsx
@@ -79,7 +79,7 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
return (
-
This app only works on chain ID: {appChainId}
+
This app only works on chain ID: {appChainId}
connector.deactivate()}>Disconnect
diff --git a/apps/token/src/routes/governance/components/proposal-terms-json/proposal-terms-json.tsx b/apps/token/src/routes/governance/components/proposal-terms-json/proposal-terms-json.tsx
index d38549dab..bb0961c88 100644
--- a/apps/token/src/routes/governance/components/proposal-terms-json/proposal-terms-json.tsx
+++ b/apps/token/src/routes/governance/components/proposal-terms-json/proposal-terms-json.tsx
@@ -10,7 +10,7 @@ export const ProposalTermsJson = ({
const { t } = useTranslation();
return (
- {t('proposalTerms')}
+ {t('proposalTerms')}
);
diff --git a/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx b/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx
index 53a2eaf22..debccb7f9 100644
--- a/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx
+++ b/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx
@@ -23,10 +23,10 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
return (
<>
- {t('proposedChangesToVegaNetwork')}
- {t('vegaTokenHoldersCanVote')}
- {t('requiredMajorityDescription')}
- {t('proposals')}
+ {t('proposedChangesToVegaNetwork')}
+ {t('vegaTokenHoldersCanVote')}
+ {t('requiredMajorityDescription')}
+ {t('proposals')}
{proposals.map((proposal) => (
diff --git a/apps/token/src/routes/governance/components/vote-details/vote-details.tsx b/apps/token/src/routes/governance/components/vote-details/vote-details.tsx
index 5057c4bd7..5f7ff29b5 100644
--- a/apps/token/src/routes/governance/components/vote-details/vote-details.tsx
+++ b/apps/token/src/routes/governance/components/vote-details/vote-details.tsx
@@ -45,7 +45,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
return (
{t('votes')}
-
+
@@ -90,7 +90,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
-
+
{t('participation')}
{': '}
{participationMet ? (
diff --git a/apps/token/src/routes/home/index.tsx b/apps/token/src/routes/home/index.tsx
index 3b1052218..6e90d393a 100644
--- a/apps/token/src/routes/home/index.tsx
+++ b/apps/token/src/routes/home/index.tsx
@@ -42,13 +42,13 @@ const Home = ({ name }: RouteChildProps) => {
/>
- {t('Token Vesting')}
-
+
{t('Token Vesting')}
+
{t(
'The vesting contract holds VEGA tokens until they have become unlocked.'
)}
-
+
{
}}
/>
-
+
{t(
'Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.'
)}
@@ -74,13 +74,13 @@ const Home = ({ name }: RouteChildProps) => {
- {t('Use your Vega tokens')}
-
+
{t('Use your Vega tokens')}
+
{t(
'To use your tokens on the Vega network they need to be associated with a Vega wallet/key.'
)}
-
+
{t(
'This can happen both while held in the vesting contract as well as when redeemed.'
)}
@@ -109,8 +109,8 @@ const Home = ({ name }: RouteChildProps) => {
- {t('Staking')}
-
+
{t('Staking')}
+
{t(
'VEGA token holders can nominate a validator node and receive staking rewards.'
)}
@@ -129,8 +129,8 @@ const Home = ({ name }: RouteChildProps) => {
- {t('Governance')}
-
+
{t('Governance')}
+
{t(
'VEGA token holders can vote on proposed changes to the network and create proposals.'
)}
@@ -155,5 +155,5 @@ const Home = ({ name }: RouteChildProps) => {
export default Home;
export const HomeSection = ({ children }: { children: React.ReactNode }) => {
- return ;
+ return ;
};
diff --git a/apps/token/src/routes/home/token-details/token-details-circulating.tsx b/apps/token/src/routes/home/token-details/token-details-circulating.tsx
index 2416b01be..e149c8b13 100644
--- a/apps/token/src/routes/home/token-details/token-details-circulating.tsx
+++ b/apps/token/src/routes/home/token-details/token-details-circulating.tsx
@@ -37,7 +37,7 @@ export const TokenDetailsCirculating = ({
}) => {
const totalCirculating = sumCirculatingTokens(tranches);
return (
-
+
{formatNumber(totalCirculating, 2)}
);
diff --git a/apps/token/src/routes/home/token-details/token-details.tsx b/apps/token/src/routes/home/token-details/token-details.tsx
index bbf1c9171..3a10f0a10 100644
--- a/apps/token/src/routes/home/token-details/token-details.tsx
+++ b/apps/token/src/routes/home/token-details/token-details.tsx
@@ -42,13 +42,13 @@ export const TokenDetails = ({
}
return (
-
+
{t('Token address').toUpperCase()}
{token.address}
@@ -59,7 +59,7 @@ export const TokenDetails = ({
{config.token_vesting_contract.address}
@@ -67,7 +67,9 @@ export const TokenDetails = ({
{t('Total supply').toUpperCase()}
- {formatNumber(totalSupply, 2)}
+
+ {formatNumber(totalSupply, 2)}
+
{t('Circulating supply').toUpperCase()}
@@ -75,7 +77,9 @@ export const TokenDetails = ({
{t('Staked on Vega validator').toUpperCase()}
- {formatNumber(totalStaked, 2)}
+
+ {formatNumber(totalStaked, 2)}
+
);
diff --git a/apps/token/src/routes/redemption/home/redemption-information.tsx b/apps/token/src/routes/redemption/home/redemption-information.tsx
index fcaa4931e..6bcef6ae0 100644
--- a/apps/token/src/routes/redemption/home/redemption-information.tsx
+++ b/apps/token/src/routes/redemption/home/redemption-information.tsx
@@ -55,16 +55,18 @@ export const RedemptionInformation = () => {
if (!filteredTranches.length) {
return (
);
@@ -92,9 +94,7 @@ export const RedemptionInformation = () => {
vested={totalVestedBalance}
/>
- {filteredTranches.length ? (
-
{t('Tranche breakdown')}
- ) : null}
+ {filteredTranches.length ?
{t('Tranche breakdown')} : null}
{zeroTranche && (
{
iconName="hand-up"
intent={Intent.Warning}
>
- {t('Find out more about Staking.')}
+ {t('Find out more about Staking.')}
{t('Stake VEGA tokens')}
diff --git a/apps/token/src/routes/redemption/redemption.tsx b/apps/token/src/routes/redemption/redemption.tsx
index 6863593f9..26e756070 100644
--- a/apps/token/src/routes/redemption/redemption.tsx
+++ b/apps/token/src/routes/redemption/redemption.tsx
@@ -68,7 +68,7 @@ const RedemptionRouter = () => {
if (!account) {
return (
-
+
{t(
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas."
)}
diff --git a/apps/token/src/routes/redemption/tranche/index.tsx b/apps/token/src/routes/redemption/tranche/index.tsx
index 63d87f437..a3f6d17b7 100644
--- a/apps/token/src/routes/redemption/tranche/index.tsx
+++ b/apps/token/src/routes/redemption/tranche/index.tsx
@@ -73,7 +73,7 @@ export const RedeemFromTranche = () => {
!trancheBalance
) {
return (
-
+
{
}
completeBody={
<>
-
+
{t(
'You have redeemed {{redeemedAmount}} VEGA tokens from this tranche. They are now free to transfer from your Ethereum wallet.',
{
@@ -107,7 +107,7 @@ export const RedeemFromTranche = () => {
}
)}
-
+
{t(
'The VEGA token address is {{address}}, make sure you add this to your wallet to see your tokens',
{
diff --git a/apps/token/src/routes/rewards/home/index.tsx b/apps/token/src/routes/rewards/home/index.tsx
index 0a5631c22..b3cb01da4 100644
--- a/apps/token/src/routes/rewards/home/index.tsx
+++ b/apps/token/src/routes/rewards/home/index.tsx
@@ -109,8 +109,8 @@ export const RewardsIndex = () => {
return (
- {t('rewardsPara1')}
- {t('rewardsPara2')}
+ {t('rewardsPara1')}
+ {t('rewardsPara2')}
{payoutDuration ? (
-
+
{t('Connected Vega key')}: {currVegaKey.pub}
{vegaTokenRewards.length ? (
diff --git a/apps/token/src/routes/staking/associate/associate-info.tsx b/apps/token/src/routes/staking/associate/associate-info.tsx
index 50fd76715..4d482ebc0 100644
--- a/apps/token/src/routes/staking/associate/associate-info.tsx
+++ b/apps/token/src/routes/staking/associate/associate-info.tsx
@@ -13,7 +13,7 @@ export const AssociateInfo = ({ pubKey }: { pubKey: string | null }) => {
{t('What Vega key is going to control your stake?')}
-
+
{t('How much would you like to associate?')}
>
diff --git a/apps/token/src/routes/staking/associate/associate-transaction.tsx b/apps/token/src/routes/staking/associate/associate-transaction.tsx
index aa0270546..c1ea7576a 100644
--- a/apps/token/src/routes/staking/associate/associate-transaction.tsx
+++ b/apps/token/src/routes/staking/associate/associate-transaction.tsx
@@ -62,13 +62,13 @@ export const AssociateTransaction = ({
if (derivedTxState === TxState.Pending) {
return (
} title={title}>
-
+
{t('Associating {{amount}} VEGA tokens with Vega key {{vegaKey}}', {
amount,
vegaKey: truncateMiddle(vegaKey),
})}
-
+
-
+
{t('pendingAssociationText', {
confirmations: requiredConfirmations,
})}
diff --git a/apps/token/src/routes/staking/disassociate/disassociate-page.tsx b/apps/token/src/routes/staking/disassociate/disassociate-page.tsx
index 0c472dcc8..d48cccc36 100644
--- a/apps/token/src/routes/staking/disassociate/disassociate-page.tsx
+++ b/apps/token/src/routes/staking/disassociate/disassociate-page.tsx
@@ -60,24 +60,20 @@ export const DisassociatePage = ({
return (
-
+
{t(
'Use this form to disassociate VEGA tokens with a Vega key. This returns them to either the Ethereum wallet that used the Staking bridge or the vesting contract.'
)}
-
+
{t('Warning')}: {' '}
{t(
'Any Tokens that have been nominated to a node will sacrifice any Rewards they are due for the current epoch. If you do not wish to sacrifices fees you should remove stake from a node at the end of an epoch before disassocation.'
)}
-
- {t('What Vega wallet are you removing Tokens from?')}
-
+ {t('What Vega wallet are you removing Tokens from?')}
-
- {t('What tokens would you like to return?')}
-
+ {t('What tokens would you like to return?')}
- {t('Manage your stake')}
+ {t('Manage your stake')}
{
@@ -254,9 +254,7 @@ export const StakingForm = ({
<>
{action === Actions.Add ? (
<>
-
- {t('How much to Add in next epoch?')}
-
+ {t('How much to Add in next epoch?')}
{t('minimumNomination', {
minTokens: minTokensWithDecimals,
diff --git a/apps/token/src/routes/staking/staking-node.tsx b/apps/token/src/routes/staking/staking-node.tsx
index 9724c73bb..1f5c29ac2 100644
--- a/apps/token/src/routes/staking/staking-node.tsx
+++ b/apps/token/src/routes/staking/staking-node.tsx
@@ -96,10 +96,7 @@ export const StakingNode = ({ vegaKey, data }: StakingNodeProps) => {
return (
<>
-
+
{nodeInfo.name
? t('validatorTitle', { nodeName: nodeInfo.name })
: t('validatorTitle', { nodeName: t('validatorTitleFallback') })}
diff --git a/apps/token/src/routes/staking/staking.tsx b/apps/token/src/routes/staking/staking.tsx
index 203566fed..fdd5b18f7 100644
--- a/apps/token/src/routes/staking/staking.tsx
+++ b/apps/token/src/routes/staking/staking.tsx
@@ -19,17 +19,19 @@ import { NodeList } from './node-list';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { truncateMiddle } from '../../lib/truncate-middle';
+const stakingBulletStyles = { marginBottom: '12px', fontSize: '18px' };
+
export const Staking = ({ data }: { data?: StakingQueryResult }) => {
const { t } = useTranslation();
return (
<>
- {t('stakingDescription1')}
- {t('stakingDescription2')}
- {t('stakingDescription3')}
- {t('stakingDescription4')}
-
+
{t('stakingDescription1')}
+ {t('stakingDescription2')}
+ {t('stakingDescription3')}
+ {t('stakingDescription4')}
+
{
- {t('stakingStep1')}
+
+ {t('stakingStep1')}
+
- {t('stakingStep1')}
+
+ {t('stakingStep2')}
+
{
/>
- {t('stakingStep3')}
+
+ {t('stakingStep3')}
+
>
@@ -82,7 +90,7 @@ export const StakingStepConnectWallets = () => {
{truncateMiddle(account)}
-
+
{t('stakingVegaWalletConnected', {
key: truncateMiddle(keypair.pub),
})}
@@ -93,7 +101,7 @@ export const StakingStepConnectWallets = () => {
return (
<>
-
+
{
/>
) : (
-
+
appDispatch({
diff --git a/apps/token/src/routes/staking/your-stake.tsx b/apps/token/src/routes/staking/your-stake.tsx
index bb1f630cb..1bef504c0 100644
--- a/apps/token/src/routes/staking/your-stake.tsx
+++ b/apps/token/src/routes/staking/your-stake.tsx
@@ -17,7 +17,7 @@ export const YourStake = ({
return (
-
{t('Your stake')}
+ {t('Your stake')}
{t('Your Stake On Node (This Epoch)')}
diff --git a/apps/token/src/routes/tranches/tranche.tsx b/apps/token/src/routes/tranches/tranche.tsx
index 689118135..08aefe54b 100644
--- a/apps/token/src/routes/tranches/tranche.tsx
+++ b/apps/token/src/routes/tranches/tranche.tsx
@@ -67,7 +67,7 @@ export const Tranche = () => {
{formatNumber(tranche.total_removed)}
- {t('Holders')}
+ {t('Holders')}
{tranche.users.length ? (
{tranche.users.map((user, i) => {
diff --git a/apps/token/src/routes/tranches/tranches.tsx b/apps/token/src/routes/tranches/tranches.tsx
index fd2e494e4..1053b45eb 100644
--- a/apps/token/src/routes/tranches/tranches.tsx
+++ b/apps/token/src/routes/tranches/tranches.tsx
@@ -29,10 +29,10 @@ export const Tranches = () => {
return (
- {t('chartTitle')}
- {t('chartAbove')}
+ {t('chartTitle')}
+ {t('chartAbove')}
- {t('chartBelow')}
+ {t('chartBelow')}
{tranches?.length ? (
{(showAll ? tranches : filteredTranches).map((tranche) => {
diff --git a/apps/token/src/routes/withdraw/index.tsx b/apps/token/src/routes/withdraw/index.tsx
index 00be00cd4..2edd7db16 100644
--- a/apps/token/src/routes/withdraw/index.tsx
+++ b/apps/token/src/routes/withdraw/index.tsx
@@ -23,7 +23,7 @@ const Withdraw = () => {
return (
<>
- {t('withdrawPageText')}
+ {t('withdrawPageText')}
{(currVegaKey) => }
@@ -141,7 +141,7 @@ export const WithdrawContainer = ({ currVegaKey }: WithdrawContainerProps) => {
title={t('pendingWithdrawalsCalloutTitle')}
intent={Intent.Warning}
>
- {t('pendingWithdrawalsCalloutText')}
+ {t('pendingWithdrawalsCalloutText')}
{t('pendingWithdrawalsCalloutButton')}
diff --git a/apps/token/src/routes/withdrawals/index.tsx b/apps/token/src/routes/withdrawals/index.tsx
index 3c3312f26..9ada450a6 100644
--- a/apps/token/src/routes/withdrawals/index.tsx
+++ b/apps/token/src/routes/withdrawals/index.tsx
@@ -74,7 +74,7 @@ const WithdrawPendingContainer = () => {
return (
<>
{t('withdrawalsPreparedWarningHeading')}
- {t('withdrawalsText')}
+ {t('withdrawalsText')}
{t('withdrawalsPreparedWarningText')}
{withdrawals.map((w) => (
diff --git a/apps/token/src/styles.css b/apps/token/src/styles.css
index b5c61c956..f21979c5c 100644
--- a/apps/token/src/styles.css
+++ b/apps/token/src/styles.css
@@ -1,3 +1,15 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+
+@layer base {
+ h1 {
+ @apply text-h3 text-white uppercase mb-12;
+ }
+ h2 {
+ @apply text-h4 text-white mb-8;
+ }
+ p {
+ @apply mb-12;
+ }
+}
diff --git a/apps/trading-e2e/.cypress-cucumber-preprocessorrc b/apps/trading-e2e/.cypress-cucumber-preprocessorrc
deleted file mode 100644
index dc930805e..000000000
--- a/apps/trading-e2e/.cypress-cucumber-preprocessorrc
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "stepDefinitions": "src/support/step_definitions"
-}
diff --git a/apps/trading-e2e/cypress.config.js b/apps/trading-e2e/cypress.config.js
new file mode 100644
index 000000000..f3ea716a0
--- /dev/null
+++ b/apps/trading-e2e/cypress.config.js
@@ -0,0 +1,48 @@
+const { defineConfig } = require('cypress');
+
+module.exports = defineConfig({
+ component: {
+ baseUrl: 'http://localhost:4200',
+ fileServerFolder: '.',
+ fixturesFolder: false,
+ specPattern: '**/*.cy.{js,jsx,ts,tsx}',
+ supportFile: './src/support/index.ts',
+ video: true,
+ videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
+ screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
+ chromeWebSecurity: false,
+ projectId: 'et4snf',
+ defaultCommandTimeout: 10000,
+ },
+ e2e: {
+ baseUrl: 'http://localhost:4200',
+ fileServerFolder: '.',
+ fixturesFolder: false,
+ specPattern: '**/*.cy.{js,jsx,ts,tsx}',
+ supportFile: './src/support/index.ts',
+ video: true,
+ videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
+ screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
+ chromeWebSecurity: false,
+ projectId: 'et4snf',
+ defaultCommandTimeout: 10000,
+ },
+ env: {
+ TRADING_TEST_VEGA_WALLET_NAME: 'UI_Trading_Test',
+ ETHEREUM_PROVIDER_URL:
+ 'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
+ VEGA_PUBLIC_KEY:
+ '47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278',
+ VEGA_PUBLIC_KEY2:
+ '1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4',
+ TRUNCATED_VEGA_PUBLIC_KEY: '47836c…c7d278',
+ TRUNCATED_VEGA_PUBLIC_KEY2: '1a18cd…0cf2e4',
+ ETHEREUM_WALLET_ADDRESS: '0x265Cc6d39a1B53d0d92068443009eE7410807158',
+ ETHERSCAN_URL: 'https://ropsten.etherscan.io',
+ tsConfig: 'tsconfig.json',
+ TAGS: 'not @todo and not @ignore and not @manual',
+ TRADING_TEST_VEGA_WALLET_PASSPHRASE: '123',
+ ETH_WALLET_MNEMONIC:
+ 'ugly gallery notice network true range brave clarify flat logic someone chunk',
+ },
+});
diff --git a/apps/trading-e2e/cypress.json b/apps/trading-e2e/cypress.json
deleted file mode 100644
index fc00e9973..000000000
--- a/apps/trading-e2e/cypress.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "baseUrl": "http://localhost:4200",
- "fileServerFolder": ".",
- "fixturesFolder": false,
- "pluginsFile": "./src/plugins/index.js",
- "testFiles": "*.{ts,feature,features}",
- "ignoreTestFiles": "**/*.js",
- "integrationFolder": "./src/integration",
- "modifyObstructiveCode": false,
- "supportFile": "./src/support/index.ts",
- "video": true,
- "videosFolder": "../../dist/cypress/apps/trading-e2e/videos",
- "screenshotsFolder": "../../dist/cypress/apps/trading-e2e/screenshots",
- "chromeWebSecurity": false,
- "projectId": "et4snf",
- "defaultCommandTimeout": 10000,
- "env": {
- "TRADING_TEST_VEGA_WALLET_NAME": "UI_Trading_Test",
- "ETHEREUM_PROVIDER_URL": "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8",
- "VEGA_PUBLIC_KEY": "47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278",
- "VEGA_PUBLIC_KEY2": "1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4",
- "TRUNCATED_VEGA_PUBLIC_KEY": "47836c…c7d278",
- "TRUNCATED_VEGA_PUBLIC_KEY2": "1a18cd…0cf2e4",
- "ETHEREUM_WALLET_ADDRESS": "0x265Cc6d39a1B53d0d92068443009eE7410807158",
- "ETHERSCAN_URL": "https://ropsten.etherscan.io",
- "tsConfig": "tsconfig.json",
- "TAGS": "not @todo and not @ignore and not @manual",
- "TRADING_TEST_VEGA_WALLET_PASSPHRASE": "123",
- "ETH_WALLET_MNEMONIC": "ugly gallery notice network true range brave clarify flat logic someone chunk"
- }
-}
diff --git a/apps/trading-e2e/project.json b/apps/trading-e2e/project.json
index da7658039..f2d777771 100644
--- a/apps/trading-e2e/project.json
+++ b/apps/trading-e2e/project.json
@@ -6,7 +6,7 @@
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
- "cypressConfig": "apps/trading-e2e/cypress.json",
+ "cypressConfig": "apps/trading-e2e/cypress.config.js",
"devServerTarget": "trading:serve"
},
"configurations": {
diff --git a/apps/trading-e2e/src/integration/deposit.ts b/apps/trading-e2e/src/integration/deposit.cy.ts
similarity index 100%
rename from apps/trading-e2e/src/integration/deposit.ts
rename to apps/trading-e2e/src/integration/deposit.cy.ts
diff --git a/apps/trading-e2e/src/integration/global.ts b/apps/trading-e2e/src/integration/global.cy.ts
similarity index 90%
rename from apps/trading-e2e/src/integration/global.ts
rename to apps/trading-e2e/src/integration/global.cy.ts
index 0736720a9..c4c468351 100644
--- a/apps/trading-e2e/src/integration/global.ts
+++ b/apps/trading-e2e/src/integration/global.cy.ts
@@ -10,6 +10,7 @@ describe('vega wallet', () => {
beforeEach(() => {
// Using portfolio page as it requires vega wallet connection
cy.visit('/portfolio');
+ cy.get('main[data-testid="portfolio"]').should('exist');
});
it('can connect', () => {
@@ -64,8 +65,10 @@ describe('vega wallet', () => {
describe('ethereum wallet', () => {
beforeEach(() => {
cy.mockWeb3Provider();
- // Using portfolio is it requires Ethereum wallet connection
+ // Using portfolio withdrawals tab is it requires Ethereum wallet connection
cy.visit('/portfolio');
+ cy.get('main[data-testid="portfolio"]').should('exist');
+ cy.getByTestId('Withdrawals').click();
});
it('can connect', () => {
@@ -73,6 +76,6 @@ describe('ethereum wallet', () => {
cy.getByTestId('web3-connector-list').should('exist');
cy.getByTestId('web3-connector-MetaMask').click();
cy.getByTestId('web3-connector-list').should('not.exist');
- cy.getByTestId('portfolio-grid').should('exist');
+ cy.getByTestId('tab-withdrawals').should('not.be.empty');
});
});
diff --git a/apps/trading-e2e/src/integration/home.ts b/apps/trading-e2e/src/integration/home.cy.ts
similarity index 100%
rename from apps/trading-e2e/src/integration/home.ts
rename to apps/trading-e2e/src/integration/home.cy.ts
diff --git a/apps/trading-e2e/src/integration/markets.ts b/apps/trading-e2e/src/integration/markets.cy.ts
similarity index 74%
rename from apps/trading-e2e/src/integration/markets.ts
rename to apps/trading-e2e/src/integration/markets.cy.ts
index aa47c61a3..0c9b6b8a0 100644
--- a/apps/trading-e2e/src/integration/markets.ts
+++ b/apps/trading-e2e/src/integration/markets.cy.ts
@@ -71,6 +71,8 @@ describe('markets table', () => {
cy.wait('@Market');
cy.contains('ACTIVE MARKET');
cy.url().should('include', '/markets/market-0');
+
+ verifyMarketSummaryDisplayed('Active');
});
it('can select a suspended market', () => {
@@ -88,5 +90,28 @@ describe('markets table', () => {
cy.wait('@Market');
cy.contains('SUSPENDED MARKET');
cy.url().should('include', '/markets/market-1');
+
+ verifyMarketSummaryDisplayed('Suspended');
});
+
+ function verifyMarketSummaryDisplayed(expectedMarketState: string) {
+ const marketSummaryBlock = 'market-summary';
+ const percentageValue = 'price-change-percentage';
+ const priceChangeValue = 'price-change';
+ const tradingVolume = 'trading-volume';
+ const tradingMode = 'trading-mode';
+ const marketState = 'market-state';
+
+ cy.getByTestId(marketSummaryBlock).within(() => {
+ cy.contains('Change (24h)');
+ cy.getByTestId(percentageValue).should('not.be.empty');
+ cy.getByTestId(priceChangeValue).should('not.be.empty');
+ cy.contains('Volume');
+ cy.getByTestId(tradingVolume).should('not.be.empty');
+ cy.contains('Trading mode');
+ cy.getByTestId(tradingMode).should('not.be.empty');
+ cy.contains('State');
+ cy.getByTestId(marketState).should('have.text', expectedMarketState);
+ });
+ }
});
diff --git a/apps/trading-e2e/src/integration/portfolio-fills.cy.ts b/apps/trading-e2e/src/integration/portfolio-fills.cy.ts
new file mode 100644
index 000000000..f22eeacd8
--- /dev/null
+++ b/apps/trading-e2e/src/integration/portfolio-fills.cy.ts
@@ -0,0 +1,120 @@
+import { aliasQuery } from '@vegaprotocol/cypress';
+import { generateFill, generateFills } from '../support/mocks/generate-fills';
+import { Side } from '@vegaprotocol/types';
+import { connectVegaWallet } from '../support/vega-wallet';
+
+describe('fills', () => {
+ before(() => {
+ const fills = [
+ generateFill({
+ buyer: {
+ id: Cypress.env('VEGA_PUBLIC_KEY'),
+ },
+ }),
+ generateFill({
+ id: '1',
+ seller: {
+ id: Cypress.env('VEGA_PUBLIC_KEY'),
+ },
+ aggressor: Side.Sell,
+ buyerFee: {
+ infrastructureFee: '5000',
+ },
+ market: {
+ name: 'Apples Daily v3',
+ positionDecimalPlaces: 2,
+ },
+ }),
+ generateFill({
+ id: '2',
+ seller: {
+ id: Cypress.env('VEGA_PUBLIC_KEY'),
+ },
+ aggressor: Side.Buy,
+ }),
+ generateFill({
+ id: '3',
+ aggressor: Side.Sell,
+ market: {
+ name: 'ETHBTC Quarterly (30 Jun 2022)',
+ },
+ buyer: {
+ id: Cypress.env('VEGA_PUBLIC_KEY'),
+ },
+ }),
+ ];
+ const result = generateFills({
+ party: {
+ tradesPaged: {
+ edges: fills.map((f, i) => {
+ return {
+ __typename: 'TradeEdge',
+ node: f,
+ cursor: i.toString(),
+ };
+ }),
+ },
+ },
+ });
+ cy.mockGQL((req) => {
+ aliasQuery(req, 'Fills', result);
+ });
+ cy.visit('/portfolio');
+ cy.get('main[data-testid="portfolio"]').should('exist');
+ });
+
+ it('renders fills', () => {
+ cy.getByTestId('Fills').click();
+ cy.getByTestId('tab-fills').contains('Please connect Vega wallet');
+
+ connectVegaWallet();
+
+ cy.getByTestId('tab-fills').should('be.visible');
+
+ cy.getByTestId('tab-fills')
+ .get('[role="gridcell"][col-id="market.name"]')
+ .each(($marketSymbol) => {
+ cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
+ });
+ cy.getByTestId('tab-fills')
+ .get('[role="gridcell"][col-id="size"]')
+ .each(($amount) => {
+ cy.wrap($amount).invoke('text').should('not.be.empty');
+ });
+ cy.getByTestId('tab-positions')
+ .get('[role="gridcell"][col-id="price"]')
+ .each(($prices) => {
+ cy.wrap($prices).invoke('text').should('not.be.empty');
+ });
+ cy.getByTestId('tab-positions')
+ .get('[role="gridcell"][col-id="price_1"]')
+ .each(($total) => {
+ cy.wrap($total).invoke('text').should('not.be.empty');
+ });
+ cy.getByTestId('tab-positions')
+ .get('[role="gridcell"][col-id="aggressor"]')
+ .each(($role) => {
+ cy.wrap($role)
+ .invoke('text')
+ .then((text) => {
+ const roles = ['Maker', 'Taker'];
+ expect(roles.indexOf(text.trim())).to.be.greaterThan(-1);
+ });
+ });
+ cy.getByTestId('tab-positions')
+ .get(
+ '[role="gridcell"][col-id="market.tradableInstrument.instrument.product"]'
+ )
+ .each(($fees) => {
+ cy.wrap($fees).invoke('text').should('not.be.empty');
+ });
+ const dateTimeRegex =
+ /(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
+ cy.get('[col-id="createdAt"]').each(($tradeDateTime, index) => {
+ if (index != 0) {
+ //ignore header
+ cy.wrap($tradeDateTime).invoke('text').should('match', dateTimeRegex);
+ }
+ });
+ });
+});
diff --git a/apps/trading-e2e/src/integration/portfolio.ts b/apps/trading-e2e/src/integration/portfolio.ts
deleted file mode 100644
index 7415cac05..000000000
--- a/apps/trading-e2e/src/integration/portfolio.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-describe('portfolio', () => {
- it('requires connecting', () => {
- cy.visit('/portfolio');
- cy.get('main[data-testid="portfolio"]').should('exist');
- });
-});
diff --git a/apps/trading-e2e/src/integration/trading-accounts.ts b/apps/trading-e2e/src/integration/trading-accounts.cy.ts
similarity index 100%
rename from apps/trading-e2e/src/integration/trading-accounts.ts
rename to apps/trading-e2e/src/integration/trading-accounts.cy.ts
diff --git a/apps/trading-e2e/src/integration/trading-deal-ticket.ts b/apps/trading-e2e/src/integration/trading-deal-ticket.cy.ts
similarity index 96%
rename from apps/trading-e2e/src/integration/trading-deal-ticket.ts
rename to apps/trading-e2e/src/integration/trading-deal-ticket.cy.ts
index 28123a6a6..b1015fc6b 100644
--- a/apps/trading-e2e/src/integration/trading-deal-ticket.ts
+++ b/apps/trading-e2e/src/integration/trading-deal-ticket.cy.ts
@@ -144,15 +144,15 @@ describe('deal ticket orders', () => {
'Awaiting network confirmation'
);
cy.getByTestId(orderTransactionHash)
- .invoke('text')
- .should('contain', 'Tx hash: test-tx-hash');
+ .invoke('attr', 'href')
+ .should('include', 'https://explorer.fairground.wtf/txs/0xtest-tx-hash');
cy.getByTestId('dialog-close').click();
};
it.skip('cannot place an order if market is suspended');
it.skip('cannot place an order if size is 0');
it.skip('cannot place an order expiry date is invalid');
- it.skip('unsuccessfull order due to no collateral');
+ it.skip('unsuccessful order due to no collateral');
});
describe('deal ticket validation', () => {
diff --git a/apps/trading-e2e/src/integration/trading-orders.ts b/apps/trading-e2e/src/integration/trading-orders.cy.ts
similarity index 100%
rename from apps/trading-e2e/src/integration/trading-orders.ts
rename to apps/trading-e2e/src/integration/trading-orders.cy.ts
diff --git a/apps/trading-e2e/src/integration/trading-positions.ts b/apps/trading-e2e/src/integration/trading-positions.cy.ts
similarity index 100%
rename from apps/trading-e2e/src/integration/trading-positions.ts
rename to apps/trading-e2e/src/integration/trading-positions.cy.ts
diff --git a/apps/trading-e2e/src/integration/trading-trades.ts b/apps/trading-e2e/src/integration/trading-trades.cy.ts
similarity index 100%
rename from apps/trading-e2e/src/integration/trading-trades.ts
rename to apps/trading-e2e/src/integration/trading-trades.cy.ts
diff --git a/apps/trading-e2e/src/integration/withdraw.ts b/apps/trading-e2e/src/integration/withdraw.cy.ts
similarity index 100%
rename from apps/trading-e2e/src/integration/withdraw.ts
rename to apps/trading-e2e/src/integration/withdraw.cy.ts
diff --git a/apps/trading-e2e/src/integration/withdrawals.ts b/apps/trading-e2e/src/integration/withdrawals.cy.ts
similarity index 100%
rename from apps/trading-e2e/src/integration/withdrawals.ts
rename to apps/trading-e2e/src/integration/withdrawals.cy.ts
diff --git a/apps/trading-e2e/src/plugins/index.js b/apps/trading-e2e/src/plugins/index.js
deleted file mode 100644
index 97abe0be2..000000000
--- a/apps/trading-e2e/src/plugins/index.js
+++ /dev/null
@@ -1,75 +0,0 @@
-///
-
-const webpackPreprocessor = require('@cypress/webpack-preprocessor');
-const webpack = require('webpack');
-const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
-const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
-const nodeExternals = require('webpack-node-externals');
-
-/**
- * @type {Cypress.PluginConfig}
- */
-module.exports = (on, config) => {
- on(
- 'file:preprocessor',
- webpackPreprocessor({
- webpackOptions: {
- resolve: {
- extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
- plugins: [
- new TsconfigPathsPlugin({
- configFile: config.env.tsConfig,
- extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
- }),
- ],
- fallback: {
- path: require.resolve('path-browserify'),
- },
- },
- module: {
- rules: [
- {
- test: /\.([jt])sx?$/,
- loader: 'ts-loader',
- exclude: [/node_modules/],
- options: {
- configFile: config.env.tsConfig,
- // https://github.com/TypeStrong/ts-loader/pull/685
- experimentalWatchApi: true,
- transpileOnly: true,
- },
- },
- {
- test: /\.feature$/,
- use: [
- {
- loader: 'cypress-cucumber-preprocessor/loader',
- },
- ],
- },
- {
- test: /\.features$/,
- use: [
- {
- loader: 'cypress-cucumber-preprocessor/lib/featuresLoader',
- },
- ],
- },
- ],
- },
- plugins: [
- new ForkTsCheckerWebpackPlugin({
- typescript: {
- enabled: true,
- configFile: config.env.tsConfig,
- },
- }),
- new webpack.ProvidePlugin({
- process: 'process/browser',
- }),
- ],
- externals: [nodeExternals()],
- },
- })
- );
-};
diff --git a/apps/trading-e2e/src/support/mocks/generate-fills.ts b/apps/trading-e2e/src/support/mocks/generate-fills.ts
new file mode 100644
index 000000000..fa55d9840
--- /dev/null
+++ b/apps/trading-e2e/src/support/mocks/generate-fills.ts
@@ -0,0 +1,134 @@
+import type {
+ Fills,
+ Fills_party_tradesPaged_edges_node,
+} from '@vegaprotocol/fills';
+import { Side } from '@vegaprotocol/types';
+import merge from 'lodash/merge';
+import type { PartialDeep } from 'type-fest';
+
+export const generateFills = (override?: PartialDeep): Fills => {
+ const fills: Fills_party_tradesPaged_edges_node[] = [
+ generateFill({
+ buyer: {
+ id: Cypress.env('VEGA_PUBLIC_KEY'),
+ },
+ }),
+ generateFill({
+ id: '1',
+ seller: {
+ id: Cypress.env('VEGA_PUBLIC_KEY'),
+ },
+ aggressor: Side.Sell,
+ buyerFee: {
+ infrastructureFee: '5000',
+ },
+ market: {
+ name: 'Apples Daily v3',
+ positionDecimalPlaces: 2,
+ },
+ }),
+ generateFill({
+ id: '2',
+ seller: {
+ id: Cypress.env('VEGA_PUBLIC_KEY'),
+ },
+ aggressor: Side.Buy,
+ }),
+ generateFill({
+ id: '3',
+ aggressor: Side.Sell,
+ market: {
+ name: 'ETHBTC Quarterly (30 Jun 2022)',
+ },
+ buyer: {
+ id: Cypress.env('VEGA_PUBLIC_KEY'),
+ },
+ }),
+ ];
+
+ const defaultResult: Fills = {
+ party: {
+ id: 'buyer-id',
+ tradesPaged: {
+ __typename: 'TradeConnection',
+ totalCount: 1,
+ edges: fills.map((f) => {
+ return {
+ __typename: 'TradeEdge',
+ node: f,
+ cursor: '3',
+ };
+ }),
+ pageInfo: {
+ __typename: 'PageInfo',
+ startCursor: '1',
+ endCursor: '2',
+ },
+ },
+ __typename: 'Party',
+ },
+ };
+
+ return merge(defaultResult, override);
+};
+
+export const generateFill = (
+ override?: PartialDeep
+) => {
+ const defaultFill: Fills_party_tradesPaged_edges_node = {
+ __typename: 'Trade',
+ id: '0',
+ createdAt: new Date().toISOString(),
+ price: '10000000',
+ size: '50000',
+ buyOrder: 'buy-order',
+ sellOrder: 'sell-order',
+ aggressor: Side.Buy,
+ buyer: {
+ __typename: 'Party',
+ id: 'buyer-id',
+ },
+ seller: {
+ __typename: 'Party',
+ id: 'seller-id',
+ },
+ buyerFee: {
+ __typename: 'TradeFee',
+ makerFee: '100',
+ infrastructureFee: '100',
+ liquidityFee: '100',
+ },
+ sellerFee: {
+ __typename: 'TradeFee',
+ makerFee: '200',
+ infrastructureFee: '200',
+ liquidityFee: '200',
+ },
+ market: {
+ __typename: 'Market',
+ id: 'market-id',
+ name: 'UNIDAI Monthly (30 Jun 2022)',
+ positionDecimalPlaces: 0,
+ decimalPlaces: 5,
+ tradableInstrument: {
+ __typename: 'TradableInstrument',
+ instrument: {
+ __typename: 'Instrument',
+ id: 'instrument-id',
+ code: 'instrument-code',
+ product: {
+ __typename: 'Future',
+ settlementAsset: {
+ __typename: 'Asset',
+ id: 'asset-id',
+ symbol: 'SYM',
+ decimals: 18,
+ },
+ },
+ },
+ },
+ },
+ };
+
+ return merge(defaultFill, override);
+};
diff --git a/apps/trading-e2e/src/support/mocks/generate-market.ts b/apps/trading-e2e/src/support/mocks/generate-market.ts
index 5a21d09c4..f8905a491 100644
--- a/apps/trading-e2e/src/support/mocks/generate-market.ts
+++ b/apps/trading-e2e/src/support/mocks/generate-market.ts
@@ -1,21 +1,68 @@
import merge from 'lodash/merge';
+import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
import type { PartialDeep } from 'type-fest';
-
-export interface Market_market {
- __typename: 'Market';
- id: string;
- name: string;
-}
-
-export interface Market {
- market: Market_market | null;
-}
+// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
+import type { Market } from '../../../../trading/pages/markets/__generated__/Market';
export const generateMarket = (override?: PartialDeep): Market => {
- const defaultResult = {
+ const defaultResult: Market = {
market: {
id: 'market-0',
- name: 'MARKET NAME',
+ name: 'ACTIVE MARKET',
+ tradingMode: MarketTradingMode.Continuous,
+ state: MarketState.Active,
+ decimalPlaces: 5,
+ data: {
+ market: {
+ id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
+ __typename: 'Market',
+ },
+ markPrice: '13739109',
+ indicativeVolume: '0',
+ bestBidVolume: '244',
+ bestOfferVolume: '100',
+ bestStaticBidVolume: '482',
+ bestStaticOfferVolume: '2188',
+ __typename: 'MarketData',
+ },
+ tradableInstrument: {
+ instrument: {
+ name: 'BTCUSD Monthly',
+ code: 'BTCUSD.MF21',
+ metadata: {
+ tags: [
+ 'formerly:076BB86A5AA41E3E',
+ 'base:BTC',
+ 'quote:USD',
+ 'class:fx/crypto',
+ 'monthly',
+ 'sector:crypto',
+ ],
+ __typename: 'InstrumentMetadata',
+ },
+ __typename: 'Instrument',
+ },
+ __typename: 'TradableInstrument',
+ },
+ marketTimestamps: {
+ open: '2022-06-21T17:18:43.484055236Z',
+ close: null,
+ __typename: 'MarketTimestamps',
+ },
+ candles: [
+ {
+ open: '2095312844',
+ close: '2090090607',
+ volume: '4847',
+ __typename: 'Candle',
+ },
+ {
+ open: '2090090000',
+ close: '2090090607',
+ volume: '4847',
+ __typename: 'Candle',
+ },
+ ],
__typename: 'Market',
},
};
diff --git a/apps/trading-e2e/src/support/trading.ts b/apps/trading-e2e/src/support/trading.ts
index 3b3c3db57..4f3decb74 100644
--- a/apps/trading-e2e/src/support/trading.ts
+++ b/apps/trading-e2e/src/support/trading.ts
@@ -21,6 +21,7 @@ export const mockTradingPage = (
generateMarket({
market: {
name: `${state.toUpperCase()} MARKET`,
+ state: state,
},
})
);
diff --git a/apps/trading/.env b/apps/trading/.env
index 82d9ff526..b632722ae 100644
--- a/apps/trading/.env
+++ b/apps/trading/.env
@@ -6,3 +6,4 @@ NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
NX_USE_ENV_OVERRIDES=1
+NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
diff --git a/apps/trading/.env.devnet b/apps/trading/.env.devnet
index 6cc08adc4..a9a913252 100644
--- a/apps/trading/.env.devnet
+++ b/apps/trading/.env.devnet
@@ -5,3 +5,4 @@ NX_VEGA_URL=https://n04.d.vega.xyz/query
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\"}
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
+NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
diff --git a/apps/trading/.env.mainnet b/apps/trading/.env.mainnet
index 71078005d..9aa4b5d17 100644
--- a/apps/trading/.env.mainnet
+++ b/apps/trading/.env.mainnet
@@ -5,3 +5,4 @@ NX_VEGA_URL=https://api.token.vega.xyz/query
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
+NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
diff --git a/apps/trading/.env.stagnet1 b/apps/trading/.env.stagnet1
index 13ac66839..7a84c347f 100644
--- a/apps/trading/.env.stagnet1
+++ b/apps/trading/.env.stagnet1
@@ -5,3 +5,4 @@ NX_VEGA_URL=https://n03.s.vega.xyz/query
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
+NX_VEGA_EXPLORER_URL=https://staging.explorer.vega.xyz
diff --git a/apps/trading/.env.stagnet2 b/apps/trading/.env.stagnet2
index ae6011f3e..4c5893943 100644
--- a/apps/trading/.env.stagnet2
+++ b/apps/trading/.env.stagnet2
@@ -5,3 +5,4 @@ NX_VEGA_URL=https://n03.stagnet2.vega.xyz/query
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
+NX_VEGA_EXPLORER_URL=https://staging2.explorer.vega.xyz
diff --git a/apps/trading/.env.testnet b/apps/trading/.env.testnet
index 7804d1378..7b36cbfe9 100644
--- a/apps/trading/.env.testnet
+++ b/apps/trading/.env.testnet
@@ -5,3 +5,4 @@ NX_VEGA_URL=https://lb.testnet.vega.xyz/query
NX_VEGA_NETWORKS='{\"MAINNET\":\"https://alpha.console.vega.xyz\"}'
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
+NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
diff --git a/apps/trading/pages/markets/trade-grid.tsx b/apps/trading/pages/markets/trade-grid.tsx
index eb26211df..2f150c99a 100644
--- a/apps/trading/pages/markets/trade-grid.tsx
+++ b/apps/trading/pages/markets/trade-grid.tsx
@@ -72,7 +72,10 @@ export const TradeMarketHeader = ({
-
+
Change (24h)
Volume
-
+
{market.data && market.data.indicativeVolume !== '0'
? market.data.indicativeVolume
: '-'}
@@ -90,11 +93,15 @@ export const TradeMarketHeader = ({
Trading mode
- {market.tradingMode}
+
+ {market.tradingMode}
+
State
- {market.state}
+
+ {market.state}
+
diff --git a/apps/trading/pages/portfolio/index.page.tsx b/apps/trading/pages/portfolio/index.page.tsx
index 143ce1366..43310c627 100644
--- a/apps/trading/pages/portfolio/index.page.tsx
+++ b/apps/trading/pages/portfolio/index.page.tsx
@@ -5,71 +5,76 @@ import { OrderListContainer } from '@vegaprotocol/order-list';
import { AccountsContainer } from '@vegaprotocol/accounts';
import { AnchorButton, Tab, Tabs } from '@vegaprotocol/ui-toolkit';
import { WithdrawalsContainer } from './withdrawals/withdrawals-container';
+import { FillsContainer } from '@vegaprotocol/fills';
+import classNames from 'classnames';
+import type { ReactNode } from 'react';
const Portfolio = () => {
- const tabClassName = 'p-[16px] pl-[316px]';
-
+ const wrapperClasses = classNames(
+ 'h-full max-h-full',
+ 'grid gap-4 grid-rows-[1fr_300px]',
+ 'bg-black-10 dark:bg-white-10',
+ 'text-ui'
+ );
+ const tabContentClassName = 'h-full grid gap-4 grid-rows-[min-content_1fr]';
return (
-
-
-
-
-
-
-
-
-
- {t('Positions')}
-
-
-
-
-
-
-
- {t('Orders')}
-
-
-
-
-
-
-
- {t('Fills')}
-
-
-
-
-
-
- {t('History')}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {t('Deposit')}
-
-
-
+
+
+
+
+
+
+ {t('Positions')}
+
+
+
+
+
+
+
+ {t('Orders')}
+
+
+
+
+
+
+
+
+
+ {t('Fills')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
);
};
@@ -78,3 +83,16 @@ Portfolio.getInitialProps = () => ({
});
export default Portfolio;
+
+interface PortfolioGridChildProps {
+ children: ReactNode;
+ className?: string;
+}
+
+const PortfolioGridChild = ({
+ children,
+ className,
+}: PortfolioGridChildProps) => {
+ const gridChildClasses = classNames('bg-white dark:bg-black', className);
+ return ;
+};
diff --git a/libs/cypress/src/lib/graphql-test-utils.ts b/libs/cypress/src/lib/graphql-test-utils.ts
index 1d20475ed..a28489aef 100644
--- a/libs/cypress/src/lib/graphql-test-utils.ts
+++ b/libs/cypress/src/lib/graphql-test-utils.ts
@@ -18,8 +18,9 @@ export const aliasQuery = (
if (hasOperationName(req, operationName)) {
req.alias = operationName;
if (data !== undefined) {
- req.reply((res) => {
- res.body.data = data;
+ req.reply({
+ statusCode: 200,
+ body: { data },
});
}
}
diff --git a/libs/deal-ticket/src/components/deal-ticket-manager.tsx b/libs/deal-ticket/src/components/deal-ticket-manager.tsx
index ba9996ef1..f23ec326b 100644
--- a/libs/deal-ticket/src/components/deal-ticket-manager.tsx
+++ b/libs/deal-ticket/src/components/deal-ticket-manager.tsx
@@ -2,9 +2,8 @@ import type { ReactNode } from 'react';
import { useEffect, useState } from 'react';
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
import { OrderStatus } from '@vegaprotocol/types';
-import { VegaTxStatus } from '@vegaprotocol/wallet';
+import { VegaOrderTransactionDialog, VegaTxStatus } from '@vegaprotocol/wallet';
import { DealTicket } from './deal-ticket';
-import { OrderDialog } from './order-dialog';
import { useOrderSubmit } from '../hooks/use-order-submit';
import type { DealTicketQuery_market } from './__generated__/DealTicketQuery';
@@ -49,12 +48,12 @@ export const DealTicketManager = ({
};
useEffect(() => {
- if (transaction.status !== VegaTxStatus.Default) {
+ if (transaction.status !== VegaTxStatus.Default || finalizedOrder) {
setOrderDialogOpen(true);
} else {
setOrderDialogOpen(false);
}
- }, [transaction.status]);
+ }, [finalizedOrder, transaction.status]);
return (
<>
@@ -82,7 +81,7 @@ export const DealTicketManager = ({
}}
intent={getDialogIntent(transaction.status)}
>
-
diff --git a/libs/deal-ticket/src/components/index.ts b/libs/deal-ticket/src/components/index.ts
index beb86e0d7..35b9f57ca 100644
--- a/libs/deal-ticket/src/components/index.ts
+++ b/libs/deal-ticket/src/components/index.ts
@@ -7,7 +7,6 @@ export * from './deal-ticket-market-amount';
export * from './deal-ticket';
export * from './expiry-selector';
export * from './info-market';
-export * from './order-dialog';
export * from './side-selector';
export * from './time-in-force-selector';
export * from './type-selector';
diff --git a/libs/deal-ticket/src/hooks/use-order-submit.spec.tsx b/libs/deal-ticket/src/hooks/use-order-submit.spec.tsx
index c2c4638ef..e4c45e03e 100644
--- a/libs/deal-ticket/src/hooks/use-order-submit.spec.tsx
+++ b/libs/deal-ticket/src/hooks/use-order-submit.spec.tsx
@@ -10,7 +10,7 @@ import { OrderSide, OrderTimeInForce, OrderType } from '@vegaprotocol/wallet';
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
import type { ReactNode } from 'react';
import { useOrderSubmit } from './use-order-submit';
-import type { DealTicketQuery_market } from '../__generated__/DealTicketQuery';
+import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
const defaultMarket: DealTicketQuery_market = {
__typename: 'Market',
diff --git a/libs/deal-ticket/src/hooks/use-order-submit.ts b/libs/deal-ticket/src/hooks/use-order-submit.ts
index 04fde0726..f4f3cfe6a 100644
--- a/libs/deal-ticket/src/hooks/use-order-submit.ts
+++ b/libs/deal-ticket/src/hooks/use-order-submit.ts
@@ -1,41 +1,19 @@
-import { useCallback, useEffect, useState } from 'react';
-import { gql, useSubscription } from '@apollo/client';
+import { useCallback, useState } from 'react';
+import { useSubscription } from '@apollo/client';
import type { Order } from '../utils/get-default-order';
-import { OrderType, useVegaWallet } from '@vegaprotocol/wallet';
-import { determineId, removeDecimal } from '@vegaprotocol/react-helpers';
-import { useVegaTransaction } from '@vegaprotocol/wallet';
-import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
import type {
OrderEvent,
OrderEventVariables,
OrderEvent_busEvents_event_Order,
-} from './__generated__/OrderEvent';
-
-const ORDER_EVENT_SUB = gql`
- subscription OrderEvent($partyId: ID!) {
- busEvents(partyId: $partyId, batchSize: 0, types: [Order]) {
- eventId
- block
- type
- event {
- ... on Order {
- type
- id
- status
- rejectionReason
- createdAt
- size
- price
- market {
- name
- decimalPlaces
- positionDecimalPlaces
- }
- }
- }
- }
- }
-`;
+} from '@vegaprotocol/wallet';
+import {
+ OrderType,
+ useVegaWallet,
+ ORDER_EVENT_SUB,
+} from '@vegaprotocol/wallet';
+import { determineId, removeDecimal } from '@vegaprotocol/react-helpers';
+import { useVegaTransaction } from '@vegaprotocol/wallet';
+import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
export const useOrderSubmit = (market: DealTicketQuery_market) => {
const { keypair } = useVegaWallet();
@@ -67,16 +45,11 @@ export const useOrderSubmit = (market: DealTicketQuery_market) => {
matchingOrderEvent.event.__typename === 'Order'
) {
setFinalizedOrder(matchingOrderEvent.event);
+ resetTransaction();
}
},
});
- useEffect(() => {
- if (finalizedOrder) {
- resetTransaction();
- }
- }, [finalizedOrder, resetTransaction]);
-
const submit = useCallback(
async (order: Order) => {
if (!keypair || !order.side) {
diff --git a/libs/environment/src/utils/compile-environment.ts b/libs/environment/src/utils/compile-environment.ts
index 9bbaa95b8..ecd91d909 100644
--- a/libs/environment/src/utils/compile-environment.ts
+++ b/libs/environment/src/utils/compile-environment.ts
@@ -58,6 +58,8 @@ const getBundledEnvironmentValue = (key: EnvKey) => {
return process.env['NX_ETHERSCAN_URL'];
case 'VEGA_NETWORKS':
return process.env['NX_VEGA_NETWORKS'];
+ case 'VEGA_EXPLORER_URL':
+ return process.env['NX_VEGA_EXPLORER_URL'];
}
};
diff --git a/libs/environment/src/utils/validate-environment.ts b/libs/environment/src/utils/validate-environment.ts
index b739e248f..44c89d8d1 100644
--- a/libs/environment/src/utils/validate-environment.ts
+++ b/libs/environment/src/utils/validate-environment.ts
@@ -14,6 +14,7 @@ export enum Networks {
const schemaObject = {
VEGA_URL: z.optional(z.string()),
+ VEGA_EXPLORER_URL: z.optional(z.string()),
VEGA_CONFIG_URL: z.optional(z.string()),
ETHEREUM_PROVIDER_URL: z.string().url({
message:
diff --git a/libs/fills/.babelrc b/libs/fills/.babelrc
new file mode 100644
index 000000000..ccae900be
--- /dev/null
+++ b/libs/fills/.babelrc
@@ -0,0 +1,12 @@
+{
+ "presets": [
+ [
+ "@nrwl/react/babel",
+ {
+ "runtime": "automatic",
+ "useBuiltIns": "usage"
+ }
+ ]
+ ],
+ "plugins": []
+}
diff --git a/libs/fills/.eslintrc.json b/libs/fills/.eslintrc.json
new file mode 100644
index 000000000..db820c5d0
--- /dev/null
+++ b/libs/fills/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+ "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
+ "ignorePatterns": ["!**/*", "__generated__"],
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+ "rules": {}
+ },
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {}
+ },
+ {
+ "files": ["*.js", "*.jsx"],
+ "rules": {}
+ }
+ ]
+}
diff --git a/libs/fills/.storybook/main.js b/libs/fills/.storybook/main.js
new file mode 100644
index 000000000..9997fd7a1
--- /dev/null
+++ b/libs/fills/.storybook/main.js
@@ -0,0 +1,28 @@
+const rootMain = require('../../../.storybook/main');
+
+module.exports = {
+ ...rootMain,
+
+ core: { ...rootMain.core, builder: 'webpack5' },
+
+ stories: [
+ ...rootMain.stories,
+ '../src/lib/**/*.stories.mdx',
+ '../src/lib/**/*.stories.@(js|jsx|ts|tsx)',
+ ],
+ addons: [
+ ...rootMain.addons,
+ '@nrwl/react/plugins/storybook',
+ 'storybook-addon-themes',
+ ],
+ webpackFinal: async (config, { configType }) => {
+ // apply any global webpack configs that might have been specified in .storybook/main.js
+ if (rootMain.webpackFinal) {
+ config = await rootMain.webpackFinal(config, { configType });
+ }
+
+ // add your own webpack tweaks if needed
+
+ return config;
+ },
+};
diff --git a/libs/fills/.storybook/preview-head.html b/libs/fills/.storybook/preview-head.html
new file mode 100644
index 000000000..dd2e70030
--- /dev/null
+++ b/libs/fills/.storybook/preview-head.html
@@ -0,0 +1 @@
+
diff --git a/libs/fills/.storybook/preview.js b/libs/fills/.storybook/preview.js
new file mode 100644
index 000000000..fd45f3d42
--- /dev/null
+++ b/libs/fills/.storybook/preview.js
@@ -0,0 +1,51 @@
+import './styles.css';
+import { ThemeContext } from '@vegaprotocol/react-helpers';
+import { useEffect, useState } from 'react';
+
+export const parameters = {
+ actions: { argTypesRegex: '^on[A-Z].*' },
+ backgrounds: { disable: true },
+ themes: {
+ default: 'dark',
+ list: [
+ { name: 'dark', class: ['dark', 'bg-black'], color: '#000' },
+ { name: 'light', class: '', color: '#FFF' },
+ ],
+ },
+};
+
+export const decorators = [
+ (Story, context) => {
+ // storybook-addon-themes doesnt seem to provide the current selected
+ // theme in context, we need to provid it in JS as some components
+ // rely on it for rendering
+ const [theme, setTheme] = useState(context.parameters.themes.default);
+
+ useEffect(() => {
+ const observer = new MutationObserver((mutationList) => {
+ if (mutationList.length) {
+ const body = mutationList[0].target;
+ if (body.classList.contains('dark')) {
+ setTheme('dark');
+ } else {
+ setTheme('light');
+ }
+ }
+ });
+
+ observer.observe(document.body, { attributes: true });
+
+ return () => {
+ observer.disconnect();
+ };
+ }, []);
+
+ return (
+
+
+
+
+
+ );
+ },
+];
diff --git a/libs/fills/.storybook/styles.css b/libs/fills/.storybook/styles.css
new file mode 100644
index 000000000..b5c61c956
--- /dev/null
+++ b/libs/fills/.storybook/styles.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/libs/fills/.storybook/tsconfig.json b/libs/fills/.storybook/tsconfig.json
new file mode 100644
index 000000000..7a1170995
--- /dev/null
+++ b/libs/fills/.storybook/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "emitDecoratorMetadata": true,
+ "outDir": ""
+ },
+ "files": [
+ "../../../node_modules/@nrwl/react/typings/styled-jsx.d.ts",
+ "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
+ "../../../node_modules/@nrwl/react/typings/image.d.ts"
+ ],
+ "exclude": [
+ "../**/*.spec.ts",
+ "../**/*.spec.js",
+ "../**/*.spec.tsx",
+ "../**/*.spec.jsx"
+ ],
+ "include": ["../src/**/*", "*.js"]
+}
diff --git a/libs/fills/README.md b/libs/fills/README.md
new file mode 100644
index 000000000..080b67b03
--- /dev/null
+++ b/libs/fills/README.md
@@ -0,0 +1,7 @@
+# fills
+
+This library was generated with [Nx](https://nx.dev).
+
+## Running unit tests
+
+Run `nx test fills` to execute the unit tests via [Jest](https://jestjs.io).
diff --git a/libs/fills/jest.config.js b/libs/fills/jest.config.js
new file mode 100644
index 000000000..5daeb4ac3
--- /dev/null
+++ b/libs/fills/jest.config.js
@@ -0,0 +1,15 @@
+module.exports = {
+ displayName: 'fills',
+ preset: '../../jest.preset.js',
+ globals: {
+ 'ts-jest': {
+ tsconfig: '/tsconfig.spec.json',
+ },
+ },
+ transform: {
+ '^.+\\.[tj]sx?$': 'ts-jest',
+ },
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
+ coverageDirectory: '../../coverage/libs/fills',
+ setupFilesAfterEnv: ['./src/setup-tests.ts'],
+};
diff --git a/libs/fills/package.json b/libs/fills/package.json
new file mode 100644
index 000000000..cb30ed0b7
--- /dev/null
+++ b/libs/fills/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "@vegaprotocol/fills",
+ "version": "0.0.1"
+}
diff --git a/libs/fills/postcss.config.js b/libs/fills/postcss.config.js
new file mode 100644
index 000000000..cbdd9c22c
--- /dev/null
+++ b/libs/fills/postcss.config.js
@@ -0,0 +1,10 @@
+const { join } = require('path');
+
+module.exports = {
+ plugins: {
+ tailwindcss: {
+ config: join(__dirname, 'tailwind.config.js'),
+ },
+ autoprefixer: {},
+ },
+};
diff --git a/libs/fills/project.json b/libs/fills/project.json
new file mode 100644
index 000000000..96f9c9196
--- /dev/null
+++ b/libs/fills/project.json
@@ -0,0 +1,74 @@
+{
+ "root": "libs/fills",
+ "sourceRoot": "libs/fills/src",
+ "projectType": "library",
+ "tags": [],
+ "targets": {
+ "build": {
+ "executor": "@nrwl/web:rollup",
+ "outputs": ["{options.outputPath}"],
+ "options": {
+ "outputPath": "dist/libs/fills",
+ "tsConfig": "libs/fills/tsconfig.lib.json",
+ "project": "libs/fills/package.json",
+ "entryFile": "libs/fills/src/index.ts",
+ "external": ["react/jsx-runtime"],
+ "rollupConfig": "@nrwl/react/plugins/bundle-rollup",
+ "compiler": "babel",
+ "assets": [
+ {
+ "glob": "libs/fills/README.md",
+ "input": ".",
+ "output": "."
+ }
+ ]
+ }
+ },
+ "lint": {
+ "executor": "@nrwl/linter:eslint",
+ "outputs": ["{options.outputFile}"],
+ "options": {
+ "lintFilePatterns": ["libs/fills/**/*.{ts,tsx,js,jsx}"]
+ }
+ },
+ "test": {
+ "executor": "@nrwl/jest:jest",
+ "outputs": ["coverage/libs/fills"],
+ "options": {
+ "jestConfig": "libs/fills/jest.config.js",
+ "passWithNoTests": true
+ }
+ },
+ "storybook": {
+ "executor": "@nrwl/storybook:storybook",
+ "options": {
+ "uiFramework": "@storybook/react",
+ "port": 4400,
+ "config": {
+ "configFolder": "libs/fills/.storybook"
+ }
+ },
+ "configurations": {
+ "ci": {
+ "quiet": true
+ }
+ }
+ },
+ "build-storybook": {
+ "executor": "@nrwl/storybook:build",
+ "outputs": ["{options.outputPath}"],
+ "options": {
+ "uiFramework": "@storybook/react",
+ "outputPath": "dist/storybook/fills",
+ "config": {
+ "configFolder": "libs/fills/.storybook"
+ }
+ },
+ "configurations": {
+ "ci": {
+ "quiet": true
+ }
+ }
+ }
+ }
+}
diff --git a/libs/fills/src/index.ts b/libs/fills/src/index.ts
new file mode 100644
index 000000000..76bbc310e
--- /dev/null
+++ b/libs/fills/src/index.ts
@@ -0,0 +1,4 @@
+export * from './lib/fills-container';
+export * from './lib/__generated__/FillFields';
+export * from './lib/__generated__/Fills';
+export * from './lib/__generated__/FillsSub';
diff --git a/libs/fills/src/lib/__generated__/FillFields.ts b/libs/fills/src/lib/__generated__/FillFields.ts
new file mode 100644
index 000000000..426e600ee
--- /dev/null
+++ b/libs/fills/src/lib/__generated__/FillFields.ts
@@ -0,0 +1,197 @@
+/* tslint:disable */
+/* eslint-disable */
+// @generated
+// This file was automatically generated and should not be edited.
+
+import { Side } from "@vegaprotocol/types";
+
+// ====================================================
+// GraphQL fragment: FillFields
+// ====================================================
+
+export interface FillFields_buyer {
+ __typename: "Party";
+ /**
+ * Party identifier
+ */
+ id: string;
+}
+
+export interface FillFields_seller {
+ __typename: "Party";
+ /**
+ * Party identifier
+ */
+ id: string;
+}
+
+export interface FillFields_buyerFee {
+ __typename: "TradeFee";
+ /**
+ * The maker fee, aggressive party to the other party (the one who had an order in the book)
+ */
+ makerFee: string;
+ /**
+ * The infrastructure fee, a fee paid to the node runner to maintain the vega network
+ */
+ infrastructureFee: string;
+ /**
+ * The fee paid to the market makers to provide liquidity in the market
+ */
+ liquidityFee: string;
+}
+
+export interface FillFields_sellerFee {
+ __typename: "TradeFee";
+ /**
+ * The maker fee, aggressive party to the other party (the one who had an order in the book)
+ */
+ makerFee: string;
+ /**
+ * The infrastructure fee, a fee paid to the node runner to maintain the vega network
+ */
+ infrastructureFee: string;
+ /**
+ * The fee paid to the market makers to provide liquidity in the market
+ */
+ liquidityFee: string;
+}
+
+export interface FillFields_market_tradableInstrument_instrument_product_settlementAsset {
+ __typename: "Asset";
+ /**
+ * The id of the asset
+ */
+ id: string;
+ /**
+ * The symbol of the asset (e.g: GBP)
+ */
+ symbol: string;
+ /**
+ * The precision of the asset
+ */
+ decimals: number;
+}
+
+export interface FillFields_market_tradableInstrument_instrument_product {
+ __typename: "Future";
+ /**
+ * The name of the asset (string)
+ */
+ settlementAsset: FillFields_market_tradableInstrument_instrument_product_settlementAsset;
+}
+
+export interface FillFields_market_tradableInstrument_instrument {
+ __typename: "Instrument";
+ /**
+ * Uniquely identify an instrument across all instruments available on Vega (string)
+ */
+ id: string;
+ /**
+ * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
+ */
+ code: string;
+ /**
+ * A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
+ */
+ product: FillFields_market_tradableInstrument_instrument_product;
+}
+
+export interface FillFields_market_tradableInstrument {
+ __typename: "TradableInstrument";
+ /**
+ * An instance of or reference to a fully specified instrument.
+ */
+ instrument: FillFields_market_tradableInstrument_instrument;
+}
+
+export interface FillFields_market {
+ __typename: "Market";
+ /**
+ * Market ID
+ */
+ id: string;
+ /**
+ * Market full name
+ */
+ name: string;
+ /**
+ * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
+ * number denominated in the currency of the Market. (uint64)
+ *
+ * Examples:
+ * Currency Balance decimalPlaces Real Balance
+ * GBP 100 0 GBP 100
+ * GBP 100 2 GBP 1.00
+ * GBP 100 4 GBP 0.01
+ * GBP 1 4 GBP 0.0001 ( 0.01p )
+ *
+ * GBX (pence) 100 0 GBP 1.00 (100p )
+ * GBX (pence) 100 2 GBP 0.01 ( 1p )
+ * GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
+ * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
+ */
+ decimalPlaces: number;
+ /**
+ * positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
+ * i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
+ * 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
+ */
+ positionDecimalPlaces: number;
+ /**
+ * An instance of or reference to a tradable instrument.
+ */
+ tradableInstrument: FillFields_market_tradableInstrument;
+}
+
+export interface FillFields {
+ __typename: "Trade";
+ /**
+ * The hash of the trade data
+ */
+ id: string;
+ /**
+ * RFC3339Nano time for when the trade occurred
+ */
+ createdAt: string;
+ /**
+ * The price of the trade (probably initially the passive order price, other determination algorithms are possible though) (uint64)
+ */
+ price: string;
+ /**
+ * The number of contracts trades, will always be <= the remaining size of both orders immediately before the trade (uint64)
+ */
+ size: string;
+ /**
+ * The order that bought
+ */
+ buyOrder: string;
+ /**
+ * The order that sold
+ */
+ sellOrder: string;
+ /**
+ * The aggressor indicates whether this trade was related to a BUY or SELL
+ */
+ aggressor: Side;
+ /**
+ * The party that bought
+ */
+ buyer: FillFields_buyer;
+ /**
+ * The party that sold
+ */
+ seller: FillFields_seller;
+ /**
+ * The fee paid by the buyer side of the trade
+ */
+ buyerFee: FillFields_buyerFee;
+ /**
+ * The fee paid by the seller side of the trade
+ */
+ sellerFee: FillFields_sellerFee;
+ /**
+ * The market the trade occurred on
+ */
+ market: FillFields_market;
+}
diff --git a/libs/fills/src/lib/__generated__/Fills.ts b/libs/fills/src/lib/__generated__/Fills.ts
new file mode 100644
index 000000000..623edabeb
--- /dev/null
+++ b/libs/fills/src/lib/__generated__/Fills.ts
@@ -0,0 +1,247 @@
+/* tslint:disable */
+/* eslint-disable */
+// @generated
+// This file was automatically generated and should not be edited.
+
+import { Pagination, Side } from "@vegaprotocol/types";
+
+// ====================================================
+// GraphQL query operation: Fills
+// ====================================================
+
+export interface Fills_party_tradesPaged_edges_node_buyer {
+ __typename: "Party";
+ /**
+ * Party identifier
+ */
+ id: string;
+}
+
+export interface Fills_party_tradesPaged_edges_node_seller {
+ __typename: "Party";
+ /**
+ * Party identifier
+ */
+ id: string;
+}
+
+export interface Fills_party_tradesPaged_edges_node_buyerFee {
+ __typename: "TradeFee";
+ /**
+ * The maker fee, aggressive party to the other party (the one who had an order in the book)
+ */
+ makerFee: string;
+ /**
+ * The infrastructure fee, a fee paid to the node runner to maintain the vega network
+ */
+ infrastructureFee: string;
+ /**
+ * The fee paid to the market makers to provide liquidity in the market
+ */
+ liquidityFee: string;
+}
+
+export interface Fills_party_tradesPaged_edges_node_sellerFee {
+ __typename: "TradeFee";
+ /**
+ * The maker fee, aggressive party to the other party (the one who had an order in the book)
+ */
+ makerFee: string;
+ /**
+ * The infrastructure fee, a fee paid to the node runner to maintain the vega network
+ */
+ infrastructureFee: string;
+ /**
+ * The fee paid to the market makers to provide liquidity in the market
+ */
+ liquidityFee: string;
+}
+
+export interface Fills_party_tradesPaged_edges_node_market_tradableInstrument_instrument_product_settlementAsset {
+ __typename: "Asset";
+ /**
+ * The id of the asset
+ */
+ id: string;
+ /**
+ * The symbol of the asset (e.g: GBP)
+ */
+ symbol: string;
+ /**
+ * The precision of the asset
+ */
+ decimals: number;
+}
+
+export interface Fills_party_tradesPaged_edges_node_market_tradableInstrument_instrument_product {
+ __typename: "Future";
+ /**
+ * The name of the asset (string)
+ */
+ settlementAsset: Fills_party_tradesPaged_edges_node_market_tradableInstrument_instrument_product_settlementAsset;
+}
+
+export interface Fills_party_tradesPaged_edges_node_market_tradableInstrument_instrument {
+ __typename: "Instrument";
+ /**
+ * Uniquely identify an instrument across all instruments available on Vega (string)
+ */
+ id: string;
+ /**
+ * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
+ */
+ code: string;
+ /**
+ * A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
+ */
+ product: Fills_party_tradesPaged_edges_node_market_tradableInstrument_instrument_product;
+}
+
+export interface Fills_party_tradesPaged_edges_node_market_tradableInstrument {
+ __typename: "TradableInstrument";
+ /**
+ * An instance of or reference to a fully specified instrument.
+ */
+ instrument: Fills_party_tradesPaged_edges_node_market_tradableInstrument_instrument;
+}
+
+export interface Fills_party_tradesPaged_edges_node_market {
+ __typename: "Market";
+ /**
+ * Market ID
+ */
+ id: string;
+ /**
+ * Market full name
+ */
+ name: string;
+ /**
+ * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
+ * number denominated in the currency of the Market. (uint64)
+ *
+ * Examples:
+ * Currency Balance decimalPlaces Real Balance
+ * GBP 100 0 GBP 100
+ * GBP 100 2 GBP 1.00
+ * GBP 100 4 GBP 0.01
+ * GBP 1 4 GBP 0.0001 ( 0.01p )
+ *
+ * GBX (pence) 100 0 GBP 1.00 (100p )
+ * GBX (pence) 100 2 GBP 0.01 ( 1p )
+ * GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
+ * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
+ */
+ decimalPlaces: number;
+ /**
+ * positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
+ * i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
+ * 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
+ */
+ positionDecimalPlaces: number;
+ /**
+ * An instance of or reference to a tradable instrument.
+ */
+ tradableInstrument: Fills_party_tradesPaged_edges_node_market_tradableInstrument;
+}
+
+export interface Fills_party_tradesPaged_edges_node {
+ __typename: "Trade";
+ /**
+ * The hash of the trade data
+ */
+ id: string;
+ /**
+ * RFC3339Nano time for when the trade occurred
+ */
+ createdAt: string;
+ /**
+ * The price of the trade (probably initially the passive order price, other determination algorithms are possible though) (uint64)
+ */
+ price: string;
+ /**
+ * The number of contracts trades, will always be <= the remaining size of both orders immediately before the trade (uint64)
+ */
+ size: string;
+ /**
+ * The order that bought
+ */
+ buyOrder: string;
+ /**
+ * The order that sold
+ */
+ sellOrder: string;
+ /**
+ * The aggressor indicates whether this trade was related to a BUY or SELL
+ */
+ aggressor: Side;
+ /**
+ * The party that bought
+ */
+ buyer: Fills_party_tradesPaged_edges_node_buyer;
+ /**
+ * The party that sold
+ */
+ seller: Fills_party_tradesPaged_edges_node_seller;
+ /**
+ * The fee paid by the buyer side of the trade
+ */
+ buyerFee: Fills_party_tradesPaged_edges_node_buyerFee;
+ /**
+ * The fee paid by the seller side of the trade
+ */
+ sellerFee: Fills_party_tradesPaged_edges_node_sellerFee;
+ /**
+ * The market the trade occurred on
+ */
+ market: Fills_party_tradesPaged_edges_node_market;
+}
+
+export interface Fills_party_tradesPaged_edges {
+ __typename: "TradeEdge";
+ node: Fills_party_tradesPaged_edges_node;
+ cursor: string;
+}
+
+export interface Fills_party_tradesPaged_pageInfo {
+ __typename: "PageInfo";
+ startCursor: string;
+ endCursor: string;
+}
+
+export interface Fills_party_tradesPaged {
+ __typename: "TradeConnection";
+ /**
+ * The total number of trades in this connection
+ */
+ totalCount: number;
+ /**
+ * The trade in this connection
+ */
+ edges: Fills_party_tradesPaged_edges[];
+ /**
+ * The pagination information
+ */
+ pageInfo: Fills_party_tradesPaged_pageInfo;
+}
+
+export interface Fills_party {
+ __typename: "Party";
+ /**
+ * Party identifier
+ */
+ id: string;
+ tradesPaged: Fills_party_tradesPaged;
+}
+
+export interface Fills {
+ /**
+ * An entity that is trading on the VEGA network
+ */
+ party: Fills_party | null;
+}
+
+export interface FillsVariables {
+ partyId: string;
+ marketId?: string | null;
+ pagination?: Pagination | null;
+}
diff --git a/libs/fills/src/lib/__generated__/FillsSub.ts b/libs/fills/src/lib/__generated__/FillsSub.ts
new file mode 100644
index 000000000..c1b056cf0
--- /dev/null
+++ b/libs/fills/src/lib/__generated__/FillsSub.ts
@@ -0,0 +1,208 @@
+/* tslint:disable */
+/* eslint-disable */
+// @generated
+// This file was automatically generated and should not be edited.
+
+import { Side } from "@vegaprotocol/types";
+
+// ====================================================
+// GraphQL subscription operation: FillsSub
+// ====================================================
+
+export interface FillsSub_trades_buyer {
+ __typename: "Party";
+ /**
+ * Party identifier
+ */
+ id: string;
+}
+
+export interface FillsSub_trades_seller {
+ __typename: "Party";
+ /**
+ * Party identifier
+ */
+ id: string;
+}
+
+export interface FillsSub_trades_buyerFee {
+ __typename: "TradeFee";
+ /**
+ * The maker fee, aggressive party to the other party (the one who had an order in the book)
+ */
+ makerFee: string;
+ /**
+ * The infrastructure fee, a fee paid to the node runner to maintain the vega network
+ */
+ infrastructureFee: string;
+ /**
+ * The fee paid to the market makers to provide liquidity in the market
+ */
+ liquidityFee: string;
+}
+
+export interface FillsSub_trades_sellerFee {
+ __typename: "TradeFee";
+ /**
+ * The maker fee, aggressive party to the other party (the one who had an order in the book)
+ */
+ makerFee: string;
+ /**
+ * The infrastructure fee, a fee paid to the node runner to maintain the vega network
+ */
+ infrastructureFee: string;
+ /**
+ * The fee paid to the market makers to provide liquidity in the market
+ */
+ liquidityFee: string;
+}
+
+export interface FillsSub_trades_market_tradableInstrument_instrument_product_settlementAsset {
+ __typename: "Asset";
+ /**
+ * The id of the asset
+ */
+ id: string;
+ /**
+ * The symbol of the asset (e.g: GBP)
+ */
+ symbol: string;
+ /**
+ * The precision of the asset
+ */
+ decimals: number;
+}
+
+export interface FillsSub_trades_market_tradableInstrument_instrument_product {
+ __typename: "Future";
+ /**
+ * The name of the asset (string)
+ */
+ settlementAsset: FillsSub_trades_market_tradableInstrument_instrument_product_settlementAsset;
+}
+
+export interface FillsSub_trades_market_tradableInstrument_instrument {
+ __typename: "Instrument";
+ /**
+ * Uniquely identify an instrument across all instruments available on Vega (string)
+ */
+ id: string;
+ /**
+ * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
+ */
+ code: string;
+ /**
+ * A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
+ */
+ product: FillsSub_trades_market_tradableInstrument_instrument_product;
+}
+
+export interface FillsSub_trades_market_tradableInstrument {
+ __typename: "TradableInstrument";
+ /**
+ * An instance of or reference to a fully specified instrument.
+ */
+ instrument: FillsSub_trades_market_tradableInstrument_instrument;
+}
+
+export interface FillsSub_trades_market {
+ __typename: "Market";
+ /**
+ * Market ID
+ */
+ id: string;
+ /**
+ * Market full name
+ */
+ name: string;
+ /**
+ * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
+ * number denominated in the currency of the Market. (uint64)
+ *
+ * Examples:
+ * Currency Balance decimalPlaces Real Balance
+ * GBP 100 0 GBP 100
+ * GBP 100 2 GBP 1.00
+ * GBP 100 4 GBP 0.01
+ * GBP 1 4 GBP 0.0001 ( 0.01p )
+ *
+ * GBX (pence) 100 0 GBP 1.00 (100p )
+ * GBX (pence) 100 2 GBP 0.01 ( 1p )
+ * GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
+ * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
+ */
+ decimalPlaces: number;
+ /**
+ * positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
+ * i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
+ * 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
+ */
+ positionDecimalPlaces: number;
+ /**
+ * An instance of or reference to a tradable instrument.
+ */
+ tradableInstrument: FillsSub_trades_market_tradableInstrument;
+}
+
+export interface FillsSub_trades {
+ __typename: "Trade";
+ /**
+ * The hash of the trade data
+ */
+ id: string;
+ /**
+ * RFC3339Nano time for when the trade occurred
+ */
+ createdAt: string;
+ /**
+ * The price of the trade (probably initially the passive order price, other determination algorithms are possible though) (uint64)
+ */
+ price: string;
+ /**
+ * The number of contracts trades, will always be <= the remaining size of both orders immediately before the trade (uint64)
+ */
+ size: string;
+ /**
+ * The order that bought
+ */
+ buyOrder: string;
+ /**
+ * The order that sold
+ */
+ sellOrder: string;
+ /**
+ * The aggressor indicates whether this trade was related to a BUY or SELL
+ */
+ aggressor: Side;
+ /**
+ * The party that bought
+ */
+ buyer: FillsSub_trades_buyer;
+ /**
+ * The party that sold
+ */
+ seller: FillsSub_trades_seller;
+ /**
+ * The fee paid by the buyer side of the trade
+ */
+ buyerFee: FillsSub_trades_buyerFee;
+ /**
+ * The fee paid by the seller side of the trade
+ */
+ sellerFee: FillsSub_trades_sellerFee;
+ /**
+ * The market the trade occurred on
+ */
+ market: FillsSub_trades_market;
+}
+
+export interface FillsSub {
+ /**
+ * Subscribe to the trades updates
+ */
+ trades: FillsSub_trades[] | null;
+}
+
+export interface FillsSubVariables {
+ partyId: string;
+}
diff --git a/libs/fills/src/lib/fills-container.tsx b/libs/fills/src/lib/fills-container.tsx
new file mode 100644
index 000000000..f5def05a1
--- /dev/null
+++ b/libs/fills/src/lib/fills-container.tsx
@@ -0,0 +1,18 @@
+import { t } from '@vegaprotocol/react-helpers';
+import { Splash } from '@vegaprotocol/ui-toolkit';
+import { useVegaWallet } from '@vegaprotocol/wallet';
+import { FillsManager } from './fills-manager';
+
+export const FillsContainer = () => {
+ const { keypair } = useVegaWallet();
+
+ if (!keypair) {
+ return (
+
+ {t('Please connect Vega wallet')}
+
+ );
+ }
+
+ return ;
+};
diff --git a/libs/fills/src/lib/fills-data-provider.ts b/libs/fills/src/lib/fills-data-provider.ts
new file mode 100644
index 000000000..d22993790
--- /dev/null
+++ b/libs/fills/src/lib/fills-data-provider.ts
@@ -0,0 +1,117 @@
+import { gql } from '@apollo/client';
+import { makeDataProvider } from '@vegaprotocol/react-helpers';
+import produce from 'immer';
+import type { FillFields } from './__generated__/FillFields';
+import type {
+ Fills,
+ Fills_party_tradesPaged_edges_node,
+} from './__generated__/Fills';
+import type { FillsSub } from './__generated__/FillsSub';
+
+const FILL_FRAGMENT = gql`
+ fragment FillFields on Trade {
+ id
+ createdAt
+ price
+ size
+ buyOrder
+ sellOrder
+ aggressor
+ buyer {
+ id
+ }
+ seller {
+ id
+ }
+ buyerFee {
+ makerFee
+ infrastructureFee
+ liquidityFee
+ }
+ sellerFee {
+ makerFee
+ infrastructureFee
+ liquidityFee
+ }
+ market {
+ id
+ name
+ decimalPlaces
+ positionDecimalPlaces
+ tradableInstrument {
+ instrument {
+ id
+ code
+ product {
+ ... on Future {
+ settlementAsset {
+ id
+ symbol
+ decimals
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+`;
+
+export const FILLS_QUERY = gql`
+ ${FILL_FRAGMENT}
+ query Fills($partyId: ID!, $marketId: ID, $pagination: Pagination) {
+ party(id: $partyId) {
+ id
+ tradesPaged(marketId: $marketId, pagination: $pagination) {
+ totalCount
+ edges {
+ node {
+ ...FillFields
+ }
+ cursor
+ }
+ pageInfo {
+ startCursor
+ endCursor
+ }
+ }
+ }
+ }
+`;
+
+export const FILLS_SUB = gql`
+ ${FILL_FRAGMENT}
+ subscription FillsSub($partyId: ID!) {
+ trades(partyId: $partyId) {
+ ...FillFields
+ }
+ }
+`;
+
+const update = (data: FillFields[], delta: FillFields[]) => {
+ // Add or update incoming trades
+ return produce(data, (draft) => {
+ delta.forEach((trade) => {
+ const index = draft.findIndex((t) => t.id === trade.id);
+ if (index === -1) {
+ draft.unshift(trade);
+ } else {
+ draft[index] = trade;
+ }
+ });
+ });
+};
+
+const getData = (
+ responseData: Fills
+): Fills_party_tradesPaged_edges_node[] | null =>
+ responseData.party?.tradesPaged.edges.map((e) => e.node) || null;
+const getDelta = (subscriptionData: FillsSub) => subscriptionData.trades || [];
+
+export const fillsDataProvider = makeDataProvider(
+ FILLS_QUERY,
+ FILLS_SUB,
+ update,
+ getData,
+ getDelta
+);
diff --git a/libs/fills/src/lib/fills-manager.tsx b/libs/fills/src/lib/fills-manager.tsx
new file mode 100644
index 000000000..0b1727e65
--- /dev/null
+++ b/libs/fills/src/lib/fills-manager.tsx
@@ -0,0 +1,80 @@
+import type { AgGridReact } from 'ag-grid-react';
+import { useCallback, useMemo, useRef } from 'react';
+import { FillsTable } from './fills-table';
+import { fillsDataProvider } from './fills-data-provider';
+import { useDataProvider } from '@vegaprotocol/react-helpers';
+import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
+import type { FillsVariables } from './__generated__/Fills';
+import type { FillFields } from './__generated__/FillFields';
+import type { FillsSub_trades } from './__generated__/FillsSub';
+import isEqual from 'lodash/isEqual';
+
+interface FillsManagerProps {
+ partyId: string;
+}
+
+export const FillsManager = ({ partyId }: FillsManagerProps) => {
+ const gridRef = useRef(null);
+ const variables = useMemo(
+ () => ({
+ partyId,
+ pagination: {
+ last: 300,
+ },
+ }),
+ [partyId]
+ );
+ const update = useCallback((delta: FillsSub_trades[]) => {
+ if (!gridRef.current) {
+ return false;
+ }
+ const updateRows: FillFields[] = [];
+ const add: FillFields[] = [];
+
+ delta.forEach((d) => {
+ if (!gridRef.current?.api) {
+ return;
+ }
+
+ const rowNode = gridRef.current.api.getRowNode(d.id);
+
+ if (rowNode) {
+ if (!isEqual(d, rowNode.data)) {
+ updateRows.push(d);
+ }
+ } else {
+ add.push(d);
+ }
+ });
+
+ if (updateRows.length || add.length) {
+ gridRef.current.api.applyTransactionAsync({
+ update: updateRows,
+ add,
+ addIndex: 0,
+ });
+ }
+
+ return true;
+ }, []);
+
+ const { data, loading, error } = useDataProvider(
+ fillsDataProvider,
+ update,
+ variables
+ );
+
+ const fills = useMemo(() => {
+ if (!data?.length) {
+ return [];
+ }
+
+ return data;
+ }, [data]);
+
+ return (
+
+
+
+ );
+};
diff --git a/libs/fills/src/lib/fills-table.spec.tsx b/libs/fills/src/lib/fills-table.spec.tsx
new file mode 100644
index 000000000..df98acf11
--- /dev/null
+++ b/libs/fills/src/lib/fills-table.spec.tsx
@@ -0,0 +1,177 @@
+import { render, act, screen, waitFor } from '@testing-library/react';
+import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
+import { Side } from '@vegaprotocol/types';
+import type { PartialDeep } from 'type-fest';
+
+import { FillsTable } from './fills-table';
+import { generateFill } from './test-helpers';
+import type { FillFields } from './__generated__/FillFields';
+
+describe('FillsTable', () => {
+ let defaultFill: PartialDeep;
+
+ beforeEach(() => {
+ defaultFill = {
+ price: '100',
+ size: '300000',
+ market: {
+ name: 'test market',
+ decimalPlaces: 2,
+ positionDecimalPlaces: 5,
+ tradableInstrument: {
+ instrument: {
+ product: {
+ settlementAsset: {
+ decimals: 2,
+ symbol: 'BTC',
+ },
+ },
+ },
+ },
+ },
+ createdAt: new Date('2022-02-02T14:00:00').toISOString(),
+ };
+ });
+
+ it('correct columns are rendered', async () => {
+ await act(async () => {
+ render( );
+ });
+
+ const headers = screen.getAllByRole('columnheader');
+ expect(headers).toHaveLength(7);
+ expect(headers.map((h) => h.textContent?.trim())).toEqual([
+ 'Market',
+ 'Amount',
+ 'Value',
+ 'Filled value',
+ 'Role',
+ 'Fee',
+ 'Date',
+ ]);
+ });
+
+ it('formats cells correctly for buyer fill', async () => {
+ const partyId = 'party-id';
+ const buyerFill = generateFill({
+ ...defaultFill,
+ buyer: {
+ id: partyId,
+ },
+ aggressor: Side.Sell,
+ buyerFee: {
+ makerFee: '2',
+ infrastructureFee: '2',
+ liquidityFee: '2',
+ },
+ });
+
+ const { container } = render(
+
+ );
+
+ // Check grid has been rendered
+ await waitFor(() => {
+ expect(container.querySelector('.ag-root-wrapper')).toBeInTheDocument();
+ });
+
+ const cells = screen.getAllByRole('gridcell');
+ const expectedValues = [
+ buyerFill.market.name,
+ '+3.00000',
+ '1.00 BTC',
+ '3.00 BTC',
+ 'Maker',
+ '0.06 BTC',
+ getDateTimeFormat().format(new Date(buyerFill.createdAt)),
+ ];
+ cells.forEach((cell, i) => {
+ expect(cell).toHaveTextContent(expectedValues[i]);
+ });
+
+ const amountCell = cells.find((c) => c.getAttribute('col-id') === 'size');
+ expect(amountCell).toHaveClass('text-vega-green');
+ });
+
+ it('formats cells correctly for seller fill', async () => {
+ const partyId = 'party-id';
+ const buyerFill = generateFill({
+ ...defaultFill,
+ seller: {
+ id: partyId,
+ },
+ aggressor: Side.Sell,
+ sellerFee: {
+ makerFee: '1',
+ infrastructureFee: '1',
+ liquidityFee: '1',
+ },
+ });
+
+ const { container } = render(
+
+ );
+
+ // Check grid has been rendered
+ await waitFor(() => {
+ expect(container.querySelector('.ag-root-wrapper')).toBeInTheDocument();
+ });
+
+ const cells = screen.getAllByRole('gridcell');
+ const expectedValues = [
+ buyerFill.market.name,
+ '-3.00000',
+ '1.00 BTC',
+ '3.00 BTC',
+ 'Taker',
+ '0.03 BTC',
+ getDateTimeFormat().format(new Date(buyerFill.createdAt)),
+ ];
+ cells.forEach((cell, i) => {
+ expect(cell).toHaveTextContent(expectedValues[i]);
+ });
+
+ const amountCell = cells.find((c) => c.getAttribute('col-id') === 'size');
+ expect(amountCell).toHaveClass('text-vega-red');
+ });
+
+ it('renders correct maker or taker role', async () => {
+ const partyId = 'party-id';
+ const takerFill = generateFill({
+ seller: {
+ id: partyId,
+ },
+ aggressor: Side.Sell,
+ });
+
+ const { container, rerender } = render(
+
+ );
+
+ // Check grid has been rendered
+ await waitFor(() => {
+ expect(container.querySelector('.ag-root-wrapper')).toBeInTheDocument();
+ });
+
+ expect(
+ screen
+ .getAllByRole('gridcell')
+ .find((c) => c.getAttribute('col-id') === 'aggressor')
+ ).toHaveTextContent('Taker');
+
+ const makerFill = generateFill({
+ seller: {
+ id: partyId,
+ },
+ aggressor: Side.Buy,
+ });
+
+ rerender( );
+
+ expect(
+ screen
+ .getAllByRole('gridcell')
+ .find((c) => c.getAttribute('col-id') === 'aggressor')
+ ).toHaveTextContent('Maker');
+ });
+});
diff --git a/libs/fills/src/lib/fills-table.stories.tsx b/libs/fills/src/lib/fills-table.stories.tsx
new file mode 100644
index 000000000..d24a8b69c
--- /dev/null
+++ b/libs/fills/src/lib/fills-table.stories.tsx
@@ -0,0 +1,18 @@
+import type { Story, Meta } from '@storybook/react';
+import type { FillsTableProps } from './fills-table';
+import { FillsTable } from './fills-table';
+import { generateFills } from './test-helpers';
+
+export default {
+ component: FillsTable,
+ title: 'FillsTable',
+} as Meta;
+
+const Template: Story = (args) => ;
+
+export const Default = Template.bind({});
+const fills = generateFills();
+Default.args = {
+ partyId: 'party-id',
+ fills: fills.party?.tradesPaged.edges.map((e) => e.node),
+};
diff --git a/libs/fills/src/lib/fills-table.tsx b/libs/fills/src/lib/fills-table.tsx
new file mode 100644
index 000000000..c8199e1ee
--- /dev/null
+++ b/libs/fills/src/lib/fills-table.tsx
@@ -0,0 +1,160 @@
+import type { AgGridReact } from 'ag-grid-react';
+import {
+ addDecimal,
+ addDecimalsFormatNumber,
+ formatNumber,
+ getDateTimeFormat,
+ t,
+} from '@vegaprotocol/react-helpers';
+import { AgGridColumn } from 'ag-grid-react';
+import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
+import { forwardRef } from 'react';
+import type { FillFields } from './__generated__/FillFields';
+import type { ValueFormatterParams } from 'ag-grid-community';
+import BigNumber from 'bignumber.js';
+import { Side } from '@vegaprotocol/types';
+
+export interface FillsTableProps {
+ partyId: string;
+ fills: FillFields[];
+}
+
+export const FillsTable = forwardRef(
+ ({ partyId, fills }, ref) => {
+ return (
+ data.id}
+ >
+
+ {
+ let className = '';
+ if (data.buyer.id === partyId) {
+ className = 'text-vega-green';
+ } else if (data.seller.id) {
+ className = 'text-vega-red';
+ }
+ return className;
+ }}
+ valueFormatter={formatSize(partyId)}
+ />
+
+
+
+
+ {
+ return getDateTimeFormat().format(new Date(value));
+ }}
+ />
+
+ );
+ }
+);
+
+const formatPrice = ({ value, data }: ValueFormatterParams) => {
+ const asset =
+ data.market.tradableInstrument.instrument.product.settlementAsset.symbol;
+ const valueFormatted = addDecimalsFormatNumber(
+ value,
+ data.market.decimalPlaces
+ );
+ return `${valueFormatted} ${asset}`;
+};
+
+const formatSize = (partyId: string) => {
+ return ({ value, data }: ValueFormatterParams) => {
+ let prefix;
+ if (data.buyer.id === partyId) {
+ prefix = '+';
+ } else if (data.seller.id) {
+ prefix = '-';
+ }
+
+ const size = addDecimalsFormatNumber(
+ value,
+ data.market.positionDecimalPlaces
+ );
+ return `${prefix}${size}`;
+ };
+};
+
+const formatTotal = ({ value, data }: ValueFormatterParams) => {
+ const asset =
+ data.market.tradableInstrument.instrument.product.settlementAsset.symbol;
+ const size = new BigNumber(
+ addDecimal(data.size, data.market.positionDecimalPlaces)
+ );
+ const price = new BigNumber(addDecimal(value, data.market.decimalPlaces));
+
+ const total = size.times(price).toString();
+ const valueFormatted = formatNumber(total, data.market.decimalPlaces);
+ return `${valueFormatted} ${asset}`;
+};
+
+const formatRole = (partyId: string) => {
+ return ({ value, data }: ValueFormatterParams) => {
+ const taker = t('Taker');
+ const maker = t('Maker');
+ if (data.buyer.id === partyId) {
+ if (value === Side.Buy) {
+ return taker;
+ } else {
+ return maker;
+ }
+ } else if (data.seller.id === partyId) {
+ if (value === Side.Sell) {
+ return taker;
+ } else {
+ return maker;
+ }
+ } else {
+ return '-';
+ }
+ };
+};
+
+const formatFee = (partyId: string) => {
+ return ({ value, data }: ValueFormatterParams) => {
+ const asset = value.settlementAsset;
+ let feesObj;
+ if (data.buyer.id === partyId) {
+ feesObj = data.buyerFee;
+ } else if (data.seller.id === partyId) {
+ feesObj = data.sellerFee;
+ } else {
+ return '-';
+ }
+
+ const fee = new BigNumber(feesObj.makerFee)
+ .plus(feesObj.infrastructureFee)
+ .plus(feesObj.liquidityFee);
+ const totalFees = addDecimalsFormatNumber(fee.toString(), asset.decimals);
+ return `${totalFees} ${asset.symbol}`;
+ };
+};
diff --git a/libs/fills/src/lib/test-helpers.ts b/libs/fills/src/lib/test-helpers.ts
new file mode 100644
index 000000000..19c3b3506
--- /dev/null
+++ b/libs/fills/src/lib/test-helpers.ts
@@ -0,0 +1,134 @@
+import { Side } from '@vegaprotocol/types';
+import merge from 'lodash/merge';
+import type { PartialDeep } from 'type-fest';
+import type {
+ Fills,
+ Fills_party_tradesPaged_edges_node,
+} from './__generated__/Fills';
+
+export const generateFills = (override?: PartialDeep): Fills => {
+ const fills: Fills_party_tradesPaged_edges_node[] = [
+ generateFill({
+ buyer: {
+ id: 'party-id',
+ },
+ }),
+ generateFill({
+ id: '1',
+ seller: {
+ id: 'party-id',
+ },
+ aggressor: Side.Sell,
+ buyerFee: {
+ infrastructureFee: '5000',
+ },
+ market: {
+ name: 'Apples Daily v3',
+ positionDecimalPlaces: 2,
+ },
+ }),
+ generateFill({
+ id: '2',
+ seller: {
+ id: 'party-id',
+ },
+ aggressor: Side.Buy,
+ }),
+ generateFill({
+ id: '3',
+ aggressor: Side.Sell,
+ market: {
+ name: 'ETHBTC Quarterly (30 Jun 2022)',
+ },
+ buyer: {
+ id: 'party-id',
+ },
+ }),
+ ];
+
+ const defaultResult: Fills = {
+ party: {
+ id: 'buyer-id',
+ tradesPaged: {
+ __typename: 'TradeConnection',
+ totalCount: 1,
+ edges: fills.map((f) => {
+ return {
+ __typename: 'TradeEdge',
+ node: f,
+ cursor: '3',
+ };
+ }),
+ pageInfo: {
+ __typename: 'PageInfo',
+ startCursor: '1',
+ endCursor: '2',
+ },
+ },
+ __typename: 'Party',
+ },
+ };
+
+ return merge(defaultResult, override);
+};
+
+export const generateFill = (
+ override?: PartialDeep
+) => {
+ const defaultFill: Fills_party_tradesPaged_edges_node = {
+ __typename: 'Trade',
+ id: '0',
+ createdAt: new Date().toISOString(),
+ price: '10000000',
+ size: '50000',
+ buyOrder: 'buy-order',
+ sellOrder: 'sell-order',
+ aggressor: Side.Buy,
+ buyer: {
+ __typename: 'Party',
+ id: 'buyer-id',
+ },
+ seller: {
+ __typename: 'Party',
+ id: 'seller-id',
+ },
+ buyerFee: {
+ __typename: 'TradeFee',
+ makerFee: '100',
+ infrastructureFee: '100',
+ liquidityFee: '100',
+ },
+ sellerFee: {
+ __typename: 'TradeFee',
+ makerFee: '200',
+ infrastructureFee: '200',
+ liquidityFee: '200',
+ },
+ market: {
+ __typename: 'Market',
+ id: 'market-id',
+ name: 'UNIDAI Monthly (30 Jun 2022)',
+ positionDecimalPlaces: 0,
+ decimalPlaces: 5,
+ tradableInstrument: {
+ __typename: 'TradableInstrument',
+ instrument: {
+ __typename: 'Instrument',
+ id: 'instrument-id',
+ code: 'instrument-code',
+ product: {
+ __typename: 'Future',
+ settlementAsset: {
+ __typename: 'Asset',
+ id: 'asset-id',
+ symbol: 'SYM',
+ decimals: 18,
+ },
+ },
+ },
+ },
+ },
+ };
+
+ return merge(defaultFill, override);
+};
diff --git a/libs/fills/src/setup-tests.ts b/libs/fills/src/setup-tests.ts
new file mode 100644
index 000000000..7b0828bfa
--- /dev/null
+++ b/libs/fills/src/setup-tests.ts
@@ -0,0 +1 @@
+import '@testing-library/jest-dom';
diff --git a/libs/fills/tailwind.config.js b/libs/fills/tailwind.config.js
new file mode 100644
index 000000000..1deb8143d
--- /dev/null
+++ b/libs/fills/tailwind.config.js
@@ -0,0 +1,17 @@
+const { join } = require('path');
+const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
+const theme = require('../tailwindcss-config/src/theme');
+const vegaCustomClasses = require('../tailwindcss-config/src/vega-custom-classes');
+
+module.exports = {
+ content: [
+ join(__dirname, 'src/**/*.{ts,tsx,html,mdx}'),
+ join(__dirname, '.storybook/preview.js'),
+ ...createGlobPatternsForDependencies(__dirname),
+ ],
+ darkMode: 'class',
+ theme: {
+ extend: theme,
+ },
+ plugins: [vegaCustomClasses],
+};
diff --git a/libs/fills/tsconfig.json b/libs/fills/tsconfig.json
new file mode 100644
index 000000000..9fff9cc2d
--- /dev/null
+++ b/libs/fills/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "allowJs": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "forceConsistentCasingInFileNames": true,
+ "strict": true,
+ "noImplicitOverride": true,
+ "noPropertyAccessFromIndexSignature": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "files": [],
+ "include": [],
+ "references": [
+ {
+ "path": "./tsconfig.lib.json"
+ },
+ {
+ "path": "./tsconfig.spec.json"
+ },
+ {
+ "path": "./.storybook/tsconfig.json"
+ }
+ ]
+}
diff --git a/libs/fills/tsconfig.lib.json b/libs/fills/tsconfig.lib.json
new file mode 100644
index 000000000..ad9c3d024
--- /dev/null
+++ b/libs/fills/tsconfig.lib.json
@@ -0,0 +1,26 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "types": ["node"]
+ },
+ "files": [
+ "../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
+ "../../node_modules/@nrwl/react/typings/image.d.ts"
+ ],
+ "exclude": [
+ "**/*.spec.ts",
+ "**/*.test.ts",
+ "**/*.spec.tsx",
+ "**/*.test.tsx",
+ "**/*.spec.js",
+ "**/*.test.js",
+ "**/*.spec.jsx",
+ "**/*.test.jsx",
+ "**/*.stories.ts",
+ "**/*.stories.js",
+ "**/*.stories.jsx",
+ "**/*.stories.tsx"
+ ],
+ "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
+}
diff --git a/libs/fills/tsconfig.spec.json b/libs/fills/tsconfig.spec.json
new file mode 100644
index 000000000..86a9fa994
--- /dev/null
+++ b/libs/fills/tsconfig.spec.json
@@ -0,0 +1,19 @@
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../dist/out-tsc",
+ "module": "commonjs",
+ "types": ["jest", "node", "@testing-library/jest-dom"]
+ },
+ "include": [
+ "**/*.test.ts",
+ "**/*.spec.ts",
+ "**/*.test.tsx",
+ "**/*.spec.tsx",
+ "**/*.test.js",
+ "**/*.spec.js",
+ "**/*.test.jsx",
+ "**/*.spec.jsx",
+ "**/*.d.ts"
+ ]
+}
diff --git a/libs/market-list/src/lib/components/landing/select-market-dialog.tsx b/libs/market-list/src/lib/components/landing/select-market-dialog.tsx
index d3b616e08..c050eb087 100644
--- a/libs/market-list/src/lib/components/landing/select-market-dialog.tsx
+++ b/libs/market-list/src/lib/components/landing/select-market-dialog.tsx
@@ -18,7 +18,7 @@ export const SelectMarketDialog = ({
open={dialogOpen}
onChange={() => setDialogOpen(false)}
titleClassNames="font-bold font-sans text-3xl tracking-tight mb-0 pl-8"
- contentClassNames="w-full md:w-[1120px]"
+ contentClassNames="w-full lg:w-[1020px]"
>
diff --git a/libs/order-list/.storybook/main.js b/libs/order-list/.storybook/main.js
new file mode 100644
index 000000000..01ae9b323
--- /dev/null
+++ b/libs/order-list/.storybook/main.js
@@ -0,0 +1,28 @@
+const rootMain = require('../../../.storybook/main');
+
+module.exports = {
+ ...rootMain,
+
+ core: { ...rootMain.core, builder: 'webpack5' },
+
+ stories: [
+ ...rootMain.stories,
+ '../src/**/*.stories.mdx',
+ '../src/**/*.stories.@(js|jsx|ts|tsx)',
+ ],
+ addons: [
+ ...rootMain.addons,
+ '@nrwl/react/plugins/storybook',
+ 'storybook-addon-themes',
+ ],
+ webpackFinal: async (config, { configType }) => {
+ // apply any global webpack configs that might have been specified in .storybook/main.js
+ if (rootMain.webpackFinal) {
+ config = await rootMain.webpackFinal(config, { configType });
+ }
+
+ // add your own webpack tweaks if needed
+
+ return config;
+ },
+};
diff --git a/libs/order-list/.storybook/preview-head.html b/libs/order-list/.storybook/preview-head.html
new file mode 100644
index 000000000..dd2e70030
--- /dev/null
+++ b/libs/order-list/.storybook/preview-head.html
@@ -0,0 +1 @@
+
diff --git a/libs/order-list/.storybook/preview.js b/libs/order-list/.storybook/preview.js
new file mode 100644
index 000000000..1082b7e44
--- /dev/null
+++ b/libs/order-list/.storybook/preview.js
@@ -0,0 +1,11 @@
+import '../src/styles.scss';
+export const parameters = {
+ actions: { argTypesRegex: '^on[A-Z].*' },
+ themes: {
+ default: 'dark',
+ list: [
+ { name: 'dark', class: ['dark', 'bg-black'], color: '#000' },
+ { name: 'light', class: '', color: '#FFF' },
+ ],
+ },
+};
diff --git a/libs/order-list/.storybook/tsconfig.json b/libs/order-list/.storybook/tsconfig.json
new file mode 100644
index 000000000..7a1170995
--- /dev/null
+++ b/libs/order-list/.storybook/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "emitDecoratorMetadata": true,
+ "outDir": ""
+ },
+ "files": [
+ "../../../node_modules/@nrwl/react/typings/styled-jsx.d.ts",
+ "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
+ "../../../node_modules/@nrwl/react/typings/image.d.ts"
+ ],
+ "exclude": [
+ "../**/*.spec.ts",
+ "../**/*.spec.js",
+ "../**/*.spec.tsx",
+ "../**/*.spec.jsx"
+ ],
+ "include": ["../src/**/*", "*.js"]
+}
diff --git a/libs/order-list/postcss.config.js b/libs/order-list/postcss.config.js
new file mode 100644
index 000000000..cbdd9c22c
--- /dev/null
+++ b/libs/order-list/postcss.config.js
@@ -0,0 +1,10 @@
+const { join } = require('path');
+
+module.exports = {
+ plugins: {
+ tailwindcss: {
+ config: join(__dirname, 'tailwind.config.js'),
+ },
+ autoprefixer: {},
+ },
+};
diff --git a/libs/order-list/project.json b/libs/order-list/project.json
index 66b7ea639..adab0b2c8 100644
--- a/libs/order-list/project.json
+++ b/libs/order-list/project.json
@@ -38,6 +38,37 @@
"jestConfig": "libs/order-list/jest.config.js",
"passWithNoTests": true
}
+ },
+ "storybook": {
+ "executor": "@nrwl/storybook:storybook",
+ "options": {
+ "uiFramework": "@storybook/react",
+ "port": 4400,
+ "config": {
+ "configFolder": "libs/order-list/.storybook"
+ }
+ },
+ "configurations": {
+ "ci": {
+ "quiet": true
+ }
+ }
+ },
+ "build-storybook": {
+ "executor": "@nrwl/storybook:build",
+ "outputs": ["{options.outputPath}"],
+ "options": {
+ "uiFramework": "@storybook/react",
+ "outputPath": "dist/storybook/order-list",
+ "config": {
+ "configFolder": "libs/order-list/.storybook"
+ }
+ },
+ "configurations": {
+ "ci": {
+ "quiet": true
+ }
+ }
}
}
}
diff --git a/libs/order-list/src/index.ts b/libs/order-list/src/index.ts
index 6337331b4..f41a696fd 100644
--- a/libs/order-list/src/index.ts
+++ b/libs/order-list/src/index.ts
@@ -1,3 +1 @@
-export * from './lib/order-list';
-export * from './lib/order-list-container';
-export * from './lib/__generated__/Orders';
+export * from './lib';
diff --git a/libs/order-list/src/lib/__generated__/OrderFields.ts b/libs/order-list/src/lib/components/__generated__/OrderFields.ts
similarity index 100%
rename from libs/order-list/src/lib/__generated__/OrderFields.ts
rename to libs/order-list/src/lib/components/__generated__/OrderFields.ts
diff --git a/libs/order-list/src/lib/__generated__/OrderSub.ts b/libs/order-list/src/lib/components/__generated__/OrderSub.ts
similarity index 100%
rename from libs/order-list/src/lib/__generated__/OrderSub.ts
rename to libs/order-list/src/lib/components/__generated__/OrderSub.ts
diff --git a/libs/order-list/src/lib/__generated__/Orders.ts b/libs/order-list/src/lib/components/__generated__/Orders.ts
similarity index 100%
rename from libs/order-list/src/lib/__generated__/Orders.ts
rename to libs/order-list/src/lib/components/__generated__/Orders.ts
diff --git a/libs/order-list/src/lib/components/__generated__/index.ts b/libs/order-list/src/lib/components/__generated__/index.ts
new file mode 100644
index 000000000..9e0340c91
--- /dev/null
+++ b/libs/order-list/src/lib/components/__generated__/index.ts
@@ -0,0 +1,3 @@
+export * from './OrderFields';
+export * from './OrderSub';
+export * from './Orders';
diff --git a/libs/order-list/src/lib/components/cancel-order-dialog/cancel-dialog.spec.tsx b/libs/order-list/src/lib/components/cancel-order-dialog/cancel-dialog.spec.tsx
new file mode 100644
index 000000000..6d5376105
--- /dev/null
+++ b/libs/order-list/src/lib/components/cancel-order-dialog/cancel-dialog.spec.tsx
@@ -0,0 +1,65 @@
+import { render, screen } from '@testing-library/react';
+import { OrderStatus, OrderType } from '@vegaprotocol/types';
+import type { VegaTxState, Order } from '@vegaprotocol/wallet';
+import { VegaTxStatus } from '@vegaprotocol/wallet';
+import type { CancelDialogProps } from './cancel-dialog';
+import { CancelDialog } from './cancel-dialog';
+
+describe('CancelDialog', () => {
+ let defaultProps: CancelDialogProps;
+
+ beforeEach(() => {
+ defaultProps = {
+ orderDialogOpen: true,
+ setOrderDialogOpen: () => false,
+ transaction: {
+ status: VegaTxStatus.Default,
+ error: null,
+ txHash: null,
+ signature: null,
+ },
+ finalizedOrder: {
+ status: OrderStatus.Cancelled,
+ rejectionReason: null,
+ size: '10',
+ price: '1000',
+ market: null,
+ type: OrderType.Limit,
+ },
+ reset: jest.fn(),
+ };
+ });
+
+ it('should render when an order is successfully cancelled', () => {
+ render(
);
+ expect(screen.getByTestId('order-status-header')).toHaveTextContent(
+ 'Order cancelled'
+ );
+ });
+
+ it('should render when an order is not successfully cancelled', () => {
+ const transaction: VegaTxState = {
+ status: VegaTxStatus.Default,
+ error: null,
+ txHash: null,
+ signature: null,
+ };
+ const finalizedOrder: Order = {
+ status: OrderStatus.Active,
+ rejectionReason: null,
+ size: '10',
+ price: '1000',
+ market: null,
+ type: OrderType.Limit,
+ };
+ const propsForTest = {
+ transaction,
+ finalizedOrder,
+ };
+
+ render(
);
+ expect(screen.getByTestId('order-status-header')).toHaveTextContent(
+ 'Cancellation failed'
+ );
+ });
+});
diff --git a/libs/order-list/src/lib/components/cancel-order-dialog/cancel-dialog.tsx b/libs/order-list/src/lib/components/cancel-order-dialog/cancel-dialog.tsx
new file mode 100644
index 000000000..2e9d5ea0d
--- /dev/null
+++ b/libs/order-list/src/lib/components/cancel-order-dialog/cancel-dialog.tsx
@@ -0,0 +1,65 @@
+import { OrderStatus } from '@vegaprotocol/types';
+import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
+import type { Order, VegaTxState } from '@vegaprotocol/wallet';
+import { VegaTxStatus, VegaOrderTransactionDialog } from '@vegaprotocol/wallet';
+import { useEffect } from 'react';
+
+export interface CancelDialogProps {
+ orderDialogOpen: boolean;
+ setOrderDialogOpen: (isOpen: boolean) => void;
+ finalizedOrder: Order | null;
+ transaction: VegaTxState;
+ reset: () => void;
+}
+
+export const CancelDialog = ({
+ orderDialogOpen,
+ setOrderDialogOpen,
+ finalizedOrder,
+ transaction,
+ reset,
+}: CancelDialogProps) => {
+ const getDialogIntent = () => {
+ if (finalizedOrder) {
+ if (finalizedOrder.status === OrderStatus.Cancelled) {
+ return Intent.Success;
+ }
+
+ return Intent.Danger;
+ }
+ return Intent.None;
+ };
+
+ useEffect(() => {
+ if (transaction.status !== VegaTxStatus.Default || finalizedOrder) {
+ setOrderDialogOpen(true);
+ } else {
+ setOrderDialogOpen(false);
+ }
+ }, [finalizedOrder, setOrderDialogOpen, transaction.status]);
+
+ return (
+
{
+ setOrderDialogOpen(isOpen);
+
+ // If closing reset
+ if (!isOpen) {
+ reset();
+ }
+ }}
+ intent={getDialogIntent()}
+ >
+
+
+ );
+};
diff --git a/libs/order-list/src/lib/components/cancel-order-dialog/index.ts b/libs/order-list/src/lib/components/cancel-order-dialog/index.ts
new file mode 100644
index 000000000..7f5e6ac3f
--- /dev/null
+++ b/libs/order-list/src/lib/components/cancel-order-dialog/index.ts
@@ -0,0 +1 @@
+export * from './cancel-dialog';
diff --git a/libs/order-list/src/lib/components/index.ts b/libs/order-list/src/lib/components/index.ts
new file mode 100644
index 000000000..23831a45f
--- /dev/null
+++ b/libs/order-list/src/lib/components/index.ts
@@ -0,0 +1,7 @@
+export * from './__generated__';
+export * from './cancel-order-dialog';
+export * from './mocks';
+export * from './order-data-provider';
+export * from './order-list';
+export * from './order-list-manager';
+export * from './order-list-container';
diff --git a/libs/order-list/src/lib/components/mocks/generate-orders.ts b/libs/order-list/src/lib/components/mocks/generate-orders.ts
new file mode 100644
index 000000000..d9c598380
--- /dev/null
+++ b/libs/order-list/src/lib/components/mocks/generate-orders.ts
@@ -0,0 +1,156 @@
+import merge from 'lodash/merge';
+import type { PartialDeep } from 'type-fest';
+
+import {
+ OrderStatus,
+ OrderTimeInForce,
+ OrderType,
+ Side,
+} from '@vegaprotocol/types';
+import type { Orders, Orders_party_orders } from '../__generated__/Orders';
+
+export const generateOrders = (override?: PartialDeep
): Orders => {
+ const orders: Orders_party_orders[] = generateOrdersArray();
+
+ const defaultResult = {
+ party: {
+ id: 'party-id',
+ orders,
+ __typename: 'Party',
+ },
+ };
+ return merge(defaultResult, override);
+};
+
+export const generateOrder = (partialOrder: Partial) =>
+ merge(
+ {
+ __typename: 'Order',
+ id: 'order-id2',
+ market: {
+ __typename: 'Market',
+ id: 'market-id',
+ name: 'market-name',
+ decimalPlaces: 2,
+ positionDecimalPlaces: 2,
+ tradableInstrument: {
+ __typename: 'TradableInstrument',
+ instrument: {
+ __typename: 'Instrument',
+ code: 'instrument-code',
+ },
+ },
+ },
+ size: '10',
+ type: OrderType.Market,
+ status: OrderStatus.Active,
+ side: Side.Buy,
+ remaining: '5',
+ price: '',
+ timeInForce: OrderTimeInForce.IOC,
+ createdAt: new Date().toISOString(),
+ updatedAt: null,
+ expiresAt: null,
+ rejectionReason: null,
+ } as Orders_party_orders,
+ partialOrder
+ );
+
+export const limitOrder = generateOrder({
+ id: 'limit-order',
+ type: OrderType.Limit,
+ status: OrderStatus.Active,
+ timeInForce: OrderTimeInForce.GTT,
+ createdAt: new Date('2022-3-3').toISOString(),
+ expiresAt: new Date('2022-3-5').toISOString(),
+});
+
+export const marketOrder = generateOrder({
+ id: 'market-order',
+ type: OrderType.Market,
+ status: OrderStatus.Active,
+});
+
+export const generateMockOrders = (): Orders_party_orders[] => {
+ return [
+ generateOrder({
+ id: '066468C06549101DAF7BC51099E1412A0067DC08C246B7D8013C9D0CBF1E8EE7',
+ market: {
+ __typename: 'Market',
+ id: 'c9f5acd348796011c075077e4d58d9b7f1689b7c1c8e030a5e886b83aa96923d',
+ name: 'AAVEDAI Monthly (30 Jun 2022)',
+ decimalPlaces: 5,
+ positionDecimalPlaces: 0,
+ tradableInstrument: {
+ __typename: 'TradableInstrument',
+ instrument: {
+ __typename: 'Instrument',
+ code: 'AAVEDAI.MF21',
+ },
+ },
+ },
+ size: '10',
+ type: OrderType.Limit,
+ status: OrderStatus.Filled,
+ side: Side.Buy,
+ remaining: '0',
+ price: '20000000',
+ timeInForce: OrderTimeInForce.GTC,
+ createdAt: new Date(2020, 1, 1).toISOString(),
+ }),
+ generateOrder({
+ id: '48DB6767E4E4E0F649C5A13ABFADE39F8451C27DA828DAF14B7A1E8E5EBDAD99',
+ market: {
+ __typename: 'Market',
+ id: '5a4b0b9e9c0629f0315ec56fcb7bd444b0c6e4da5ec7677719d502626658a376',
+ name: 'Tesla Quarterly (30 Jun 2022)',
+ decimalPlaces: 5,
+ positionDecimalPlaces: 0,
+ tradableInstrument: {
+ __typename: 'TradableInstrument',
+ instrument: {
+ __typename: 'Instrument',
+ code: 'TSLA.QM21',
+ },
+ },
+ },
+ size: '1',
+ type: OrderType.Limit,
+ status: OrderStatus.Filled,
+ side: Side.Buy,
+ remaining: '0',
+ price: '100',
+ timeInForce: OrderTimeInForce.GTC,
+ createdAt: new Date().toISOString(),
+ }),
+ generateOrder({
+ id: '4e93702990712c41f6995fcbbd94f60bb372ad12d64dfa7d96d205c49f790336',
+ market: {
+ __typename: 'Market',
+ id: 'c6f4337b31ed57a961969c3ba10297b369d01b9e75a4cbb96db4fc62886444e6',
+ name: 'BTCUSD Monthly (30 Jun 2022)',
+ decimalPlaces: 5,
+ positionDecimalPlaces: 0,
+ tradableInstrument: {
+ __typename: 'TradableInstrument',
+ instrument: {
+ __typename: 'Instrument',
+ code: 'BTCUSD.MF21',
+ },
+ },
+ },
+ size: '1',
+ type: OrderType.Limit,
+ status: OrderStatus.Filled,
+ side: Side.Buy,
+ remaining: '0',
+ price: '20000',
+ timeInForce: OrderTimeInForce.GTC,
+ createdAt: new Date(2022, 5, 10).toISOString(),
+ }),
+ ];
+};
+
+export const generateOrdersArray = (): Orders_party_orders[] => {
+ return [marketOrder, limitOrder, ...generateMockOrders()];
+};
diff --git a/libs/order-list/src/lib/components/mocks/index.ts b/libs/order-list/src/lib/components/mocks/index.ts
new file mode 100644
index 000000000..47cc6ab69
--- /dev/null
+++ b/libs/order-list/src/lib/components/mocks/index.ts
@@ -0,0 +1 @@
+export * from './generate-orders';
diff --git a/libs/order-list/src/lib/components/order-data-provider/__generated__/OrderFields.ts b/libs/order-list/src/lib/components/order-data-provider/__generated__/OrderFields.ts
new file mode 100644
index 000000000..cac0eff56
--- /dev/null
+++ b/libs/order-list/src/lib/components/order-data-provider/__generated__/OrderFields.ts
@@ -0,0 +1,121 @@
+/* tslint:disable */
+/* eslint-disable */
+// @generated
+// This file was automatically generated and should not be edited.
+
+import { OrderType, Side, OrderStatus, OrderRejectionReason, OrderTimeInForce } from "@vegaprotocol/types";
+
+// ====================================================
+// GraphQL fragment: OrderFields
+// ====================================================
+
+export interface OrderFields_market_tradableInstrument_instrument {
+ __typename: "Instrument";
+ /**
+ * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
+ */
+ code: string;
+}
+
+export interface OrderFields_market_tradableInstrument {
+ __typename: "TradableInstrument";
+ /**
+ * An instance of or reference to a fully specified instrument.
+ */
+ instrument: OrderFields_market_tradableInstrument_instrument;
+}
+
+export interface OrderFields_market {
+ __typename: "Market";
+ /**
+ * Market ID
+ */
+ id: string;
+ /**
+ * Market full name
+ */
+ name: string;
+ /**
+ * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
+ * number denominated in the currency of the Market. (uint64)
+ *
+ * Examples:
+ * Currency Balance decimalPlaces Real Balance
+ * GBP 100 0 GBP 100
+ * GBP 100 2 GBP 1.00
+ * GBP 100 4 GBP 0.01
+ * GBP 1 4 GBP 0.0001 ( 0.01p )
+ *
+ * GBX (pence) 100 0 GBP 1.00 (100p )
+ * GBX (pence) 100 2 GBP 0.01 ( 1p )
+ * GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
+ * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
+ */
+ decimalPlaces: number;
+ /**
+ * positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
+ * i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
+ * 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
+ */
+ positionDecimalPlaces: number;
+ /**
+ * An instance of or reference to a tradable instrument.
+ */
+ tradableInstrument: OrderFields_market_tradableInstrument;
+}
+
+export interface OrderFields {
+ __typename: "Order";
+ /**
+ * Hash of the order data
+ */
+ id: string;
+ /**
+ * The market the order is trading on (probably stored internally as a hash of the market details)
+ */
+ market: OrderFields_market | null;
+ /**
+ * Type the order type (defaults to PARTY)
+ */
+ type: OrderType | null;
+ /**
+ * Whether the order is to buy or sell
+ */
+ side: Side;
+ /**
+ * Total number of contracts that may be bought or sold (immutable) (uint64)
+ */
+ size: string;
+ /**
+ * The status of an order, for example 'Active'
+ */
+ status: OrderStatus;
+ /**
+ * Reason for the order to be rejected
+ */
+ rejectionReason: OrderRejectionReason | null;
+ /**
+ * The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
+ */
+ price: string;
+ /**
+ * The timeInForce of order (determines how and if it executes, and whether it persists on the book)
+ */
+ timeInForce: OrderTimeInForce;
+ /**
+ * Number of contracts remaining of the total that have not yet been bought or sold (uint64)
+ */
+ remaining: string;
+ /**
+ * Expiration time of this order (ISO-8601 RFC3339+Nano formatted date)
+ */
+ expiresAt: string | null;
+ /**
+ * RFC3339Nano formatted date and time for when the order was created (timestamp)
+ */
+ createdAt: string;
+ /**
+ * RFC3339Nano time the order was altered
+ */
+ updatedAt: string | null;
+}
diff --git a/libs/order-list/src/lib/components/order-data-provider/__generated__/OrderSub.ts b/libs/order-list/src/lib/components/order-data-provider/__generated__/OrderSub.ts
new file mode 100644
index 000000000..99cdb2aae
--- /dev/null
+++ b/libs/order-list/src/lib/components/order-data-provider/__generated__/OrderSub.ts
@@ -0,0 +1,132 @@
+/* tslint:disable */
+/* eslint-disable */
+// @generated
+// This file was automatically generated and should not be edited.
+
+import { OrderType, Side, OrderStatus, OrderRejectionReason, OrderTimeInForce } from "@vegaprotocol/types";
+
+// ====================================================
+// GraphQL subscription operation: OrderSub
+// ====================================================
+
+export interface OrderSub_orders_market_tradableInstrument_instrument {
+ __typename: "Instrument";
+ /**
+ * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
+ */
+ code: string;
+}
+
+export interface OrderSub_orders_market_tradableInstrument {
+ __typename: "TradableInstrument";
+ /**
+ * An instance of or reference to a fully specified instrument.
+ */
+ instrument: OrderSub_orders_market_tradableInstrument_instrument;
+}
+
+export interface OrderSub_orders_market {
+ __typename: "Market";
+ /**
+ * Market ID
+ */
+ id: string;
+ /**
+ * Market full name
+ */
+ name: string;
+ /**
+ * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
+ * number denominated in the currency of the Market. (uint64)
+ *
+ * Examples:
+ * Currency Balance decimalPlaces Real Balance
+ * GBP 100 0 GBP 100
+ * GBP 100 2 GBP 1.00
+ * GBP 100 4 GBP 0.01
+ * GBP 1 4 GBP 0.0001 ( 0.01p )
+ *
+ * GBX (pence) 100 0 GBP 1.00 (100p )
+ * GBX (pence) 100 2 GBP 0.01 ( 1p )
+ * GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
+ * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
+ */
+ decimalPlaces: number;
+ /**
+ * positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
+ * i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
+ * 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
+ */
+ positionDecimalPlaces: number;
+ /**
+ * An instance of or reference to a tradable instrument.
+ */
+ tradableInstrument: OrderSub_orders_market_tradableInstrument;
+}
+
+export interface OrderSub_orders {
+ __typename: "Order";
+ /**
+ * Hash of the order data
+ */
+ id: string;
+ /**
+ * The market the order is trading on (probably stored internally as a hash of the market details)
+ */
+ market: OrderSub_orders_market | null;
+ /**
+ * Type the order type (defaults to PARTY)
+ */
+ type: OrderType | null;
+ /**
+ * Whether the order is to buy or sell
+ */
+ side: Side;
+ /**
+ * Total number of contracts that may be bought or sold (immutable) (uint64)
+ */
+ size: string;
+ /**
+ * The status of an order, for example 'Active'
+ */
+ status: OrderStatus;
+ /**
+ * Reason for the order to be rejected
+ */
+ rejectionReason: OrderRejectionReason | null;
+ /**
+ * The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
+ */
+ price: string;
+ /**
+ * The timeInForce of order (determines how and if it executes, and whether it persists on the book)
+ */
+ timeInForce: OrderTimeInForce;
+ /**
+ * Number of contracts remaining of the total that have not yet been bought or sold (uint64)
+ */
+ remaining: string;
+ /**
+ * Expiration time of this order (ISO-8601 RFC3339+Nano formatted date)
+ */
+ expiresAt: string | null;
+ /**
+ * RFC3339Nano formatted date and time for when the order was created (timestamp)
+ */
+ createdAt: string;
+ /**
+ * RFC3339Nano time the order was altered
+ */
+ updatedAt: string | null;
+}
+
+export interface OrderSub {
+ /**
+ * Subscribe to orders updates
+ */
+ orders: OrderSub_orders[] | null;
+}
+
+export interface OrderSubVariables {
+ partyId: string;
+}
diff --git a/libs/order-list/src/lib/components/order-data-provider/__generated__/Orders.ts b/libs/order-list/src/lib/components/order-data-provider/__generated__/Orders.ts
new file mode 100644
index 000000000..ab2ca9e60
--- /dev/null
+++ b/libs/order-list/src/lib/components/order-data-provider/__generated__/Orders.ts
@@ -0,0 +1,144 @@
+/* tslint:disable */
+/* eslint-disable */
+// @generated
+// This file was automatically generated and should not be edited.
+
+import { OrderType, Side, OrderStatus, OrderRejectionReason, OrderTimeInForce } from "@vegaprotocol/types";
+
+// ====================================================
+// GraphQL query operation: Orders
+// ====================================================
+
+export interface Orders_party_orders_market_tradableInstrument_instrument {
+ __typename: "Instrument";
+ /**
+ * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string)
+ */
+ code: string;
+}
+
+export interface Orders_party_orders_market_tradableInstrument {
+ __typename: "TradableInstrument";
+ /**
+ * An instance of or reference to a fully specified instrument.
+ */
+ instrument: Orders_party_orders_market_tradableInstrument_instrument;
+}
+
+export interface Orders_party_orders_market {
+ __typename: "Market";
+ /**
+ * Market ID
+ */
+ id: string;
+ /**
+ * Market full name
+ */
+ name: string;
+ /**
+ * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
+ * number denominated in the currency of the Market. (uint64)
+ *
+ * Examples:
+ * Currency Balance decimalPlaces Real Balance
+ * GBP 100 0 GBP 100
+ * GBP 100 2 GBP 1.00
+ * GBP 100 4 GBP 0.01
+ * GBP 1 4 GBP 0.0001 ( 0.01p )
+ *
+ * GBX (pence) 100 0 GBP 1.00 (100p )
+ * GBX (pence) 100 2 GBP 0.01 ( 1p )
+ * GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
+ * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
+ */
+ decimalPlaces: number;
+ /**
+ * positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
+ * i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
+ * 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
+ */
+ positionDecimalPlaces: number;
+ /**
+ * An instance of or reference to a tradable instrument.
+ */
+ tradableInstrument: Orders_party_orders_market_tradableInstrument;
+}
+
+export interface Orders_party_orders {
+ __typename: "Order";
+ /**
+ * Hash of the order data
+ */
+ id: string;
+ /**
+ * The market the order is trading on (probably stored internally as a hash of the market details)
+ */
+ market: Orders_party_orders_market | null;
+ /**
+ * Type the order type (defaults to PARTY)
+ */
+ type: OrderType | null;
+ /**
+ * Whether the order is to buy or sell
+ */
+ side: Side;
+ /**
+ * Total number of contracts that may be bought or sold (immutable) (uint64)
+ */
+ size: string;
+ /**
+ * The status of an order, for example 'Active'
+ */
+ status: OrderStatus;
+ /**
+ * Reason for the order to be rejected
+ */
+ rejectionReason: OrderRejectionReason | null;
+ /**
+ * The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
+ */
+ price: string;
+ /**
+ * The timeInForce of order (determines how and if it executes, and whether it persists on the book)
+ */
+ timeInForce: OrderTimeInForce;
+ /**
+ * Number of contracts remaining of the total that have not yet been bought or sold (uint64)
+ */
+ remaining: string;
+ /**
+ * Expiration time of this order (ISO-8601 RFC3339+Nano formatted date)
+ */
+ expiresAt: string | null;
+ /**
+ * RFC3339Nano formatted date and time for when the order was created (timestamp)
+ */
+ createdAt: string;
+ /**
+ * RFC3339Nano time the order was altered
+ */
+ updatedAt: string | null;
+}
+
+export interface Orders_party {
+ __typename: "Party";
+ /**
+ * Party identifier
+ */
+ id: string;
+ /**
+ * Orders relating to a party
+ */
+ orders: Orders_party_orders[] | null;
+}
+
+export interface Orders {
+ /**
+ * An entity that is trading on the VEGA network
+ */
+ party: Orders_party | null;
+}
+
+export interface OrdersVariables {
+ partyId: string;
+}
diff --git a/libs/order-list/src/lib/components/order-data-provider/index.ts b/libs/order-list/src/lib/components/order-data-provider/index.ts
new file mode 100644
index 000000000..04954cd93
--- /dev/null
+++ b/libs/order-list/src/lib/components/order-data-provider/index.ts
@@ -0,0 +1 @@
+export * from './order-data-provider';
diff --git a/libs/order-list/src/lib/order-data-provider.spec.ts b/libs/order-list/src/lib/components/order-data-provider/order-data-provider.spec.ts
similarity index 94%
rename from libs/order-list/src/lib/order-data-provider.spec.ts
rename to libs/order-list/src/lib/components/order-data-provider/order-data-provider.spec.ts
index 7a1208add..292ba08d8 100644
--- a/libs/order-list/src/lib/order-data-provider.spec.ts
+++ b/libs/order-list/src/lib/components/order-data-provider/order-data-provider.spec.ts
@@ -4,8 +4,8 @@ import {
Side,
OrderTimeInForce,
} from '@vegaprotocol/types';
-import { sortOrders } from './orders-data-provider';
-import type { Orders_party_orders } from './__generated__/Orders';
+import type { Orders_party_orders } from '../__generated__/Orders';
+import { sortOrders } from './order-data-provider';
const marketOrder: Orders_party_orders = {
__typename: 'Order',
diff --git a/libs/order-list/src/lib/orders-data-provider.ts b/libs/order-list/src/lib/components/order-data-provider/order-data-provider.ts
similarity index 91%
rename from libs/order-list/src/lib/orders-data-provider.ts
rename to libs/order-list/src/lib/components/order-data-provider/order-data-provider.ts
index ccd6d88fb..75e011c39 100644
--- a/libs/order-list/src/lib/orders-data-provider.ts
+++ b/libs/order-list/src/lib/components/order-data-provider/order-data-provider.ts
@@ -1,9 +1,9 @@
import produce from 'immer';
import { gql } from '@apollo/client';
import { makeDataProvider } from '@vegaprotocol/react-helpers';
-import type { OrderFields } from './__generated__/OrderFields';
-import type { Orders, Orders_party_orders } from './__generated__/Orders';
-import type { OrderSub } from './__generated__/OrderSub';
+import type { OrderFields } from '../__generated__/OrderFields';
+import type { Orders, Orders_party_orders } from '../__generated__/Orders';
+import type { OrderSub } from '../__generated__/OrderSub';
import orderBy from 'lodash/orderBy';
import uniqBy from 'lodash/uniqBy';
diff --git a/libs/order-list/src/lib/order-list-container.tsx b/libs/order-list/src/lib/components/order-list-container.tsx
similarity index 100%
rename from libs/order-list/src/lib/order-list-container.tsx
rename to libs/order-list/src/lib/components/order-list-container.tsx
diff --git a/libs/order-list/src/lib/components/order-list-manager/index.ts b/libs/order-list/src/lib/components/order-list-manager/index.ts
new file mode 100644
index 000000000..f40e1e3a8
--- /dev/null
+++ b/libs/order-list/src/lib/components/order-list-manager/index.ts
@@ -0,0 +1 @@
+export * from './order-list-manager';
diff --git a/libs/order-list/src/lib/order-list-manager.spec.tsx b/libs/order-list/src/lib/components/order-list-manager/order-list-manager.spec.tsx
similarity index 92%
rename from libs/order-list/src/lib/order-list-manager.spec.tsx
rename to libs/order-list/src/lib/components/order-list-manager/order-list-manager.spec.tsx
index 98c0edc88..44d5fa244 100644
--- a/libs/order-list/src/lib/order-list-manager.spec.tsx
+++ b/libs/order-list/src/lib/components/order-list-manager/order-list-manager.spec.tsx
@@ -1,8 +1,8 @@
import { render, screen } from '@testing-library/react';
import { OrderListManager } from './order-list-manager';
import * as useDataProviderHook from '@vegaprotocol/react-helpers';
-import type { Orders_party_orders } from './__generated__/Orders';
-import * as orderListMock from './order-list';
+import type { Orders_party_orders } from '../__generated__/Orders';
+import * as orderListMock from '../order-list';
import { forwardRef } from 'react';
jest.mock('./order-list');
diff --git a/libs/order-list/src/lib/order-list-manager.tsx b/libs/order-list/src/lib/components/order-list-manager/order-list-manager.tsx
similarity index 79%
rename from libs/order-list/src/lib/order-list-manager.tsx
rename to libs/order-list/src/lib/components/order-list-manager/order-list-manager.tsx
index 11971bc03..307a98ee4 100644
--- a/libs/order-list/src/lib/order-list-manager.tsx
+++ b/libs/order-list/src/lib/components/order-list-manager/order-list-manager.tsx
@@ -1,15 +1,15 @@
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
-import { OrderList } from './order-list';
-import type { OrderFields } from './__generated__/OrderFields';
+import { OrderList } from '../order-list';
+import type { OrderFields } from '../__generated__/OrderFields';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import {
ordersDataProvider,
prepareIncomingOrders,
sortOrders,
-} from './orders-data-provider';
+} from '../order-data-provider';
import { useCallback, useMemo, useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
-import type { OrderSub_orders } from './__generated__/OrderSub';
+import type { OrderSub_orders } from '../__generated__/OrderSub';
import isEqual from 'lodash/isEqual';
interface OrderListManagerProps {
@@ -25,10 +25,9 @@ export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
if (!gridRef.current) {
return false;
}
-
const incoming = prepareIncomingOrders(delta);
- const update: OrderFields[] = [];
+ const updateRows: OrderFields[] = [];
const add: OrderFields[] = [];
incoming.forEach((d) => {
@@ -39,17 +38,17 @@ export const OrderListManager = ({ partyId }: OrderListManagerProps) => {
const rowNode = gridRef.current.api.getRowNode(d.id);
if (rowNode) {
- if (!isEqual) {
- update.push(d);
+ if (!isEqual(d, rowNode.data)) {
+ updateRows.push(d);
}
} else {
add.push(d);
}
});
- if (update.length || add.length) {
+ if (updateRows.length || add.length) {
gridRef.current.api.applyTransactionAsync({
- update,
+ update: updateRows,
add,
addIndex: 0,
});
diff --git a/libs/order-list/src/lib/components/order-list/index.ts b/libs/order-list/src/lib/components/order-list/index.ts
new file mode 100644
index 000000000..e90c751a1
--- /dev/null
+++ b/libs/order-list/src/lib/components/order-list/index.ts
@@ -0,0 +1,2 @@
+export * from './order-list.stories';
+export * from './order-list';
diff --git a/libs/order-list/src/lib/components/order-list/order-list.spec.tsx b/libs/order-list/src/lib/components/order-list/order-list.spec.tsx
new file mode 100644
index 000000000..4cc0b2b78
--- /dev/null
+++ b/libs/order-list/src/lib/components/order-list/order-list.spec.tsx
@@ -0,0 +1,116 @@
+import { act, render, screen } from '@testing-library/react';
+import { addDecimal, getDateTimeFormat } from '@vegaprotocol/react-helpers';
+import type { Orders_party_orders } from '../__generated__/Orders';
+import { OrderStatus, OrderRejectionReason } from '@vegaprotocol/types';
+import { OrderList } from './order-list';
+import type { PartialDeep } from 'type-fest';
+import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
+import { VegaWalletContext } from '@vegaprotocol/wallet';
+import { MockedProvider } from '@apollo/client/testing';
+import { limitOrder, marketOrder } from '../mocks/generate-orders';
+
+const generateJsx = (
+ orders: Orders_party_orders[] | null,
+ context: PartialDeep = { keypair: { pub: '0x123' } }
+) => {
+ return (
+
+
+
+
+
+ );
+};
+
+describe('OrderList', () => {
+ it('should show no orders message', async () => {
+ await act(async () => {
+ render(generateJsx([]));
+ });
+ expect(screen.getByText('No orders')).toBeInTheDocument();
+ });
+
+ it('should render correct columns', async () => {
+ await act(async () => {
+ render(generateJsx([marketOrder, limitOrder]));
+ });
+
+ const headers = screen.getAllByRole('columnheader');
+ expect(headers).toHaveLength(10);
+ expect(headers.map((h) => h.textContent?.trim())).toEqual([
+ 'Market',
+ 'Amount',
+ 'Type',
+ 'Status',
+ 'Filled',
+ 'Price',
+ 'Time In Force',
+ 'Created At',
+ 'Updated At',
+ 'Cancel',
+ ]);
+ });
+
+ it('should apply correct formatting for market order', async () => {
+ await act(async () => {
+ render(generateJsx([marketOrder]));
+ });
+
+ const cells = screen.getAllByRole('gridcell');
+ const expectedValues: string[] = [
+ marketOrder.market?.tradableInstrument.instrument.code || '',
+ '+0.10',
+ marketOrder.type || '',
+ marketOrder.status,
+ '5',
+ '-',
+ marketOrder.timeInForce,
+ getDateTimeFormat().format(new Date(marketOrder.createdAt)),
+ '-',
+ 'Cancel',
+ ];
+ cells.forEach((cell, i) =>
+ expect(cell).toHaveTextContent(expectedValues[i])
+ );
+ });
+
+ it('should apply correct formatting applied for GTT limit order', async () => {
+ await act(async () => {
+ render(generateJsx([limitOrder]));
+ });
+ const cells = screen.getAllByRole('gridcell');
+
+ const expectedValues: string[] = [
+ limitOrder.market?.tradableInstrument.instrument.code || '',
+ '+0.10',
+ limitOrder.type || '',
+ limitOrder.status,
+ '5',
+ addDecimal(limitOrder.price, limitOrder.market?.decimalPlaces ?? 0),
+ `${limitOrder.timeInForce}: ${getDateTimeFormat().format(
+ new Date(limitOrder.expiresAt ?? '')
+ )}`,
+ getDateTimeFormat().format(new Date(limitOrder.createdAt)),
+ '-',
+ 'Cancel',
+ ];
+ cells.forEach((cell, i) =>
+ expect(cell).toHaveTextContent(expectedValues[i])
+ );
+ });
+
+ it('should apply correct formatting for a rejected order', async () => {
+ const rejectedOrder = {
+ ...marketOrder,
+ status: OrderStatus.Rejected,
+ rejectionReason: OrderRejectionReason.InsufficientAssetBalance,
+ };
+ await act(async () => {
+ render(generateJsx([rejectedOrder]));
+ });
+ const cells = screen.getAllByRole('gridcell');
+ expect(cells[3]).toHaveTextContent(
+ `${rejectedOrder.status}: ${rejectedOrder.rejectionReason}`
+ );
+ });
+});
diff --git a/libs/order-list/src/lib/components/order-list/order-list.stories.tsx b/libs/order-list/src/lib/components/order-list/order-list.stories.tsx
new file mode 100644
index 000000000..136c4ca96
--- /dev/null
+++ b/libs/order-list/src/lib/components/order-list/order-list.stories.tsx
@@ -0,0 +1,69 @@
+import type { Story, Meta } from '@storybook/react';
+import { OrderType, OrderStatus } from '@vegaprotocol/types';
+import { OrderList, OrderListTable } from './order-list';
+import { CancelDialog } from '../cancel-order-dialog';
+import { useState } from 'react';
+import type { VegaTxState, Order } from '@vegaprotocol/wallet';
+import { VegaTxStatus } from '@vegaprotocol/wallet';
+import { generateOrdersArray } from '../mocks';
+
+export default {
+ component: OrderList,
+ title: 'OrderList',
+} as Meta;
+
+const Template: Story = (args) => {
+ const cancel = () => Promise.resolve();
+ return (
+
+
+
+ );
+};
+
+const Template2: Story = (args) => {
+ const [open, setOpen] = useState(false);
+ const cancel = () => {
+ setOpen(!open);
+ return Promise.resolve();
+ };
+ const transaction: VegaTxState = {
+ status: VegaTxStatus.Default,
+ error: null,
+ txHash: null,
+ signature: null,
+ };
+ const finalizedOrder: Order = {
+ status: OrderStatus.Cancelled,
+ rejectionReason: null,
+ size: '10',
+ price: '1000',
+ market: null,
+ type: OrderType.Limit,
+ };
+ const reset = () => null;
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export const Default = Template.bind({});
+Default.args = {
+ data: generateOrdersArray(),
+};
+
+export const Modal = Template2.bind({});
+Modal.args = {
+ data: generateOrdersArray(),
+};
diff --git a/libs/order-list/src/lib/order-list.tsx b/libs/order-list/src/lib/components/order-list/order-list.tsx
similarity index 61%
rename from libs/order-list/src/lib/order-list.tsx
rename to libs/order-list/src/lib/components/order-list/order-list.tsx
index 132024d06..f9e87653d 100644
--- a/libs/order-list/src/lib/order-list.tsx
+++ b/libs/order-list/src/lib/components/order-list/order-list.tsx
@@ -1,16 +1,16 @@
import { OrderTimeInForce, OrderStatus, Side } from '@vegaprotocol/types';
-import type { Orders_party_orders } from './__generated__/Orders';
-import {
- addDecimal,
- formatNumber,
- getDateTimeFormat,
- t,
-} from '@vegaprotocol/react-helpers';
-import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
-import type { ValueFormatterParams } from 'ag-grid-community';
+import type { Orders_party_orders } from '../__generated__/Orders';
+import { addDecimal, getDateTimeFormat, t } from '@vegaprotocol/react-helpers';
+import { AgGridDynamic as AgGrid, Button } from '@vegaprotocol/ui-toolkit';
+import type {
+ ICellRendererParams,
+ ValueFormatterParams,
+} from 'ag-grid-community';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridColumn } from 'ag-grid-react';
-import { forwardRef } from 'react';
+import { forwardRef, useState } from 'react';
+import { useOrderCancel } from '@vegaprotocol/wallet';
+import { CancelDialog } from '../cancel-order-dialog/cancel-dialog';
import BigNumber from 'bignumber.js';
interface OrderListProps {
@@ -19,6 +19,30 @@ interface OrderListProps {
export const OrderList = forwardRef(
({ data }, ref) => {
+ const [orderDialogOpen, setOrderDialogOpen] = useState(false);
+ const { transaction, finalizedOrder, reset, cancel } = useOrderCancel();
+ return (
+ <>
+
+
+ >
+ );
+ }
+);
+
+interface OrderListTableProps {
+ data: Orders_party_orders[] | null;
+ cancel: (body?: unknown) => Promise;
+}
+
+export const OrderListTable = forwardRef(
+ ({ data, cancel }, ref) => {
return (
(
defaultColDef={{ flex: 1, resizable: true }}
style={{ width: '100%', height: '100%' }}
getRowId={({ data }) => data.id}
+ rowHeight={40}
>
(
if (data.type === 'Market') {
return '-';
}
- return formatNumber(value, data.market.decimalPlaces);
+ return addDecimal(value, data.market.decimalPlaces);
}}
/>
(
return value ? getDateTimeFormat().format(new Date(value)) : '-';
}}
/>
+ {
+ if (
+ ![
+ OrderStatus.Cancelled,
+ OrderStatus.Rejected,
+ OrderStatus.Expired,
+ OrderStatus.Filled,
+ OrderStatus.Stopped,
+ ].includes(data.status)
+ ) {
+ return (
+ {
+ await cancel(data);
+ }}
+ >
+ Cancel
+
+ );
+ }
+ return null;
+ }}
+ />
);
}
diff --git a/libs/order-list/src/lib/index.ts b/libs/order-list/src/lib/index.ts
new file mode 100644
index 000000000..07635cbbc
--- /dev/null
+++ b/libs/order-list/src/lib/index.ts
@@ -0,0 +1 @@
+export * from './components';
diff --git a/libs/order-list/src/lib/order-list.spec.tsx b/libs/order-list/src/lib/order-list.spec.tsx
deleted file mode 100644
index 13737d7ac..000000000
--- a/libs/order-list/src/lib/order-list.spec.tsx
+++ /dev/null
@@ -1,156 +0,0 @@
-import { act, render, screen } from '@testing-library/react';
-import { formatNumber, getDateTimeFormat } from '@vegaprotocol/react-helpers';
-import type { Orders_party_orders } from './__generated__/Orders';
-import {
- OrderStatus,
- OrderTimeInForce,
- OrderType,
- Side,
- OrderRejectionReason,
-} from '@vegaprotocol/types';
-import { OrderList } from './order-list';
-
-it('No orders message shown', async () => {
- await act(async () => {
- render( );
- });
- expect(screen.getByText('No orders')).toBeInTheDocument();
-});
-
-const marketOrder: Orders_party_orders = {
- __typename: 'Order',
- id: 'order-id',
- market: {
- __typename: 'Market',
- id: 'market-id',
- name: 'market-name',
- decimalPlaces: 2,
- positionDecimalPlaces: 0,
- tradableInstrument: {
- __typename: 'TradableInstrument',
- instrument: {
- __typename: 'Instrument',
- code: 'instrument-code',
- },
- },
- },
- size: '10',
- type: OrderType.Market,
- status: OrderStatus.Active,
- side: Side.Buy,
- remaining: '5',
- price: '',
- timeInForce: OrderTimeInForce.IOC,
- createdAt: new Date('2022-2-3').toISOString(),
- updatedAt: null,
- expiresAt: null,
- rejectionReason: null,
-};
-
-const limitOrder: Orders_party_orders = {
- __typename: 'Order',
- id: 'order-id',
- market: {
- __typename: 'Market',
- id: 'market-id',
- name: 'market-name',
- decimalPlaces: 2,
- positionDecimalPlaces: 2,
- tradableInstrument: {
- __typename: 'TradableInstrument',
- instrument: {
- __typename: 'Instrument',
- code: 'instrument-code',
- },
- },
- },
- size: '1000',
- type: OrderType.Limit,
- status: OrderStatus.Active,
- side: Side.Sell,
- remaining: '500',
- price: '12345',
- timeInForce: OrderTimeInForce.GTT,
- createdAt: new Date('2022-3-3').toISOString(),
- expiresAt: new Date('2022-3-5').toISOString(),
- updatedAt: null,
- rejectionReason: null,
-};
-
-it('Correct columns are rendered', async () => {
- await act(async () => {
- render( );
- });
-
- const headers = screen.getAllByRole('columnheader');
- expect(headers).toHaveLength(9);
- expect(headers.map((h) => h.textContent?.trim())).toEqual([
- 'Market',
- 'Amount',
- 'Type',
- 'Status',
- 'Filled',
- 'Price',
- 'Time In Force',
- 'Created At',
- 'Updated At',
- ]);
-});
-
-it('Correct formatting applied for market order', async () => {
- await act(async () => {
- render( );
- });
-
- const cells = screen.getAllByRole('gridcell');
- const expectedValues = [
- marketOrder.market?.tradableInstrument.instrument.code,
- '+10',
- marketOrder.type,
- marketOrder.status,
- '5',
- '-',
- marketOrder.timeInForce,
- getDateTimeFormat().format(new Date(marketOrder.createdAt)),
- '-',
- ];
- cells.forEach((cell, i) => expect(cell).toHaveTextContent(expectedValues[i]));
-});
-
-it('Correct formatting applied for GTT limit order', async () => {
- await act(async () => {
- render( );
- });
- const cells = screen.getAllByRole('gridcell');
- const expectedValues = [
- limitOrder.market?.tradableInstrument.instrument.code,
- '-10.00',
- limitOrder.type,
- limitOrder.status,
- '5.00',
- formatNumber(limitOrder.price, limitOrder.market?.decimalPlaces ?? 0),
- `${limitOrder.timeInForce}: ${getDateTimeFormat().format(
- new Date(limitOrder.expiresAt ?? '')
- )}`,
- getDateTimeFormat().format(new Date(limitOrder.createdAt)),
- '-',
- ];
- cells.forEach((cell, i) => {
- expect(cell).toHaveTextContent(expectedValues[i]);
- });
-});
-
-it('Correct formatting applied for a rejected order', async () => {
- const rejectedOrder = {
- ...marketOrder,
- status: OrderStatus.Rejected,
- rejectionReason: OrderRejectionReason.InsufficientAssetBalance,
- };
- await act(async () => {
- render( );
- });
- const cells = screen.getAllByRole('gridcell');
- expect(cells[3]).toHaveTextContent(
- `${rejectedOrder.status}: ${rejectedOrder.rejectionReason}`
- );
-});
diff --git a/libs/order-list/src/styles.scss b/libs/order-list/src/styles.scss
new file mode 100644
index 000000000..b5c61c956
--- /dev/null
+++ b/libs/order-list/src/styles.scss
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/libs/order-list/tailwind.config.js b/libs/order-list/tailwind.config.js
new file mode 100644
index 000000000..1deb8143d
--- /dev/null
+++ b/libs/order-list/tailwind.config.js
@@ -0,0 +1,17 @@
+const { join } = require('path');
+const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
+const theme = require('../tailwindcss-config/src/theme');
+const vegaCustomClasses = require('../tailwindcss-config/src/vega-custom-classes');
+
+module.exports = {
+ content: [
+ join(__dirname, 'src/**/*.{ts,tsx,html,mdx}'),
+ join(__dirname, '.storybook/preview.js'),
+ ...createGlobPatternsForDependencies(__dirname),
+ ],
+ darkMode: 'class',
+ theme: {
+ extend: theme,
+ },
+ plugins: [vegaCustomClasses],
+};
diff --git a/libs/order-list/tsconfig.json b/libs/order-list/tsconfig.json
index 1eabf319c..b9096c1ed 100644
--- a/libs/order-list/tsconfig.json
+++ b/libs/order-list/tsconfig.json
@@ -20,6 +20,9 @@
},
{
"path": "./tsconfig.spec.json"
+ },
+ {
+ "path": "./.storybook/tsconfig.json"
}
]
}
diff --git a/libs/order-list/tsconfig.lib.json b/libs/order-list/tsconfig.lib.json
index 252904bb7..ad9c3d024 100644
--- a/libs/order-list/tsconfig.lib.json
+++ b/libs/order-list/tsconfig.lib.json
@@ -16,7 +16,11 @@
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
- "**/*.test.jsx"
+ "**/*.test.jsx",
+ "**/*.stories.ts",
+ "**/*.stories.js",
+ "**/*.stories.jsx",
+ "**/*.stories.tsx"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}
diff --git a/libs/react-helpers/src/hooks/use-fetch.ts b/libs/react-helpers/src/hooks/use-fetch.ts
index 7665ca8be..d3df3e631 100644
--- a/libs/react-helpers/src/hooks/use-fetch.ts
+++ b/libs/react-helpers/src/hooks/use-fetch.ts
@@ -25,7 +25,8 @@ export const useFetch = (
): {
state: State;
refetch: (
- params?: Record | undefined
+ params?: Record | undefined,
+ body?: BodyInit
) => Promise;
} => {
// Used to prevent state update if the component is unmounted
@@ -51,7 +52,10 @@ export const useFetch = (
const [state, dispatch] = useReducer(fetchReducer, initialState);
const fetchCallback = useCallback(
- async (params?: Record) => {
+ async (
+ params?: Record,
+ body?: BodyInit
+ ) => {
if (!url) return;
const fetchData = async () => {
@@ -67,7 +71,10 @@ export const useFetch = (
}
}
- const response = await fetch(assembledUrl.toString(), options);
+ const response = await fetch(assembledUrl.toString(), {
+ ...options,
+ body: body ? body : options?.body,
+ });
if (!response.ok) {
throw new Error(response.statusText);
}
diff --git a/libs/tailwindcss-config/src/theme-lite.js b/libs/tailwindcss-config/src/theme-lite.js
index 125e540e0..1920a316d 100644
--- a/libs/tailwindcss-config/src/theme-lite.js
+++ b/libs/tailwindcss-config/src/theme-lite.js
@@ -12,9 +12,21 @@ module.exports = {
mint: '#00F780',
pink: '#FF077F',
blue: '#2E6DE5',
+ vega: {
+ ...theme.colors.vega,
+ 'highlight-item': '#000',
+ 'highlight-item-dark': '#fff',
+ },
+ 'dropdown-bg-dark': theme.colors.black['100'],
},
fontSize: {
...theme.fontSize,
capMenu: ['15px', { lineHeight: '24px', letterSpacing: '-0.01em' }],
+ market: ['15px', { lineHeight: '24px' }],
+ },
+ boxShadow: {
+ ...theme.boxShadow,
+ 'inset-black': '',
+ 'inset-white': '',
},
};
diff --git a/libs/tailwindcss-config/src/theme.js b/libs/tailwindcss-config/src/theme.js
index 8ffdc2e21..6001a1b68 100644
--- a/libs/tailwindcss-config/src/theme.js
+++ b/libs/tailwindcss-config/src/theme.js
@@ -55,6 +55,8 @@ const colours = {
'green-dark': '#008545',
red: '#FF261A',
'red-dark': '#EB001B',
+ 'highlight-item': '#FF077F',
+ 'highlight-item-dark': '#DFFF0B',
},
blue: '#1DA2FB',
coral: '#FF6057',
@@ -65,6 +67,8 @@ const colours = {
selected: '#DFFF0B',
success: '#00F780',
'danger-bg': '#9E0025', // for white text
+ 'dropdown-bg': '#FFF',
+ 'dropdown-bg-dark': shadeOfGray(100 - 60),
};
const boxShadowPosition = {
diff --git a/libs/trades/src/lib/trades-table.tsx b/libs/trades/src/lib/trades-table.tsx
index 49791340d..8c045709f 100644
--- a/libs/trades/src/lib/trades-table.tsx
+++ b/libs/trades/src/lib/trades-table.tsx
@@ -43,7 +43,7 @@ interface TradesTableProps {
export const TradesTable = forwardRef(
({ data }, ref) => {
- // Sort intial trades
+ // Sort initial trades
const trades = useMemo(() => {
if (!data) {
return null;
diff --git a/libs/types/src/__generated__/globalTypes.ts b/libs/types/src/__generated__/globalTypes.ts
index 4e275a805..ba6288849 100644
--- a/libs/types/src/__generated__/globalTypes.ts
+++ b/libs/types/src/__generated__/globalTypes.ts
@@ -288,6 +288,16 @@ export enum WithdrawalStatus {
Rejected = "Rejected",
}
+/**
+ * Pagination constructs to support cursor based pagination in the API
+ */
+export interface Pagination {
+ first?: number | null;
+ after?: string | null;
+ last?: number | null;
+ before?: string | null;
+}
+
//==============================================================
// END Enums and Input Objects
//==============================================================
diff --git a/libs/ui-toolkit/src/components/ag-grid/ag-grid-dark.tsx b/libs/ui-toolkit/src/components/ag-grid/ag-grid-dark.tsx
index 84410d393..9f84adf72 100644
--- a/libs/ui-toolkit/src/components/ag-grid/ag-grid-dark.tsx
+++ b/libs/ui-toolkit/src/components/ag-grid/ag-grid-dark.tsx
@@ -27,9 +27,16 @@ const agGridDarkVariables = `
}
`;
-export const AgGrid = (props: { children: ReactNode }) => (
+export const AgGrid = ({
+ children,
+ customThemeParams,
+}: {
+ children: ReactNode;
+ customThemeParams?: string;
+}) => (
<>
- {props.children}
+ {customThemeParams && }
+ {children}
>
);
diff --git a/libs/ui-toolkit/src/components/ag-grid/ag-grid-dynamic-themed.tsx b/libs/ui-toolkit/src/components/ag-grid/ag-grid-dynamic-themed.tsx
index 52b9cf1e7..39ee2ca36 100644
--- a/libs/ui-toolkit/src/components/ag-grid/ag-grid-dynamic-themed.tsx
+++ b/libs/ui-toolkit/src/components/ag-grid/ag-grid-dynamic-themed.tsx
@@ -8,6 +8,7 @@ import 'ag-grid-community/dist/styles/ag-grid.css';
interface GridProps {
children: ReactNode;
+ customThemeParams: string;
}
const AgGridLightTheme = dynamic(
@@ -24,11 +25,13 @@ export const AgGridThemed = ({
style,
className,
gridRef,
+ customThemeParams = '',
...props
}: (AgGridReactProps | AgReactUiProps) & {
style?: React.CSSProperties;
className?: string;
gridRef?: React.ForwardedRef;
+ customThemeParams?: string;
}) => {
const theme = useContext(ThemeContext);
const defaultProps = { rowHeight: 20, headerHeight: 22 };
@@ -40,11 +43,11 @@ export const AgGridThemed = ({
style={style}
>
{theme === 'dark' ? (
-
+
) : (
-
+
)}
diff --git a/libs/ui-toolkit/src/components/ag-grid/ag-grid-dynamic.tsx b/libs/ui-toolkit/src/components/ag-grid/ag-grid-dynamic.tsx
index 0c49a05b6..e722af2ec 100644
--- a/libs/ui-toolkit/src/components/ag-grid/ag-grid-dynamic.tsx
+++ b/libs/ui-toolkit/src/components/ag-grid/ag-grid-dynamic.tsx
@@ -11,6 +11,7 @@ type Props = (AgGridReactProps | AgReactUiProps) & {
style?: React.CSSProperties;
className?: string;
gridRef?: React.Ref;
+ customThemeParams?: string;
};
// https://stackoverflow.com/questions/69433673/nextjs-reactdomserver-does-not-yet-support-suspense
diff --git a/libs/ui-toolkit/src/components/ag-grid/ag-grid-light.tsx b/libs/ui-toolkit/src/components/ag-grid/ag-grid-light.tsx
index 06fac8fa1..e016fd5d6 100644
--- a/libs/ui-toolkit/src/components/ag-grid/ag-grid-light.tsx
+++ b/libs/ui-toolkit/src/components/ag-grid/ag-grid-light.tsx
@@ -27,9 +27,16 @@ const agGridLightVariables = `
}
`;
-export const AgGrid = (props: { children: ReactNode }) => (
+export const AgGrid = ({
+ children,
+ customThemeParams,
+}: {
+ children: ReactNode;
+ customThemeParams?: string;
+}) => (
<>
- {props.children}
+ {customThemeParams && }
+ {children}
>
);
diff --git a/libs/ui-toolkit/src/components/async-renderer/async-renderer.tsx b/libs/ui-toolkit/src/components/async-renderer/async-renderer.tsx
index 16ef721e4..8690b36ae 100644
--- a/libs/ui-toolkit/src/components/async-renderer/async-renderer.tsx
+++ b/libs/ui-toolkit/src/components/async-renderer/async-renderer.tsx
@@ -4,29 +4,41 @@ import { t } from '@vegaprotocol/react-helpers';
interface AsyncRendererProps {
loading: boolean;
+ loadingMessage?: string;
error: Error | undefined | null;
+ errorMessage?: string;
data: T | undefined;
+ noDataMessage?: string;
children?: ReactNode | null;
render?: (data: T) => ReactNode;
}
export function AsyncRenderer({
loading,
+ loadingMessage,
error,
+ errorMessage,
data,
+ noDataMessage,
children,
render,
}: AsyncRendererProps) {
if (error) {
- return {t(`Something went wrong: ${error.message}`)} ;
+ return (
+
+ {errorMessage
+ ? errorMessage
+ : t(`Something went wrong: ${error.message}`)}
+
+ );
}
if (loading) {
- return {t('Loading...')} ;
+ return {loadingMessage ? loadingMessage : t('Loading...')} ;
}
if (!data) {
- return {t('No data')} ;
+ return {noDataMessage ? noDataMessage : t('No data')} ;
}
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{render ? render(data) : children}>;
diff --git a/libs/ui-toolkit/src/components/button/button.tsx b/libs/ui-toolkit/src/components/button/button.tsx
index 2c94aeea7..c27046447 100644
--- a/libs/ui-toolkit/src/components/button/button.tsx
+++ b/libs/ui-toolkit/src/components/button/button.tsx
@@ -47,6 +47,7 @@ export const getButtonClasses = (
const commonButtonClasses = classnames(
'relative disabled:static',
'text-ui font-semibold focus-visible:outline-none border no-underline hover:no-underline',
+ 'py-[3px]',
{
'shadow-none': !boxShadow,
'shadow-[3px_3px_0_0] focus-visible:shadow-vega-pink dark:focus-visible:shadow-vega-yellow active:top-[1px] active:left-[1px] active:shadow-[2px_2px_0_0]':
@@ -73,7 +74,7 @@ export const getButtonClasses = (
const standardButtonBorderWidth = `${
borderWidthProvided ? borderWidthProvided : 'border'
}`;
- const buttonHeight = `${heightProvided ? heightProvided : 'h-28'}`;
+ const buttonHeight = `${heightProvided ? heightProvided : ''}`;
const primaryClasses = [
sharedClasses,
diff --git a/libs/ui-toolkit/src/components/dropdown-menu/dropdown-menu.tsx b/libs/ui-toolkit/src/components/dropdown-menu/dropdown-menu.tsx
index 7eaaf2529..1a11bb28f 100644
--- a/libs/ui-toolkit/src/components/dropdown-menu/dropdown-menu.tsx
+++ b/libs/ui-toolkit/src/components/dropdown-menu/dropdown-menu.tsx
@@ -13,7 +13,7 @@ const itemClass = classNames(
'hover:cursor-pointer',
'select-none',
'whitespace-nowrap',
- 'focus:bg-vega-pink dark:focus:bg-vega-yellow',
+ 'focus:bg-vega-highlight-item dark:focus:bg-vega-highlight-item-dark',
'focus:text-white dark:focus:text-black',
'focus:outline-none'
);
@@ -23,7 +23,7 @@ function getItemClasses(inset: boolean, checked?: boolean) {
itemClass,
inset ? 'pl-28' : 'pl-8',
checked
- ? 'bg-vega-pink dark:bg-vega-yellow text-white dark:text-black'
+ ? 'bg-vega-highlight-item dark:bg-vega-highlight-item-dark text-white dark:text-black'
: 'text-black dark:text-white'
);
}
@@ -73,7 +73,7 @@ export const DropdownMenuContent = forwardRef<
{...contentProps}
ref={forwardedRef}
className={classNames(
- 'inline-block box-border border-1 border-black bg-white dark:bg-black-60',
+ 'inline-block box-border border-1 border-black bg-dropdown-bg dark:bg-dropdown-bg-dark',
className
)}
/>
diff --git a/libs/ui-toolkit/src/utils/intent.tsx b/libs/ui-toolkit/src/utils/intent.tsx
index 6030cb773..d634847ec 100644
--- a/libs/ui-toolkit/src/utils/intent.tsx
+++ b/libs/ui-toolkit/src/utils/intent.tsx
@@ -30,7 +30,7 @@ export const getIntentBorder = (intent = Intent.None) => {
export const getIntentTextAndBackground = (intent = Intent.None) => {
return {
- 'bg-black text-white dark:bg-white text-black': intent === Intent.None,
+ 'bg-black text-white dark:bg-white dark:text-black': intent === Intent.None,
'bg-vega-pink text-black dark:bg-vega-yellow dark:text-black-normal':
intent === Intent.Primary,
'bg-danger text-white': intent === Intent.Danger,
diff --git a/libs/wallet/.eslintrc.json b/libs/wallet/.eslintrc.json
index 734ddacee..db820c5d0 100644
--- a/libs/wallet/.eslintrc.json
+++ b/libs/wallet/.eslintrc.json
@@ -1,6 +1,6 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
- "ignorePatterns": ["!**/*"],
+ "ignorePatterns": ["!**/*", "__generated__"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
diff --git a/libs/wallet/src/connect-dialog.spec.tsx b/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx
similarity index 74%
rename from libs/wallet/src/connect-dialog.spec.tsx
rename to libs/wallet/src/connect-dialog/connect-dialog.spec.tsx
index ae203cf07..10bcc271d 100644
--- a/libs/wallet/src/connect-dialog.spec.tsx
+++ b/libs/wallet/src/connect-dialog/connect-dialog.spec.tsx
@@ -5,11 +5,11 @@ import {
screen,
waitFor,
} from '@testing-library/react';
-import type { VegaWalletContextShape } from './context';
-import { VegaWalletContext } from './context';
+import type { VegaWalletContextShape } from '../context';
+import { VegaWalletContext } from '../context';
import { VegaConnectDialog } from './connect-dialog';
-import type { VegaConnectDialogProps } from '.';
-import { RestConnector } from './connectors';
+import type { VegaConnectDialogProps } from '..';
+import { RestConnector } from '../connectors';
let defaultProps: VegaConnectDialogProps;
let defaultContextValue: VegaWalletContextShape;
@@ -33,6 +33,8 @@ beforeEach(() => {
};
});
+const DEFAULT_URL = 'http://localhost:1789/api/v1';
+
function generateJSX(
props?: Partial,
contextValue?: Partial
@@ -92,7 +94,40 @@ it('Successful connection using rest auth form', async () => {
fireEvent.submit(screen.getByTestId('rest-connector-form'));
});
- expect(spy).toHaveBeenCalledWith(fields);
+ expect(spy).toHaveBeenCalledWith(DEFAULT_URL, fields);
+
+ expect(defaultProps.setDialogOpen).toHaveBeenCalledWith(false);
+});
+
+it('Successful connection using custom url', async () => {
+ const spy = jest
+ .spyOn(defaultProps.connectors['rest'] as RestConnector, 'authenticate')
+ .mockImplementation(() => Promise.resolve({ success: true, error: null }));
+
+ render(generateJSX({ dialogOpen: true }));
+ // Switches to rest form
+ fireEvent.click(screen.getByText('rest provider'));
+
+ // Client side validation
+ fireEvent.submit(screen.getByTestId('rest-connector-form'));
+ expect(spy).not.toHaveBeenCalled();
+ await waitFor(() => {
+ expect(screen.getAllByText('Required')).toHaveLength(2);
+ });
+
+ // Set custom URL
+ fireEvent.change(screen.getByLabelText('Url'), {
+ target: { value: 'localhost:1234' },
+ });
+
+ const fields = fillInForm();
+
+ // Wait for auth method to be called
+ await act(async () => {
+ fireEvent.submit(screen.getByTestId('rest-connector-form'));
+ });
+
+ expect(spy).toHaveBeenCalledWith('localhost:1234', fields);
expect(defaultProps.setDialogOpen).toHaveBeenCalledWith(false);
});
@@ -117,7 +152,7 @@ it('Unsuccessful connection using rest auth form', async () => {
fireEvent.submit(screen.getByTestId('rest-connector-form'));
});
- expect(spy).toHaveBeenCalledWith(fields);
+ expect(spy).toHaveBeenCalledWith(DEFAULT_URL, fields);
expect(screen.getByTestId('form-error')).toHaveTextContent(
'Something went wrong'
@@ -135,7 +170,7 @@ it('Unsuccessful connection using rest auth form', async () => {
});
expect(screen.getByTestId('form-error')).toHaveTextContent(
- 'Wallet not running at http://localhost:1789'
+ `Wallet not running at ${DEFAULT_URL}`
);
// Reject eg non 200 results
diff --git a/libs/wallet/src/connect-dialog.tsx b/libs/wallet/src/connect-dialog/connect-dialog.tsx
similarity index 88%
rename from libs/wallet/src/connect-dialog.tsx
rename to libs/wallet/src/connect-dialog/connect-dialog.tsx
index 31923d19d..92338a29c 100644
--- a/libs/wallet/src/connect-dialog.tsx
+++ b/libs/wallet/src/connect-dialog/connect-dialog.tsx
@@ -1,11 +1,10 @@
-import { useCallback, useState } from 'react';
+import { useCallback, useEffect, useState } from 'react';
import { Dialog } from '@vegaprotocol/ui-toolkit';
-import type { VegaConnector } from './connectors';
-import { RestConnectorForm } from './rest-connector-form';
-import { useEffect } from 'react';
-import { RestConnector } from './connectors';
-import { useVegaWallet } from './hooks';
import { t } from '@vegaprotocol/react-helpers';
+import type { VegaConnector } from '../connectors';
+import { RestConnector } from '../connectors';
+import { RestConnectorForm } from '../rest-connector-form';
+import { useVegaWallet } from '../use-vega-wallet';
export interface VegaConnectDialogProps {
connectors: { [name: string]: VegaConnector };
diff --git a/libs/wallet/src/connect-dialog/index.ts b/libs/wallet/src/connect-dialog/index.ts
new file mode 100644
index 000000000..44b323191
--- /dev/null
+++ b/libs/wallet/src/connect-dialog/index.ts
@@ -0,0 +1 @@
+export * from './connect-dialog';
diff --git a/libs/wallet/src/connectors/index.ts b/libs/wallet/src/connectors/index.ts
index c23e90638..3a7e4e693 100644
--- a/libs/wallet/src/connectors/index.ts
+++ b/libs/wallet/src/connectors/index.ts
@@ -1,30 +1,3 @@
-import type {
- VegaKey,
- TransactionResponse,
-} from '@vegaprotocol/vegawallet-service-api-client';
-import type { TransactionSubmission } from '../types';
-export { RestConnector } from './rest-connector';
-
-type ErrorResponse =
- | {
- error: string;
- }
- | {
- errors: object;
- };
-
-export interface VegaConnector {
- /** Description of how to use this connector */
- description: string;
-
- /** Connect to wallet and return keys */
- connect(): Promise;
-
- /** Disconnect from wallet */
- disconnect(): Promise;
-
- /** Send a TX to the network. Only support order submission for now */
- sendTx: (
- body: TransactionSubmission
- ) => Promise;
-}
+export * from './vega-connector';
+export * from './rest-connector';
+export * from './injected-connector';
diff --git a/libs/wallet/src/connectors/injected-connector.ts b/libs/wallet/src/connectors/injected-connector.ts
index 48fc4ab48..0b10eeb38 100644
--- a/libs/wallet/src/connectors/injected-connector.ts
+++ b/libs/wallet/src/connectors/injected-connector.ts
@@ -1,4 +1,4 @@
-import type { VegaConnector } from '.';
+import type { VegaConnector } from './vega-connector';
/**
* Dummy injected connector that we may use when browser wallet is implemented
diff --git a/libs/wallet/src/connectors/rest-connector.ts b/libs/wallet/src/connectors/rest-connector.ts
index de7b9c410..158444be0 100644
--- a/libs/wallet/src/connectors/rest-connector.ts
+++ b/libs/wallet/src/connectors/rest-connector.ts
@@ -1,11 +1,12 @@
import type { Configuration } from '@vegaprotocol/vegawallet-service-api-client';
import {
createConfiguration,
+ ServerConfiguration,
DefaultApi,
} from '@vegaprotocol/vegawallet-service-api-client';
import { LocalStorage } from '@vegaprotocol/react-helpers';
import { WALLET_CONFIG } from '../storage-keys';
-import type { VegaConnector } from '.';
+import type { VegaConnector } from './vega-connector';
import type { TransactionSubmission } from '../types';
// Perhaps there should be a default ConnectorConfig that others can extend off. Do all connectors
@@ -39,13 +40,26 @@ export class RestConnector implements VegaConnector {
this.service = new DefaultApi(this.apiConfig);
}
- async authenticate(params: { wallet: string; passphrase: string }) {
+ async authenticate(
+ url: string,
+ params: {
+ wallet: string;
+ passphrase: string;
+ }
+ ) {
try {
- const res = await this.service.authTokenPost(params);
+ const service = new DefaultApi(
+ createConfiguration({
+ baseServer: new ServerConfiguration>(url, {}),
+ })
+ );
+
+ const res = await service.authTokenPost(params);
// Renew service instance with default bearer authMethod now that we have the token
this.service = new DefaultApi(
createConfiguration({
+ baseServer: new ServerConfiguration>(url, {}),
authMethods: {
bearer: `Bearer ${res.token}`,
},
@@ -95,7 +109,7 @@ export class RestConnector implements VegaConnector {
}
private handleSendTxError(err: unknown) {
- const unpexpectedError = { error: 'Something went wrong' };
+ const unexpectedError = { error: 'Something went wrong' };
if (isServiceError(err)) {
if (err.code === 401) {
@@ -105,10 +119,10 @@ export class RestConnector implements VegaConnector {
try {
return JSON.parse(err.body ?? '');
} catch {
- return unpexpectedError;
+ return unexpectedError;
}
} else {
- return unpexpectedError;
+ return unexpectedError;
}
}
diff --git a/libs/wallet/src/connectors/vega-connector.ts b/libs/wallet/src/connectors/vega-connector.ts
new file mode 100644
index 000000000..312ac585a
--- /dev/null
+++ b/libs/wallet/src/connectors/vega-connector.ts
@@ -0,0 +1,29 @@
+import type {
+ VegaKey,
+ TransactionResponse,
+} from '@vegaprotocol/vegawallet-service-api-client';
+import type { TransactionSubmission } from '../types';
+
+type ErrorResponse =
+ | {
+ error: string;
+ }
+ | {
+ errors: object;
+ };
+
+export interface VegaConnector {
+ /** Description of how to use this connector */
+ description: string;
+
+ /** Connect to wallet and return keys */
+ connect(): Promise;
+
+ /** Disconnect from wallet */
+ disconnect(): Promise;
+
+ /** Send a TX to the network. Only support order submission for now */
+ sendTx: (
+ body: TransactionSubmission
+ ) => Promise;
+}
diff --git a/libs/wallet/src/index.ts b/libs/wallet/src/index.ts
index 62e2cde4e..0dfbaccdf 100644
--- a/libs/wallet/src/index.ts
+++ b/libs/wallet/src/index.ts
@@ -1,10 +1,12 @@
-export * from './provider';
export * from './context';
-export * from './hooks';
-export * from './connect-dialog';
+export * from './use-vega-wallet';
export * from './connectors';
export * from './storage-keys';
export * from './types';
export * from './use-vega-transaction';
export * from './use-eager-connect';
export * from './manage-dialog';
+export * from './vega-order-transaction-dialog';
+export * from './provider';
+export * from './order-hooks';
+export * from './connect-dialog';
diff --git a/libs/wallet/src/order-hooks/__generated__/OrderEvent.ts b/libs/wallet/src/order-hooks/__generated__/OrderEvent.ts
new file mode 100644
index 000000000..8ce11083c
--- /dev/null
+++ b/libs/wallet/src/order-hooks/__generated__/OrderEvent.ts
@@ -0,0 +1,100 @@
+/* tslint:disable */
+/* eslint-disable */
+// @generated
+// This file was automatically generated and should not be edited.
+
+import { BusEventType, OrderType, OrderStatus, OrderRejectionReason } from "@vegaprotocol/types";
+
+// ====================================================
+// GraphQL subscription operation: OrderEvent
+// ====================================================
+
+export interface OrderEvent_busEvents_event_TimeUpdate {
+ __typename: "TimeUpdate" | "MarketEvent" | "TransferResponses" | "PositionResolution" | "Trade" | "Account" | "Party" | "MarginLevels" | "Proposal" | "Vote" | "MarketData" | "NodeSignature" | "LossSocialization" | "SettlePosition" | "Market" | "Asset" | "MarketTick" | "SettleDistressed" | "AuctionEvent" | "RiskFactor" | "Deposit" | "Withdrawal" | "OracleSpec" | "LiquidityProvision";
+}
+
+export interface OrderEvent_busEvents_event_Order_market {
+ __typename: "Market";
+ /**
+ * Market full name
+ */
+ name: string;
+ /**
+ * decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
+ * number denominated in the currency of the Market. (uint64)
+ *
+ * Examples:
+ * Currency Balance decimalPlaces Real Balance
+ * GBP 100 0 GBP 100
+ * GBP 100 2 GBP 1.00
+ * GBP 100 4 GBP 0.01
+ * GBP 1 4 GBP 0.0001 ( 0.01p )
+ *
+ * GBX (pence) 100 0 GBP 1.00 (100p )
+ * GBX (pence) 100 2 GBP 0.01 ( 1p )
+ * GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
+ * GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
+ */
+ decimalPlaces: number;
+}
+
+export interface OrderEvent_busEvents_event_Order {
+ __typename: "Order";
+ /**
+ * Type the order type (defaults to PARTY)
+ */
+ type: OrderType | null;
+ /**
+ * Hash of the order data
+ */
+ id: string;
+ /**
+ * The status of an order, for example 'Active'
+ */
+ status: OrderStatus;
+ /**
+ * Reason for the order to be rejected
+ */
+ rejectionReason: OrderRejectionReason | null;
+ /**
+ * RFC3339Nano formatted date and time for when the order was created (timestamp)
+ */
+ createdAt: string;
+ /**
+ * Total number of contracts that may be bought or sold (immutable) (uint64)
+ */
+ size: string;
+ /**
+ * The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
+ */
+ price: string;
+ /**
+ * The market the order is trading on (probably stored internally as a hash of the market details)
+ */
+ market: OrderEvent_busEvents_event_Order_market | null;
+}
+
+export type OrderEvent_busEvents_event = OrderEvent_busEvents_event_TimeUpdate | OrderEvent_busEvents_event_Order;
+
+export interface OrderEvent_busEvents {
+ __typename: "BusEvent";
+ /**
+ * the type of event we're dealing with
+ */
+ type: BusEventType;
+ /**
+ * the payload - the wrapped event
+ */
+ event: OrderEvent_busEvents_event;
+}
+
+export interface OrderEvent {
+ /**
+ * Subscribe to event data from the event bus
+ */
+ busEvents: OrderEvent_busEvents[] | null;
+}
+
+export interface OrderEventVariables {
+ partyId: string;
+}
diff --git a/libs/wallet/src/order-hooks/__generated__/index.ts b/libs/wallet/src/order-hooks/__generated__/index.ts
new file mode 100644
index 000000000..20f35ba26
--- /dev/null
+++ b/libs/wallet/src/order-hooks/__generated__/index.ts
@@ -0,0 +1 @@
+export * from './OrderEvent';
diff --git a/libs/wallet/src/order-hooks/index.ts b/libs/wallet/src/order-hooks/index.ts
new file mode 100644
index 000000000..36f6e10ba
--- /dev/null
+++ b/libs/wallet/src/order-hooks/index.ts
@@ -0,0 +1,3 @@
+export * from './__generated__';
+export * from './order-event-query';
+export * from './use-order-cancel';
diff --git a/libs/wallet/src/order-hooks/order-event-query.ts b/libs/wallet/src/order-hooks/order-event-query.ts
new file mode 100644
index 000000000..4835760b6
--- /dev/null
+++ b/libs/wallet/src/order-hooks/order-event-query.ts
@@ -0,0 +1,24 @@
+import { gql } from '@apollo/client';
+
+export const ORDER_EVENT_SUB = gql`
+ subscription OrderEvent($partyId: ID!) {
+ busEvents(partyId: $partyId, batchSize: 0, types: [Order]) {
+ type
+ event {
+ ... on Order {
+ type
+ id
+ status
+ rejectionReason
+ createdAt
+ size
+ price
+ market {
+ name
+ decimalPlaces
+ }
+ }
+ }
+ }
+ }
+`;
diff --git a/libs/wallet/src/order-hooks/use-order-cancel.spec.tsx b/libs/wallet/src/order-hooks/use-order-cancel.spec.tsx
new file mode 100644
index 000000000..91afbf73a
--- /dev/null
+++ b/libs/wallet/src/order-hooks/use-order-cancel.spec.tsx
@@ -0,0 +1,123 @@
+import { MockedProvider } from '@apollo/client/testing';
+import { act, renderHook } from '@testing-library/react-hooks';
+import { MarketState, MarketTradingMode, OrderType } from '@vegaprotocol/types';
+import type { ReactNode } from 'react';
+import type { VegaKeyExtended, VegaWalletContextShape } from '../context';
+import { VegaWalletContext } from '../context';
+import { VegaTxStatus } from '../use-vega-transaction';
+import type { Order } from '../vega-order-transaction-dialog';
+import { useOrderCancel } from './use-order-cancel';
+
+const defaultMarket = {
+ __typename: 'Market',
+ id: 'market-id',
+ decimalPlaces: 2,
+ positionDecimalPlaces: 1,
+ tradingMode: MarketTradingMode.Continuous,
+ state: MarketState.Active,
+ name: 'market-name',
+ tradableInstrument: {
+ __typename: 'TradableInstrument',
+ instrument: {
+ __typename: 'Instrument',
+ product: {
+ __typename: 'Future',
+ quoteName: 'quote-name',
+ },
+ },
+ },
+ depth: {
+ __typename: 'MarketDepth',
+ lastTrade: {
+ __typename: 'Trade',
+ price: '100',
+ },
+ },
+};
+
+const defaultWalletContext = {
+ keypair: null,
+ keypairs: [],
+ sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
+ connect: jest.fn(),
+ disconnect: jest.fn(),
+ selectPublicKey: jest.fn(),
+ connector: null,
+};
+
+function setup(context?: Partial) {
+ const wrapper = ({ children }: { children: ReactNode }) => (
+
+
+ {children}
+
+
+ );
+ return renderHook(() => useOrderCancel(), { wrapper });
+}
+
+describe('useOrderCancel', () => {
+ it('has the correct default state', () => {
+ const { result } = setup();
+ expect(typeof result.current.cancel).toEqual('function');
+ expect(typeof result.current.reset).toEqual('function');
+ expect(result.current.transaction.status).toEqual(VegaTxStatus.Default);
+ expect(result.current.transaction.txHash).toEqual(null);
+ expect(result.current.transaction.error).toEqual(null);
+ });
+
+ it('should not sendTx if no keypair', async () => {
+ const mockSendTx = jest.fn();
+ const order: Order = {
+ type: OrderType.Market,
+ size: '10',
+ price: '1234567.89',
+ status: '',
+ rejectionReason: null,
+ market: defaultMarket,
+ };
+ const { result } = setup({
+ sendTx: mockSendTx,
+ keypairs: [],
+ keypair: null,
+ });
+ await act(async () => {
+ result.current.cancel(order);
+ });
+ expect(mockSendTx).not.toHaveBeenCalled();
+ });
+
+ it('should cancel a correctly formatted order', async () => {
+ const mockSendTx = jest.fn().mockReturnValue(Promise.resolve({}));
+ const keypair = {
+ pub: '0x123',
+ } as VegaKeyExtended;
+ const order: Order = {
+ type: OrderType.Limit,
+ size: '10',
+ price: '1234567.89',
+ status: '',
+ rejectionReason: null,
+ market: defaultMarket,
+ };
+ const { result } = setup({
+ sendTx: mockSendTx,
+ keypairs: [keypair],
+ keypair,
+ });
+
+ await act(async () => {
+ result.current.cancel(order);
+ });
+
+ expect(mockSendTx).toHaveBeenCalledWith({
+ pubKey: keypair.pub,
+ propagate: true,
+ orderCancellation: {
+ marketId: 'market-id',
+ },
+ });
+ });
+});
diff --git a/libs/wallet/src/order-hooks/use-order-cancel.tsx b/libs/wallet/src/order-hooks/use-order-cancel.tsx
new file mode 100644
index 000000000..9d335c936
--- /dev/null
+++ b/libs/wallet/src/order-hooks/use-order-cancel.tsx
@@ -0,0 +1,83 @@
+import { useCallback, useState } from 'react';
+import { determineId } from '@vegaprotocol/react-helpers';
+
+import { useVegaTransaction } from '../use-vega-transaction';
+import { useVegaWallet } from '../use-vega-wallet';
+import { useSubscription } from '@apollo/client';
+import type {
+ OrderEvent,
+ OrderEventVariables,
+ OrderEvent_busEvents_event_Order,
+} from './__generated__/OrderEvent';
+import { ORDER_EVENT_SUB } from './order-event-query';
+import * as Sentry from '@sentry/react';
+
+export const useOrderCancel = () => {
+ const { keypair } = useVegaWallet();
+ const { send, transaction, reset: resetTransaction } = useVegaTransaction();
+ const [updatedOrder, setUpdatedOrder] =
+ useState(null);
+ const [id, setId] = useState('');
+
+ // Start a subscription looking for the newly created order
+ useSubscription(ORDER_EVENT_SUB, {
+ variables: { partyId: keypair?.pub || '' },
+ skip: !id,
+ onSubscriptionData: ({ subscriptionData }) => {
+ if (!subscriptionData.data?.busEvents?.length) {
+ return;
+ }
+ // No types available for the subscription result
+ const matchingOrderEvent = subscriptionData.data.busEvents[0].event;
+
+ if (matchingOrderEvent && matchingOrderEvent.__typename === 'Order') {
+ setUpdatedOrder(matchingOrderEvent);
+ resetTransaction();
+ }
+ },
+ });
+
+ const cancel = useCallback(
+ async (order) => {
+ if (!keypair) {
+ return;
+ }
+
+ setUpdatedOrder(null);
+
+ try {
+ const res = await send({
+ pubKey: keypair.pub,
+ propagate: true,
+ orderCancellation: {
+ orderId: order.id,
+ marketId: order.market.id,
+ },
+ });
+
+ if (res?.signature) {
+ setId(determineId(res.signature));
+ }
+ return res;
+ } catch (e) {
+ Sentry.captureException(e);
+ return;
+ }
+ },
+ [keypair, send]
+ );
+
+ const reset = useCallback(() => {
+ resetTransaction();
+ setUpdatedOrder(null);
+ setId('');
+ }, [resetTransaction]);
+
+ return {
+ transaction,
+ finalizedOrder: updatedOrder,
+ id,
+ cancel,
+ reset,
+ };
+};
diff --git a/libs/wallet/src/provider.spec.tsx b/libs/wallet/src/provider.spec.tsx
index 43559fbe0..4babf710b 100644
--- a/libs/wallet/src/provider.spec.tsx
+++ b/libs/wallet/src/provider.spec.tsx
@@ -1,7 +1,7 @@
import { act, fireEvent, render, screen } from '@testing-library/react';
import type { VegaKey } from '@vegaprotocol/vegawallet-service-api-client';
import { RestConnector } from './connectors';
-import { useVegaWallet } from './hooks';
+import { useVegaWallet } from './use-vega-wallet';
import { VegaWalletProvider } from './provider';
import { WALLET_KEY } from './storage-keys';
diff --git a/libs/wallet/src/provider.tsx b/libs/wallet/src/provider.tsx
index 7428cbe63..640a79e61 100644
--- a/libs/wallet/src/provider.tsx
+++ b/libs/wallet/src/provider.tsx
@@ -2,7 +2,7 @@ import { LocalStorage, t } from '@vegaprotocol/react-helpers';
import type { ReactNode } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { VegaKeyExtended, VegaWalletContextShape } from '.';
-import type { VegaConnector } from './connectors';
+import type { VegaConnector } from './connectors/vega-connector';
import { VegaWalletContext } from './context';
import { WALLET_KEY } from './storage-keys';
import type { TransactionSubmission } from './types';
@@ -43,7 +43,6 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
});
setKeypairs(publicKeysWithName);
-
if (publicKey === null) {
setPublicKey(publicKeysWithName[0].pub);
}
@@ -60,6 +59,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
try {
await connector.current?.disconnect();
setKeypairs(null);
+ setPublicKey(null);
connector.current = null;
LocalStorage.removeItem(WALLET_KEY);
return true;
@@ -104,7 +104,7 @@ export const VegaWalletProvider = ({ children }: VegaWalletProviderProps) => {
disconnect,
connector: connector.current,
sendTx,
- };
+ } as VegaWalletContextShape;
}, [keypair, keypairs, setPublicKey, connect, disconnect, connector, sendTx]);
return (
diff --git a/libs/wallet/src/rest-connector-form.tsx b/libs/wallet/src/rest-connector-form.tsx
index 37005a71e..9533f0d87 100644
--- a/libs/wallet/src/rest-connector-form.tsx
+++ b/libs/wallet/src/rest-connector-form.tsx
@@ -5,6 +5,7 @@ import { useForm } from 'react-hook-form';
import type { RestConnector } from '.';
interface FormFields {
+ url: string;
wallet: string;
passphrase: string;
}
@@ -14,6 +15,8 @@ interface RestConnectorFormProps {
onAuthenticate: () => void;
}
+const VEGA_DEFAULT_URL = 'http://localhost:1789/api/v1';
+
export function RestConnectorForm({
connector,
onAuthenticate,
@@ -24,12 +27,16 @@ export function RestConnectorForm({
register,
handleSubmit,
formState: { errors },
- } = useForm();
+ } = useForm({
+ defaultValues: {
+ url: VEGA_DEFAULT_URL,
+ },
+ });
async function onSubmit(fields: FormFields) {
try {
setError('');
- const res = await connector.authenticate({
+ const res = await connector.authenticate(fields.url, {
wallet: fields.wallet,
passphrase: fields.passphrase,
});
@@ -41,7 +48,7 @@ export function RestConnectorForm({
}
} catch (err) {
if (err instanceof TypeError) {
- setError(t('Wallet not running at http://localhost:1789'));
+ setError(t(`Wallet not running at ${fields.url}`));
} else if (err instanceof Error) {
setError(t('Authentication failed'));
} else {
@@ -52,6 +59,18 @@ export function RestConnectorForm({
return (