Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
973a2759c0 | ||
|
|
b163eb6bfe | ||
|
|
8b69169021 | ||
|
|
79ac604009 | ||
|
|
162b167860 | ||
|
|
a4149cc55e | ||
|
|
4161a74eaf |
@@ -48,8 +48,6 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
id: ${{ github.event.release.id }}
|
||||
body: |
|
||||
${{ github.event.release.body }}
|
||||
|
||||
---
|
||||
|
||||
# Deployments
|
||||
@@ -71,6 +69,6 @@ jobs:
|
||||
|
||||
IPFS gateways:
|
||||
|
||||
https://${{ env.IPFS_V1 }}.dweb.link/
|
||||
https://${{ env.IPFS_V1 }}.cf-ipfs.com/
|
||||
https://${{ env.IPFS_V1 }}.ipfs.dweb.link/
|
||||
https://${{ env.IPFS_V1 }}.ipfs.cf-ipfs.com/
|
||||
ipfs://${{ env.IPFS_V0 }}/
|
||||
|
||||
@@ -16,6 +16,7 @@ import { AppStateProvider } from './contexts/app-state/app-state-provider';
|
||||
import { ContractsProvider } from './contexts/contracts/contracts-provider';
|
||||
import { AppRouter } from './routes';
|
||||
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||
import { WithdrawalApprovalDialogContainer } from '@vegaprotocol/web3';
|
||||
import {
|
||||
createConnectors,
|
||||
useEthTransactionManager,
|
||||
@@ -43,7 +44,7 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { WithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { SplashLoader } from './components/splash-loader';
|
||||
import { ToastsManager } from './toasts-manager';
|
||||
import {
|
||||
@@ -158,7 +159,8 @@ const Web3Container = ({
|
||||
<InitializeHandlers />
|
||||
<VegaWalletDialogs />
|
||||
<TransactionModal />
|
||||
<WithdrawalDialog />
|
||||
<CreateWithdrawalDialog />
|
||||
<WithdrawalApprovalDialogContainer />
|
||||
<TelemetryDialog />
|
||||
</>
|
||||
</BalanceManager>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"tranche_end": "2023-06-11T00:00:00.000Z",
|
||||
"total_added": "15949",
|
||||
"total_removed": "215.8495183664",
|
||||
"locked_amount": "12086.6850242682186761",
|
||||
"locked_amount": "11959.0406231332146841",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "11447",
|
||||
@@ -329,7 +329,7 @@
|
||||
"tranche_end": "2023-05-20T00:00:00.000Z",
|
||||
"total_added": "20967.125",
|
||||
"total_removed": "4313.169927159795",
|
||||
"locked_amount": "1043.349054639274308255",
|
||||
"locked_amount": "869.949636622298931016875",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "897",
|
||||
@@ -2501,7 +2501,7 @@
|
||||
"tranche_start": "2023-03-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-06T00:00:00.000Z",
|
||||
"total_added": "14099",
|
||||
"total_removed": "4403.49002352036",
|
||||
"total_removed": "5107.49002352036",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -3286,6 +3286,11 @@
|
||||
"user": "0xd8489848Ee545DB18859B3e03dE01aD99a22a807",
|
||||
"tx": "0xf8f66ba448c7711852effc4782d944f50061c1b077c9e9e6dfbc93a0ec45c297"
|
||||
},
|
||||
{
|
||||
"amount": "704",
|
||||
"user": "0x0b5739816AB0097C43F77479048a0A6093DD0441",
|
||||
"tx": "0x0ed882c629ec1b0154e769f86e3ecd9f4680de6d3bcec67556225eaaaa1bddf2"
|
||||
},
|
||||
{
|
||||
"amount": "30",
|
||||
"user": "0xBe9F912Ad481C61B653463E8F1D2b2b310D49861",
|
||||
@@ -4021,10 +4026,17 @@
|
||||
"tx": "0x46d80a145d2f49ef3152cbfaa6d2bb054deabe21396ab43553a3f076e2db62e1"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "704",
|
||||
"user": "0x0b5739816AB0097C43F77479048a0A6093DD0441",
|
||||
"tranche_id": 53,
|
||||
"tx": "0x0ed882c629ec1b0154e769f86e3ecd9f4680de6d3bcec67556225eaaaa1bddf2"
|
||||
}
|
||||
],
|
||||
"total_tokens": "704",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "704"
|
||||
"withdrawn_tokens": "704",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x981D29D939cAa4ADC04A5e44224dBB45539E8730",
|
||||
@@ -5584,7 +5596,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "47605.4017552180739029016",
|
||||
"locked_amount": "47546.4919777417822259639",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -5650,7 +5662,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "185.341149013024125",
|
||||
"locked_amount": "181.933156542531675",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -5683,7 +5695,7 @@
|
||||
"tranche_end": "2023-11-01T00:00:00.000Z",
|
||||
"total_added": "15000.000000000000015",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "13572.785514794685013572785514794685",
|
||||
"locked_amount": "13552.559820350241013552559820350241",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1.5e-14",
|
||||
@@ -5791,7 +5803,7 @@
|
||||
"tranche_end": "2023-09-01T00:00:00.000Z",
|
||||
"total_added": "17500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "10033.28599914452525",
|
||||
"locked_amount": "10009.68935562600725",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -6058,7 +6070,7 @@
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "18592.291570575",
|
||||
"locked_amount": "15433.59854588704875",
|
||||
"locked_amount": "15382.196228514426375",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -6510,7 +6522,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "47561.9678160577578038775",
|
||||
"locked_amount": "47503.1117863411078668825",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -6543,7 +6555,7 @@
|
||||
"tranche_end": "2024-04-01T00:00:00.000Z",
|
||||
"total_added": "54144.7663",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "47116.7217333150004818831",
|
||||
"locked_amount": "47080.01840876004104699305",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "54144.7663",
|
||||
@@ -6576,7 +6588,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "18435.7575659563655",
|
||||
"locked_amount": "18393.206399036021604",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -6769,7 +6781,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1664.285419837646",
|
||||
"locked_amount": "1660.88676433282625",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -7838,7 +7850,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "1709370.7872515768348",
|
||||
"locked_amount": "77027.5703575844087789349",
|
||||
"locked_amount": "75708.9407040557483188244",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -41915,7 +41927,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "747515.181114080043393",
|
||||
"locked_amount": "142865.8822173107416572269134",
|
||||
"locked_amount": "140839.6075379211948069455415",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -43319,7 +43331,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "889552.43313356066956852",
|
||||
"locked_amount": "5806280.83111707407448955866311495377399295",
|
||||
"locked_amount": "5799095.79120322791577005129823294316842385",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -60271,7 +60283,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "45798.0743154683416",
|
||||
"locked_amount": "22637.900715233350373427636631164",
|
||||
"locked_amount": "22316.8408879026950634697130999496",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { TradingViews } from './trade-views';
|
||||
import { MarketSelector } from './market-selector';
|
||||
import { HeaderStats } from './header-stats';
|
||||
import { PositionsMultiKey } from '@vegaprotocol/positions';
|
||||
|
||||
interface TradeGridProps {
|
||||
market: Market | null;
|
||||
@@ -130,6 +131,11 @@ const MarketBottomPanel = memo(
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-bottom-right">
|
||||
<Tab id="positions-multi" name={t('Positions v2')}>
|
||||
<VegaWalletContainer>
|
||||
<PositionsMultiKey />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.positions.component
|
||||
|
||||
@@ -20,6 +20,7 @@ export const SettlementDateCell = ({
|
||||
}: SettlementDataCellProps) => {
|
||||
const linkCreator = useLinks(DApp.Explorer);
|
||||
const date = closeTimestamp ? new Date(closeTimestamp) : metaDate;
|
||||
console.log(metaDate);
|
||||
|
||||
let text = '';
|
||||
if (!date) {
|
||||
|
||||
@@ -92,5 +92,8 @@ const cacheConfig: InMemoryCacheConfig = {
|
||||
Fees: {
|
||||
keyFields: false,
|
||||
},
|
||||
Position: {
|
||||
keyFields: ['market', ['id'], 'party', ['id']],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -46,7 +46,7 @@ export const Navbar = ({
|
||||
return (
|
||||
<Navigation
|
||||
appName="Console"
|
||||
theme={theme}
|
||||
theme={'dark'}
|
||||
actions={
|
||||
<>
|
||||
<ProtocolUpgradeCountdown
|
||||
|
||||
@@ -6,7 +6,10 @@ import { VegaConnectDialog } from '@vegaprotocol/wallet';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { DepositDialog } from '@vegaprotocol/deposits';
|
||||
import { Web3ConnectUncontrolledDialog } from '@vegaprotocol/web3';
|
||||
import {
|
||||
Web3ConnectUncontrolledDialog,
|
||||
WithdrawalApprovalDialogContainer,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { WelcomeDialog } from '../components/welcome-dialog';
|
||||
import { TransferDialog } from '@vegaprotocol/accounts';
|
||||
|
||||
@@ -27,6 +30,7 @@ const DialogsContainer = () => {
|
||||
<Web3ConnectUncontrolledDialog />
|
||||
<CreateWithdrawalDialog />
|
||||
<TransferDialog />
|
||||
<WithdrawalApprovalDialogContainer />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './lib/__generated__/Positions';
|
||||
export * from './lib/positions-container';
|
||||
export * from './lib/positions-data-providers';
|
||||
export * from './lib/positions-multi-key';
|
||||
export * from './lib/margin-data-provider';
|
||||
export * from './lib/positions-table';
|
||||
export * from './lib/use-market-margin';
|
||||
|
||||
@@ -9,6 +9,9 @@ fragment PositionFields on Position {
|
||||
market {
|
||||
id
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
query Positions($partyId: ID!) {
|
||||
@@ -34,6 +37,8 @@ subscription PositionsSubscription($partyId: ID!) {
|
||||
marketId
|
||||
lossSocializationAmount
|
||||
positionStatus
|
||||
partyId
|
||||
marketId
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
fragment PositionMultiFields on Position {
|
||||
realisedPNL
|
||||
openVolume
|
||||
unrealisedPNL
|
||||
averageEntryPrice
|
||||
updatedAt
|
||||
positionStatus
|
||||
lossSocializationAmount
|
||||
market {
|
||||
id
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
query PositionsMulti($partyIds: [ID!]!) {
|
||||
positions(filter: { partyIds: $partyIds }) {
|
||||
edges {
|
||||
node {
|
||||
...PositionMultiFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query MarketName($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query MarketDecimals($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
decimalPlaces
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -3,21 +3,21 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } };
|
||||
export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string }, party: { __typename?: 'Party', id: string } };
|
||||
|
||||
export type PositionsQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } } }> | null } | null } | null };
|
||||
export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string }, party: { __typename?: 'Party', id: string } } }> | null } | null } | null };
|
||||
|
||||
export type PositionsSubscriptionSubscriptionVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type PositionsSubscriptionSubscription = { __typename?: 'Subscription', positions: Array<{ __typename?: 'PositionUpdate', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, marketId: string, lossSocializationAmount: string, positionStatus: Types.PositionStatus }> };
|
||||
export type PositionsSubscriptionSubscription = { __typename?: 'Subscription', positions: Array<{ __typename?: 'PositionUpdate', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, marketId: string, lossSocializationAmount: string, positionStatus: Types.PositionStatus, partyId: string }> };
|
||||
|
||||
export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } };
|
||||
|
||||
@@ -57,6 +57,9 @@ export const PositionFieldsFragmentDoc = gql`
|
||||
market {
|
||||
id
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const MarginFieldsFragmentDoc = gql`
|
||||
@@ -126,6 +129,8 @@ export const PositionsSubscriptionDocument = gql`
|
||||
marketId
|
||||
lossSocializationAmount
|
||||
positionStatus
|
||||
partyId
|
||||
marketId
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type PositionMultiFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string }, party: { __typename?: 'Party', id: string } };
|
||||
|
||||
export type PositionsMultiQueryVariables = Types.Exact<{
|
||||
partyIds: Array<Types.Scalars['ID']> | Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type PositionsMultiQuery = { __typename?: 'Query', positions?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string }, party: { __typename?: 'Party', id: string } } }> | null } | null };
|
||||
|
||||
export type MarketNameQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarketNameQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', code: string } } } | null };
|
||||
|
||||
export type MarketDecimalsQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarketDecimalsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number } | null };
|
||||
|
||||
export const PositionMultiFieldsFragmentDoc = gql`
|
||||
fragment PositionMultiFields on Position {
|
||||
realisedPNL
|
||||
openVolume
|
||||
unrealisedPNL
|
||||
averageEntryPrice
|
||||
updatedAt
|
||||
positionStatus
|
||||
lossSocializationAmount
|
||||
market {
|
||||
id
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const PositionsMultiDocument = gql`
|
||||
query PositionsMulti($partyIds: [ID!]!) {
|
||||
positions(filter: {partyIds: $partyIds}) {
|
||||
edges {
|
||||
node {
|
||||
...PositionMultiFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${PositionMultiFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __usePositionsMultiQuery__
|
||||
*
|
||||
* To run a query within a React component, call `usePositionsMultiQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `usePositionsMultiQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = usePositionsMultiQuery({
|
||||
* variables: {
|
||||
* partyIds: // value for 'partyIds'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function usePositionsMultiQuery(baseOptions: Apollo.QueryHookOptions<PositionsMultiQuery, PositionsMultiQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<PositionsMultiQuery, PositionsMultiQueryVariables>(PositionsMultiDocument, options);
|
||||
}
|
||||
export function usePositionsMultiLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PositionsMultiQuery, PositionsMultiQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<PositionsMultiQuery, PositionsMultiQueryVariables>(PositionsMultiDocument, options);
|
||||
}
|
||||
export type PositionsMultiQueryHookResult = ReturnType<typeof usePositionsMultiQuery>;
|
||||
export type PositionsMultiLazyQueryHookResult = ReturnType<typeof usePositionsMultiLazyQuery>;
|
||||
export type PositionsMultiQueryResult = Apollo.QueryResult<PositionsMultiQuery, PositionsMultiQueryVariables>;
|
||||
export const MarketNameDocument = gql`
|
||||
query MarketName($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useMarketNameQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useMarketNameQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useMarketNameQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useMarketNameQuery({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useMarketNameQuery(baseOptions: Apollo.QueryHookOptions<MarketNameQuery, MarketNameQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<MarketNameQuery, MarketNameQueryVariables>(MarketNameDocument, options);
|
||||
}
|
||||
export function useMarketNameLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketNameQuery, MarketNameQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<MarketNameQuery, MarketNameQueryVariables>(MarketNameDocument, options);
|
||||
}
|
||||
export type MarketNameQueryHookResult = ReturnType<typeof useMarketNameQuery>;
|
||||
export type MarketNameLazyQueryHookResult = ReturnType<typeof useMarketNameLazyQuery>;
|
||||
export type MarketNameQueryResult = Apollo.QueryResult<MarketNameQuery, MarketNameQueryVariables>;
|
||||
export const MarketDecimalsDocument = gql`
|
||||
query MarketDecimals($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
decimalPlaces
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useMarketDecimalsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useMarketDecimalsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useMarketDecimalsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useMarketDecimalsQuery({
|
||||
* variables: {
|
||||
* marketId: // value for 'marketId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useMarketDecimalsQuery(baseOptions: Apollo.QueryHookOptions<MarketDecimalsQuery, MarketDecimalsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<MarketDecimalsQuery, MarketDecimalsQueryVariables>(MarketDecimalsDocument, options);
|
||||
}
|
||||
export function useMarketDecimalsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MarketDecimalsQuery, MarketDecimalsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<MarketDecimalsQuery, MarketDecimalsQueryVariables>(MarketDecimalsDocument, options);
|
||||
}
|
||||
export type MarketDecimalsQueryHookResult = ReturnType<typeof useMarketDecimalsQuery>;
|
||||
export type MarketDecimalsLazyQueryHookResult = ReturnType<typeof useMarketDecimalsLazyQuery>;
|
||||
export type MarketDecimalsQueryResult = Apollo.QueryResult<MarketDecimalsQuery, MarketDecimalsQueryVariables>;
|
||||
@@ -0,0 +1,159 @@
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useMarketDecimalsQuery,
|
||||
useMarketNameQuery,
|
||||
usePositionsMultiQuery,
|
||||
} from './__generated__/PositionsMulti';
|
||||
import { AgGridLazy } from '@vegaprotocol/datagrid';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import type {
|
||||
PositionsSubscriptionSubscription,
|
||||
PositionsSubscriptionSubscriptionVariables,
|
||||
} from './__generated__/Positions';
|
||||
import { PositionsSubscriptionDocument } from './__generated__/Positions';
|
||||
import { addDecimalsFormatNumber, truncateByChars } from '@vegaprotocol/utils';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
export const PositionsMultiKey = () => {
|
||||
const { pubKeys } = useVegaWallet();
|
||||
const { data } = usePositions();
|
||||
const colDefs = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
field: 'party.id',
|
||||
valueFormatter: ({ value }) => {
|
||||
const truncated = truncateByChars(value);
|
||||
const pk = pubKeys?.find((pk) => pk.publicKey === value);
|
||||
return pk ? pk.name + ' ' + truncated : truncated;
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: 'Market',
|
||||
field: 'market.id',
|
||||
cellRenderer: ({ value }) => {
|
||||
return <MarketCell id={value} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'openVolume',
|
||||
},
|
||||
{
|
||||
field: 'unrealisedPNL',
|
||||
cellRenderer: ({ data }) => {
|
||||
return (
|
||||
<PNLCell marketId={data.market.id} value={data.unrealisedPNL} />
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'realisedPNL',
|
||||
cellRenderer: ({ data }) => {
|
||||
return <PNLCell marketId={data.market.id} value={data.realisedPNL} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'updatedAt',
|
||||
},
|
||||
];
|
||||
}, [pubKeys]);
|
||||
|
||||
const rowData = data?.positions?.edges?.length
|
||||
? data.positions.edges.map((e) => e.node)
|
||||
: [];
|
||||
|
||||
return (
|
||||
<AgGridLazy
|
||||
getRowId={({ data }) => `${data.party.id}:${data.market.id}`}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
columnDefs={colDefs}
|
||||
rowData={rowData}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const usePositions = () => {
|
||||
const { pubKeys } = useVegaWallet();
|
||||
const client = useApolloClient();
|
||||
const { data, loading, error } = usePositionsMultiQuery({
|
||||
variables: {
|
||||
partyIds: pubKeys ? pubKeys.map((pk) => pk.publicKey) : [],
|
||||
},
|
||||
skip: !pubKeys || pubKeys.length === 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!pubKeys?.length) return;
|
||||
const subs = pubKeys.map((p) => {
|
||||
return client
|
||||
.subscribe<
|
||||
PositionsSubscriptionSubscription,
|
||||
PositionsSubscriptionSubscriptionVariables
|
||||
>({
|
||||
query: PositionsSubscriptionDocument,
|
||||
variables: {
|
||||
partyId: p.publicKey,
|
||||
},
|
||||
// no cache as we only want to store data in the root Position query,
|
||||
// we modify this cache entry directly below
|
||||
fetchPolicy: 'no-cache',
|
||||
})
|
||||
.subscribe(({ data }) => {
|
||||
data?.positions.forEach((position) => {
|
||||
const id = client.cache.identify({
|
||||
__typename: 'Position',
|
||||
party: { id: position.partyId },
|
||||
market: { id: position.marketId },
|
||||
});
|
||||
client.cache.modify({
|
||||
id,
|
||||
fields: {
|
||||
realisedPNL: () => position.realisedPNL,
|
||||
unrealisedPNL: () => position.unrealisedPNL,
|
||||
openVolume: () => position.openVolume,
|
||||
averageEntryPrice: () => position.averageEntryPrice,
|
||||
positionStatus: () => position.positionStatus,
|
||||
lossSocializationAmount: () => position.lossSocializationAmount,
|
||||
updatedAt: () => position.updatedAt,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
subs.forEach((sub) => {
|
||||
sub.unsubscribe();
|
||||
});
|
||||
};
|
||||
}, [pubKeys, client]);
|
||||
|
||||
return { data, loading, error };
|
||||
};
|
||||
|
||||
const MarketCell = ({ id }: { id: string }) => {
|
||||
const { data } = useMarketNameQuery({
|
||||
variables: {
|
||||
marketId: id,
|
||||
},
|
||||
// We should cache all static market data higher up the render tree so this
|
||||
// can be cache only
|
||||
// fetchPolicy: 'cache-only',
|
||||
});
|
||||
if (!data?.market) return <span>-</span>;
|
||||
return <span>{data.market.tradableInstrument.instrument.code}</span>;
|
||||
};
|
||||
|
||||
const PNLCell = ({ marketId, value }: { marketId: string; value: string }) => {
|
||||
const { data } = useMarketDecimalsQuery({
|
||||
variables: {
|
||||
marketId,
|
||||
},
|
||||
// We should cache all static market data higher up the render tree so this
|
||||
// can be cache only
|
||||
// fetchPolicy: 'cache-only'
|
||||
});
|
||||
if (!data?.market) return <span>-</span>;
|
||||
return (
|
||||
<span>{addDecimalsFormatNumber(value, data.market.decimalPlaces)}</span>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,8 @@ import styles from './toast.module.css';
|
||||
import type { IconName } from '@blueprintjs/icons';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import classNames from 'classnames';
|
||||
import type { HTMLAttributes, HtmlHTMLAttributes } from 'react';
|
||||
import type { HTMLAttributes, HtmlHTMLAttributes, ReactNode } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { forwardRef, useEffect } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useLayoutEffect } from 'react';
|
||||
@@ -11,6 +12,7 @@ import { useRef } from 'react';
|
||||
import { Intent } from '../../utils/intent';
|
||||
import { Icon } from '../icon';
|
||||
import { Loader } from '../loader';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export type ToastContent = JSX.Element | undefined;
|
||||
|
||||
@@ -63,11 +65,88 @@ export const Panel = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
||||
}
|
||||
);
|
||||
|
||||
type CollapsiblePanelProps = {
|
||||
actions?: ReactNode;
|
||||
};
|
||||
export const CollapsiblePanel = forwardRef<
|
||||
HTMLDivElement,
|
||||
CollapsiblePanelProps & HTMLAttributes<HTMLDivElement>
|
||||
>(({ children, className, actions, ...props }, ref) => {
|
||||
const [collapsed, setCollapsed] = useState(true);
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<div
|
||||
data-panel
|
||||
ref={ref}
|
||||
data-test
|
||||
className={classNames(
|
||||
'relative',
|
||||
'p-2 rounded mt-[10px]',
|
||||
'font-mono text-[12px] leading-[16px] font-normal',
|
||||
'[&>h4]:font-bold',
|
||||
'overflow-auto',
|
||||
{
|
||||
'h-[64px] overflow-hidden': collapsed,
|
||||
'pb-4': !collapsed,
|
||||
},
|
||||
className
|
||||
)}
|
||||
aria-expanded={!collapsed}
|
||||
onDoubleClick={(e) => {
|
||||
e.preventDefault();
|
||||
setCollapsed(!collapsed);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
{collapsed && (
|
||||
<div
|
||||
data-panel-curtain
|
||||
className={classNames(
|
||||
'bg-gradient-to-b from-transparent to-inherit',
|
||||
'absolute bottom-0 left-0 h-8 w-full pointer-events-none'
|
||||
)}
|
||||
></div>
|
||||
)}
|
||||
<div
|
||||
data-panel-actions
|
||||
className={classNames(
|
||||
'absolute bottom-0 right-0',
|
||||
'p-2',
|
||||
'rounded-tl',
|
||||
'flex align-middle gap-1'
|
||||
)}
|
||||
>
|
||||
{actions}
|
||||
<button
|
||||
className="cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setCollapsed(!collapsed);
|
||||
}}
|
||||
title={collapsed ? t('Expand') : t('Collapse')}
|
||||
aria-label={collapsed ? t('Expand') : t('Collapse')}
|
||||
>
|
||||
{collapsed ? (
|
||||
<Icon name="expand-all" size={3} />
|
||||
) : (
|
||||
<Icon name="collapse-all" size={3} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const ToastHeading = forwardRef<
|
||||
HTMLHeadingElement,
|
||||
HtmlHTMLAttributes<HTMLHeadingElement>
|
||||
>(({ children, ...props }, ref) => (
|
||||
<h3 ref={ref} className="text-sm uppercase mb-1" {...props}>
|
||||
>(({ children, className, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={classNames('text-sm uppercase mb-1', className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h3>
|
||||
));
|
||||
@@ -167,7 +246,7 @@ export const Toast = ({
|
||||
},
|
||||
// panel's colours
|
||||
{
|
||||
'[&_[data-panel]]:bg-vega-light-150 [&_[data-panel]]:dark:bg-vega-dark-150 ':
|
||||
'[&_[data-panel]]:bg-vega-light-150 [&_[data-panel]]:dark:bg-vega-dark-150':
|
||||
intent === Intent.None,
|
||||
'[&_[data-panel]]:bg-vega-blue-350 [&_[data-panel]]:dark:bg-vega-blue-650':
|
||||
intent === Intent.Primary,
|
||||
@@ -178,6 +257,31 @@ export const Toast = ({
|
||||
'[&_[data-panel]]:bg-vega-pink-350 [&_[data-panel]]:dark:bg-vega-pink-650':
|
||||
intent === Intent.Danger,
|
||||
},
|
||||
{
|
||||
'[&_[data-panel]]:to-vega-light-150 [&_[data-panel]]:dark:to-vega-dark-150':
|
||||
intent === Intent.None,
|
||||
'[&_[data-panel]]:to-vega-blue-350 [&_[data-panel]]:dark:to-vega-blue-650':
|
||||
intent === Intent.Primary,
|
||||
'[&_[data-panel]]:to-vega-green-350 [&_[data-panel]]:dark:to-vega-green-650':
|
||||
intent === Intent.Success,
|
||||
'[&_[data-panel]]:to-vega-orange-350 [&_[data-panel]]:dark:to-vega-orange-650':
|
||||
intent === Intent.Warning,
|
||||
'[&_[data-panel]]:to-vega-pink-350 [&_[data-panel]]:dark:to-vega-pink-650':
|
||||
intent === Intent.Danger,
|
||||
},
|
||||
// panel's actions
|
||||
{
|
||||
'[&_[data-panel-actions]]:bg-vega-light-200 [&_[data-panel-actions]]:dark:bg-vega-dark-100 ':
|
||||
intent === Intent.None,
|
||||
'[&_[data-panel-actions]]:bg-vega-blue-400 [&_[data-panel-actions]]:dark:bg-vega-blue-600':
|
||||
intent === Intent.Primary,
|
||||
'[&_[data-panel-actions]]:bg-vega-green-400 [&_[data-panel-actions]]:dark:bg-vega-green-600':
|
||||
intent === Intent.Success,
|
||||
'[&_[data-panel-actions]]:bg-vega-orange-400 [&_[data-panel-actions]]:dark:bg-vega-orange-600':
|
||||
intent === Intent.Warning,
|
||||
'[&_[data-panel-actions]]:bg-vega-pink-400 [&_[data-panel-actions]]:dark:bg-vega-pink-600':
|
||||
intent === Intent.Danger,
|
||||
},
|
||||
// panels's progress bar colours
|
||||
'[&_[data-progress-bar]]:mt-[10px] [&_[data-progress-bar]]:mb-[4px]',
|
||||
{
|
||||
|
||||
@@ -9,20 +9,21 @@ export * from './lib/use-ethereum-config';
|
||||
export * from './lib/use-ethereum-read-contract';
|
||||
export * from './lib/use-ethereum-transaction-manager';
|
||||
export * from './lib/use-ethereum-transaction-store';
|
||||
export * from './lib/use-ethereum-transaction-toasts';
|
||||
export * from './lib/use-ethereum-transaction-updater';
|
||||
export * from './lib/use-ethereum-transaction';
|
||||
export * from './lib/use-ethereum-withdraw-approval-toasts';
|
||||
export * from './lib/use-ethereum-withdraw-approvals-manager';
|
||||
export * from './lib/use-ethereum-withdraw-approvals-store';
|
||||
export * from './lib/use-get-withdraw-delay';
|
||||
export * from './lib/use-get-withdraw-threshold';
|
||||
export * from './lib/use-token-contract';
|
||||
export * from './lib/use-token-decimals';
|
||||
export * from './lib/use-vega-transaction-toasts';
|
||||
export * from './lib/use-web3-disconnect';
|
||||
export * from './lib/web3-connect-dialog';
|
||||
export * from './lib/web3-connect-store';
|
||||
export * from './lib/web3-connectors';
|
||||
export * from './lib/web3-provider';
|
||||
export * from './lib/use-vega-transaction-toasts';
|
||||
export * from './lib/use-ethereum-transaction-toasts';
|
||||
export * from './lib/use-ethereum-withdraw-approval-toasts';
|
||||
export * from './lib/withdrawal-approval-dialog';
|
||||
export * from './lib/withdrawal-approval-status';
|
||||
|
||||
@@ -50,11 +50,10 @@ const EthWithdrawalApprovalToastContent = ({
|
||||
</strong>
|
||||
</Panel>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{title.length > 0 && (
|
||||
<ToastHeading className="font-bold">{title}</ToastHeading>
|
||||
)}
|
||||
{title.length > 0 && <ToastHeading>{title}</ToastHeading>}
|
||||
<VerificationStatus state={tx} />
|
||||
{details}
|
||||
</>
|
||||
|
||||
@@ -49,6 +49,7 @@ import { useMarketList } from '@vegaprotocol/markets';
|
||||
import type { Side } from '@vegaprotocol/types';
|
||||
import { OrderStatusMapping } from '@vegaprotocol/types';
|
||||
import { Size } from '@vegaprotocol/datagrid';
|
||||
import { useWithdrawalApprovalDialog } from './withdrawal-approval-dialog';
|
||||
|
||||
const intentMap: { [s in VegaTxStatus]: Intent } = {
|
||||
Default: Intent.Primary,
|
||||
@@ -457,20 +458,41 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
</Button>
|
||||
</p>
|
||||
);
|
||||
|
||||
const dialogTrigger = (
|
||||
// It has to stay as <a> due to the word breaking issue
|
||||
// eslint-disable-next-line jsx-a11y/anchor-is-valid
|
||||
<a
|
||||
href="#"
|
||||
className="inline underline underline-offset-4 cursor-pointer text-inherit break-words"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (tx.withdrawal?.id) {
|
||||
useWithdrawalApprovalDialog.getState().open(tx.withdrawal?.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('save your withdrawal details')}
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToastHeading>{t('Funds unlocked')}</ToastHeading>
|
||||
<p>{t('Your funds have been unlocked for withdrawal')}</p>
|
||||
<p>{t('Your funds have been unlocked for withdrawal.')}</p>
|
||||
{tx.txHash && (
|
||||
<p className="break-all">
|
||||
<ExternalLink
|
||||
href={explorerLink(EXPLORER_TX.replace(':hash', tx.txHash))}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t('View in block explorer')}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
<ExternalLink
|
||||
className="block mb-[5px] break-all"
|
||||
href={explorerLink(EXPLORER_TX.replace(':hash', tx.txHash))}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t('View in block explorer')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
{/* TODO: Delay message - This withdrawal is subject to a delay. Come back in 5 days to complete the withdrawal. */}
|
||||
<p className="break-words">
|
||||
{t('You can')} {dialogTrigger} {t('for extra security.')}
|
||||
</p>
|
||||
<VegaTransactionDetails tx={tx} />
|
||||
{completeWithdrawalButton}
|
||||
</>
|
||||
@@ -638,8 +660,9 @@ export const useVegaTransactionToasts = () => {
|
||||
);
|
||||
|
||||
const fromVegaTransaction = (tx: VegaStoredTxState): Toast => {
|
||||
const closeAfter = isFinal(tx) ? CLOSE_AFTER : undefined;
|
||||
const { intent, content } = getVegaTransactionContentIntent(tx);
|
||||
const closeAfter =
|
||||
isFinal(tx) && !isWithdrawTransaction(tx.body) ? CLOSE_AFTER : undefined;
|
||||
|
||||
return {
|
||||
id: `vega-${tx.id}`,
|
||||
@@ -680,10 +703,22 @@ export const getVegaTransactionContentIntent = (tx: VegaStoredTxState) => {
|
||||
}
|
||||
|
||||
// Transaction can be successful but the order can be rejected by the network
|
||||
const intentForRejectedOrder =
|
||||
tx.order &&
|
||||
!isOrderAmendmentTransaction(tx.body) &&
|
||||
getOrderToastIntent(tx.order.status);
|
||||
|
||||
// Although the transaction is completed on the vega network the whole
|
||||
// withdrawal process is not - funds are only released at this point
|
||||
const intentForCompletedWithdrawal =
|
||||
tx.status === VegaTxStatus.Complete &&
|
||||
isWithdrawTransaction(tx.body) &&
|
||||
Intent.Warning;
|
||||
|
||||
const intent =
|
||||
(tx.order &&
|
||||
!isOrderAmendmentTransaction(tx.body) &&
|
||||
getOrderToastIntent(tx.order.status)) ||
|
||||
intentForRejectedOrder ||
|
||||
intentForCompletedWithdrawal ||
|
||||
intentMap[tx.status];
|
||||
|
||||
return { intent, content };
|
||||
};
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Button,
|
||||
CopyWithTooltip,
|
||||
Dialog,
|
||||
Icon,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
Splash,
|
||||
SyntaxHighlighter,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useWithdrawalApprovalQuery } from '@vegaprotocol/wallet';
|
||||
import omit from 'lodash/omit';
|
||||
import { create } from 'zustand';
|
||||
|
||||
type WithdrawalApprovalDialogProps = {
|
||||
withdrawalId: string | undefined;
|
||||
trigger?: HTMLElement | null;
|
||||
open: boolean;
|
||||
onChange: (open: boolean) => void;
|
||||
asJson?: boolean;
|
||||
};
|
||||
export const WithdrawalApprovalDialog = ({
|
||||
withdrawalId,
|
||||
trigger,
|
||||
open,
|
||||
onChange,
|
||||
asJson,
|
||||
}: WithdrawalApprovalDialogProps) => {
|
||||
return (
|
||||
<Dialog
|
||||
title={t('Save withdrawal details')}
|
||||
icon={<Icon name="info-sign"></Icon>}
|
||||
open={open}
|
||||
onChange={(isOpen) => onChange(isOpen)}
|
||||
onCloseAutoFocus={(e) => {
|
||||
/**
|
||||
* This mimics radix's default behaviour that focuses the dialog's
|
||||
* trigger after closing itself
|
||||
*/
|
||||
if (trigger) {
|
||||
e.preventDefault();
|
||||
trigger.focus();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="pr-8">
|
||||
<p>
|
||||
{t(
|
||||
`If the network is reset or has an outage, records of your withdrawal
|
||||
may be lost. We recommend you save these details in a safe place so
|
||||
you can still complete your withdrawal.`
|
||||
)}
|
||||
</p>
|
||||
{withdrawalId ? (
|
||||
<WithdrawalApprovalDialogContent
|
||||
withdrawalId={withdrawalId}
|
||||
asJson={Boolean(asJson)}
|
||||
/>
|
||||
) : (
|
||||
<NoDataContent />
|
||||
)}
|
||||
</div>
|
||||
<div className="w-1/4">
|
||||
<Button
|
||||
data-testid="close-withdrawal-approval-dialog"
|
||||
fill={true}
|
||||
size="sm"
|
||||
onClick={() => onChange(false)}
|
||||
>
|
||||
{t('Close')}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
type WithdrawalApprovalDialogContentProps = {
|
||||
withdrawalId: string;
|
||||
asJson: boolean;
|
||||
};
|
||||
|
||||
const NoDataContent = ({ msg = t('No data') }) => (
|
||||
<div className="py-12" data-testid="splash">
|
||||
<Splash>{msg}</Splash>
|
||||
</div>
|
||||
);
|
||||
|
||||
const WithdrawalApprovalDialogContent = ({
|
||||
withdrawalId,
|
||||
asJson,
|
||||
}: WithdrawalApprovalDialogContentProps) => {
|
||||
const { data, loading } = useWithdrawalApprovalQuery({
|
||||
variables: {
|
||||
withdrawalId,
|
||||
},
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return <NoDataContent msg={t('Loading')} />;
|
||||
}
|
||||
|
||||
if (data?.erc20WithdrawalApproval) {
|
||||
const details = omit(data.erc20WithdrawalApproval, '__typename');
|
||||
|
||||
if (asJson) {
|
||||
return (
|
||||
<div className="py-4">
|
||||
<SyntaxHighlighter size="smaller" data={details} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="py-4 flex flex-col">
|
||||
<div className="self-end mb-1">
|
||||
<CopyWithTooltip text={JSON.stringify(details, undefined, 2)}>
|
||||
<Button
|
||||
className="flex gap-1 items-center no-underline"
|
||||
size="xs"
|
||||
variant="primary"
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.COPY} size={14} />
|
||||
<span className="text-sm no-underline">{t('Copy')}</span>
|
||||
</Button>
|
||||
</CopyWithTooltip>
|
||||
</div>
|
||||
<KeyValueTable>
|
||||
{Object.entries(details).map(([key, value]) => (
|
||||
<KeyValueTableRow key={key}>
|
||||
<div data-testid={`${key}_label`}>{key}</div>
|
||||
<div data-testid={`${key}_value`} className="break-all">
|
||||
{value}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
))}
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return <NoDataContent />;
|
||||
};
|
||||
|
||||
export type WithdrawalApprovalDialogStore = {
|
||||
isOpen: boolean;
|
||||
id: string;
|
||||
trigger: HTMLElement | null | undefined;
|
||||
asJson: boolean;
|
||||
setOpen: (isOpen: boolean) => void;
|
||||
open: (id: string, trigger?: HTMLElement | null, asJson?: boolean) => void;
|
||||
};
|
||||
|
||||
export const useWithdrawalApprovalDialog =
|
||||
create<WithdrawalApprovalDialogStore>()((set) => ({
|
||||
isOpen: false,
|
||||
id: '',
|
||||
trigger: null,
|
||||
asJson: false,
|
||||
setOpen: (isOpen) => {
|
||||
set({ isOpen: isOpen });
|
||||
},
|
||||
open: (id, trigger?, asJson = false) => {
|
||||
set({
|
||||
isOpen: true,
|
||||
id,
|
||||
trigger,
|
||||
asJson,
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
||||
export const WithdrawalApprovalDialogContainer = () => {
|
||||
const { isOpen, id, trigger, setOpen, asJson } =
|
||||
useWithdrawalApprovalDialog();
|
||||
return (
|
||||
<WithdrawalApprovalDialog
|
||||
withdrawalId={id}
|
||||
trigger={trigger || null}
|
||||
open={isOpen}
|
||||
onChange={setOpen}
|
||||
asJson={asJson}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,13 +1,13 @@
|
||||
export * from './lib/__generated__/Erc20Approval';
|
||||
export * from './lib/__generated__/Withdrawal';
|
||||
export * from './lib/create-withdrawal-dialog';
|
||||
export * from './lib/withdrawal-dialog';
|
||||
export * from './lib/withdraw-form';
|
||||
export * from './lib/withdraw-form-container';
|
||||
export * from './lib/withdraw-manager';
|
||||
export * from './lib/withdrawals-table';
|
||||
export * from './lib/withdrawal-feedback';
|
||||
export * from './lib/use-complete-withdraw';
|
||||
export * from './lib/use-create-withdraw';
|
||||
export * from './lib/use-verify-withdrawal';
|
||||
export * from './lib/withdraw-form-container';
|
||||
export * from './lib/withdraw-form';
|
||||
export * from './lib/withdraw-manager';
|
||||
export * from './lib/withdrawal-dialog';
|
||||
export * from './lib/withdrawal-feedback';
|
||||
export * from './lib/withdrawals-provider';
|
||||
export * from './lib/__generated__/Withdrawal';
|
||||
export * from './lib/__generated__/Erc20Approval';
|
||||
export * from './lib/withdrawals-table';
|
||||
|
||||
@@ -9,7 +9,16 @@ import {
|
||||
} from '@vegaprotocol/utils';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
ButtonLink,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
Icon,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type {
|
||||
TypedDataAgGrid,
|
||||
VegaICellRendererParams,
|
||||
@@ -18,7 +27,10 @@ import type {
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { EtherscanLink } from '@vegaprotocol/environment';
|
||||
import type { WithdrawalFieldsFragment } from './__generated__/Withdrawal';
|
||||
import { useEthWithdrawApprovalsStore } from '@vegaprotocol/web3';
|
||||
import {
|
||||
useEthWithdrawApprovalsStore,
|
||||
useWithdrawalApprovalDialog,
|
||||
} from '@vegaprotocol/web3';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
export const WithdrawalsTable = (
|
||||
@@ -119,6 +131,7 @@ export const WithdrawalsTable = (
|
||||
headerName={t('Transaction')}
|
||||
field="txHash"
|
||||
flex={2}
|
||||
type="rightAligned"
|
||||
cellRendererParams={{
|
||||
complete: (withdrawal: WithdrawalFieldsFragment) => {
|
||||
createWithdrawApproval(withdrawal);
|
||||
@@ -139,18 +152,51 @@ export type CompleteCellProps = {
|
||||
complete: (withdrawal: WithdrawalFieldsFragment) => void;
|
||||
};
|
||||
export const CompleteCell = ({ data, complete }: CompleteCellProps) => {
|
||||
const open = useWithdrawalApprovalDialog((state) => state.open);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
return data.pendingOnForeignChain ? (
|
||||
'-'
|
||||
) : (
|
||||
<ButtonLink
|
||||
data-testid="complete-withdrawal"
|
||||
onClick={() => complete(data)}
|
||||
>
|
||||
{t('Complete withdrawal')}
|
||||
</ButtonLink>
|
||||
<div className="flex justify-end gap-1">
|
||||
<ButtonLink
|
||||
data-testid="complete-withdrawal"
|
||||
onClick={() => complete(data)}
|
||||
>
|
||||
{t('Complete withdrawal')}
|
||||
</ButtonLink>
|
||||
|
||||
<DropdownMenu
|
||||
trigger={
|
||||
<DropdownMenuTrigger
|
||||
className="hover:bg-vega-light-200 dark:hover:bg-vega-dark-200 p-0.5 focus:rounded-full hover:rounded-full"
|
||||
data-testid="dropdown-menu"
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.KEBAB} />
|
||||
</DropdownMenuTrigger>
|
||||
}
|
||||
>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem
|
||||
key={'withdrawal-approval'}
|
||||
data-testid="withdrawal-approval"
|
||||
ref={ref}
|
||||
onClick={() => {
|
||||
if (data.id) {
|
||||
open(data.id, ref.current, false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<Icon name="info-sign" size={4} /> {t('View withdrawal details')}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user