diff --git a/apps/simple-trading-app/setup-tests.ts b/apps/simple-trading-app/setup-tests.ts index dabdc544a..adff1f883 100644 --- a/apps/simple-trading-app/setup-tests.ts +++ b/apps/simple-trading-app/setup-tests.ts @@ -1,4 +1,7 @@ import '@testing-library/jest-dom'; +import { defaultFallbackInView } from 'react-intersection-observer'; + +defaultFallbackInView(true); Object.defineProperty(window, 'matchMedia', { writable: true, diff --git a/apps/simple-trading-app/src/app/app.tsx b/apps/simple-trading-app/src/app/app.tsx index aeb99149f..f106bddbb 100644 --- a/apps/simple-trading-app/src/app/app.tsx +++ b/apps/simple-trading-app/src/app/app.tsx @@ -43,7 +43,7 @@ function App() { -
+
{ - return ( +const tempEmptyText =

Please select a market from the markets page

; + +export const DealTicketContainer = () => { + const { marketId } = useParams<{ marketId: string }>(); + return marketId ? ( {(data) => ( @@ -14,5 +17,7 @@ export const DealTicketContainer = ({ marketId }: DealTicketContainerProps) => { )} + ) : ( + tempEmptyText ); }; diff --git a/apps/simple-trading-app/src/app/components/deal-ticket/deal-ticket-steps.tsx b/apps/simple-trading-app/src/app/components/deal-ticket/deal-ticket-steps.tsx index 1f102cabe..7a00b91dd 100644 --- a/apps/simple-trading-app/src/app/components/deal-ticket/deal-ticket-steps.tsx +++ b/apps/simple-trading-app/src/app/components/deal-ticket/deal-ticket-steps.tsx @@ -70,7 +70,7 @@ export const DealTicketSteps = ({ market }: DealTicketMarketProps) => { { label: 'Select Asset', description: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.`, - component:
, + component:

{market.name}

, }, { label: 'Select Order Type', diff --git a/apps/simple-trading-app/src/app/components/drawer/drawer-container.tsx b/apps/simple-trading-app/src/app/components/drawer/drawer-container.tsx index 537a6361f..62922c51b 100644 --- a/apps/simple-trading-app/src/app/components/drawer/drawer-container.tsx +++ b/apps/simple-trading-app/src/app/components/drawer/drawer-container.tsx @@ -10,5 +10,5 @@ export const DrawerContainer = ({ children }: Props) => ( ); export const DrawerWrapper = ({ children }: Props) => ( -
{children}
+
{children}
); diff --git a/apps/simple-trading-app/src/app/components/simple-market-list/__generated__/CandleLive.ts b/apps/simple-trading-app/src/app/components/simple-market-list/__generated__/CandleLive.ts new file mode 100644 index 000000000..6c390a93d --- /dev/null +++ b/apps/simple-trading-app/src/app/components/simple-market-list/__generated__/CandleLive.ts @@ -0,0 +1,27 @@ +/* tslint:disable */ +/* eslint-disable */ +// @generated +// This file was automatically generated and should not be edited. + +// ==================================================== +// GraphQL subscription operation: CandleLive +// ==================================================== + +export interface CandleLive_candles { + __typename: "Candle"; + /** + * Close price (uint64) + */ + close: string; +} + +export interface CandleLive { + /** + * Subscribe to the candles updates + */ + candles: CandleLive_candles; +} + +export interface CandleLiveVariables { + marketId: string; +} diff --git a/apps/simple-trading-app/src/app/components/simple-market-list/__generated__/SimpleMarkets.ts b/apps/simple-trading-app/src/app/components/simple-market-list/__generated__/SimpleMarkets.ts index b84ffddc6..9e9ce7943 100644 --- a/apps/simple-trading-app/src/app/components/simple-market-list/__generated__/SimpleMarkets.ts +++ b/apps/simple-trading-app/src/app/components/simple-market-list/__generated__/SimpleMarkets.ts @@ -3,7 +3,7 @@ // @generated // This file was automatically generated and should not be edited. -import { Interval, MarketState } from "@vegaprotocol/types"; +import { MarketState } from "@vegaprotocol/types"; // ==================================================== // GraphQL query operation: SimpleMarkets @@ -117,6 +117,5 @@ export interface SimpleMarkets { } export interface SimpleMarketsVariables { - CandleInterval: Interval; CandleSince: string; } diff --git a/apps/simple-trading-app/src/app/components/simple-market-list/data-provider.ts b/apps/simple-trading-app/src/app/components/simple-market-list/data-provider.ts index 04e9f35ce..1560020c9 100644 --- a/apps/simple-trading-app/src/app/components/simple-market-list/data-provider.ts +++ b/apps/simple-trading-app/src/app/components/simple-market-list/data-provider.ts @@ -18,9 +18,9 @@ const MARKET_DATA_FRAGMENT = gql` } `; -const MARKETS_QUERY = gql` +export const MARKETS_QUERY = gql` ${MARKET_DATA_FRAGMENT} - query SimpleMarkets($CandleInterval: Interval!, $CandleSince: String!) { + query SimpleMarkets($CandleSince: String!) { markets { id name @@ -41,7 +41,7 @@ const MARKETS_QUERY = gql` } } } - candles(interval: $CandleInterval, since: $CandleSince) { + candles(interval: I1H, since: $CandleSince) { open close } @@ -58,6 +58,14 @@ const MARKET_DATA_SUB = gql` } `; +export const CANDLE_SUB = gql` + subscription CandleLive($marketId: ID!) { + candles(marketId: $marketId, interval: I1H) { + close + } + } +`; + const update = ( draft: SimpleMarkets_markets[], delta: SimpleMarketDataSub_marketData diff --git a/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.spec.tsx b/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.spec.tsx index 82641266b..be70d03bf 100644 --- a/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.spec.tsx +++ b/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.spec.tsx @@ -1,15 +1,22 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import { useDataProvider } from '@vegaprotocol/react-helpers'; +import { MockedProvider } from '@apollo/client/testing'; +import type { MockedResponse } from '@apollo/client/testing'; import { MarketState } from '@vegaprotocol/types'; import SimpleMarketList from './simple-market-list'; +import { MARKETS_QUERY } from './data-provider'; import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets'; +import type { SimpleMarkets } from './__generated__/SimpleMarkets'; -jest.mock('./data-provider', () => jest.fn()); +const mockedNavigate = jest.fn(); -jest.mock('@vegaprotocol/react-helpers', () => ({ - useDataProvider: jest.fn(), - t: (txt: string) => txt, +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: () => mockedNavigate, +})); + +jest.mock('date-fns', () => ({ + subDays: () => new Date('2022-06-02T11:11:21.721Z'), })); describe('SimpleMarketList', () => { @@ -17,16 +24,31 @@ describe('SimpleMarketList', () => { jest.clearAllMocks(); }); - it('should be properly renderer as empty', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (useDataProvider as unknown as jest.SpyInstance).mockImplementation( - () => ({ data: [], error: false, loading: false }) + it('should be properly renderer as empty', async () => { + const mocks: MockedResponse = { + request: { + query: MARKETS_QUERY, + variables: { + CandleSince: '2022-06-02T11:11:21.721Z', + }, + }, + result: { + data: { markets: [] }, + }, + }; + + render( + + + ); - render(); + + await new Promise((resolve) => setTimeout(resolve, 0)); + expect(screen.getByText('No data to display')).toBeInTheDocument(); }); - it('should be properly rendered with some data', () => { + it('should be properly rendered with some data', async () => { const data = [ { id: '1', @@ -69,11 +91,26 @@ describe('SimpleMarketList', () => { }, }, ] as unknown as SimpleMarkets_markets[]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (useDataProvider as unknown as jest.SpyInstance).mockImplementation( - () => ({ data, error: false, loading: false }) + + const mocks: MockedResponse = { + request: { + query: MARKETS_QUERY, + variables: { + CandleSince: '2022-06-02T11:11:21.721Z', + }, + }, + result: { + data: { markets: data }, + }, + }; + render( + + + ); - render(); + + await new Promise((resolve) => setTimeout(resolve, 0)); + expect(screen.getByRole('list')).toBeInTheDocument(); expect(screen.getAllByRole('listitem')).toHaveLength(2); }); diff --git a/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.tsx b/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.tsx index 74d715352..7ff7006c2 100644 --- a/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.tsx +++ b/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-list.tsx @@ -1,30 +1,55 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { subDays } from 'date-fns'; import { useDataProvider } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers'; import { AsyncRenderer, Lozenge, Splash } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit'; +import type { MarketState } from '@vegaprotocol/types'; import SimpleMarketPercentChange from './simple-market-percent-change'; import SimpleMarketExpires from './simple-market-expires'; import DataProvider from './data-provider'; import { MARKET_STATUS } from './constants'; const SimpleMarketList = () => { + const navigate = useNavigate(); + const statusesRef = useRef>({}); const variables = useMemo( () => ({ - CandleInterval: 'I1H', - CandleSince: new Date(Date.now() - 24 * 60 * 60 * 1000).toJSON(), + CandleSince: subDays(Date.now(), 1).toJSON(), }), [] ); + const update = useCallback( + (delta) => { + if (statusesRef.current[delta.market.id] !== delta.market.state) { + return false; + } + return true; + }, + [statusesRef] + ); + const { data, error, loading } = useDataProvider( DataProvider, - undefined, // @TODO - if we need a live update in the future + update, variables ); - const onClick = useCallback((marketId) => { - // @TODO - let's try to have navigation first - console.log('trigger market', marketId); - }, []); + useEffect(() => { + const statuses: Record = {}; + data?.forEach((market) => { + statuses[market.id] = market.data?.market.state || ''; + }); + statusesRef.current = statuses; + }, [data]); + + const onClick = useCallback( + (marketId) => { + navigate(`/trading/${marketId}`); + }, + [navigate] + ); + return ( {data && data.length > 0 ? ( @@ -47,7 +72,10 @@ const SimpleMarketList = () => {
- +
{ let candles: (SimpleMarkets_markets_candles | null)[] | null; it('empty array', () => { candles = []; - render(); + render( + + + + ); expect(screen.getByText('-')).toBeInTheDocument(); }); it('null', () => { candles = null; - render(); + render( + + + + ); expect(screen.getByText('-')).toBeInTheDocument(); }); it('an appreciated one', () => { @@ -22,7 +31,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => { { close: '100' } as SimpleMarkets_markets_candles, null, ]; - render(); + render( + + + + ); expect(screen.getByText('100.000%')).toBeInTheDocument(); expect(screen.getByText('100.000%')).toHaveStyle( `color: ${theme.colors.vega.green}` @@ -34,7 +47,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => { { close: '50' } as SimpleMarkets_markets_candles, null, ]; - render(); + render( + + + + ); expect(screen.getByText('-50.000%')).toBeInTheDocument(); expect(screen.getByText('-50.000%')).toHaveStyle( `color: ${theme.colors.vega.pink}` diff --git a/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-percent-change.tsx b/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-percent-change.tsx index 58aac8cc7..9d1cd2086 100644 --- a/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-percent-change.tsx +++ b/apps/simple-trading-app/src/app/components/simple-market-list/simple-market-percent-change.tsx @@ -1,22 +1,34 @@ -import React from 'react'; +import React, { useState } from 'react'; +import { InView } from 'react-intersection-observer'; +import { useSubscription } from '@apollo/client'; import { theme } from '@vegaprotocol/tailwindcss-config'; import type { SimpleMarkets_markets_candles } from './__generated__/SimpleMarkets'; +import type { + CandleLive, + CandleLiveVariables, +} from './__generated__/CandleLive'; +import { CANDLE_SUB } from './data-provider'; interface Props { candles: (SimpleMarkets_markets_candles | null)[] | null; + marketId: string; } const getChange = ( - candles: (SimpleMarkets_markets_candles | null)[] | null + candles: (SimpleMarkets_markets_candles | null)[] | null, + lastClose?: string ) => { if (candles) { const first = parseInt(candles.find((item) => item?.open)?.open || '-1'); - const last = candles.reduceRight((aggr, item) => { - if (aggr === -1 && item?.close) { - aggr = parseInt(item.close); - } - return aggr; - }, -1); + const last = + typeof lastClose === 'undefined' + ? candles.reduceRight((aggr, item) => { + if (aggr === -1 && item?.close) { + aggr = parseInt(item.close); + } + return aggr; + }, -1) + : parseInt(lastClose); if (first !== -1 && last !== -1) { return Number(((last - first) / first) * 100).toFixed(3) + '%'; } @@ -34,10 +46,25 @@ const getColor = (change: number | string) => { return theme.colors.intent.highlight; }; -const SimpleMarketPercentChange = ({ candles }: Props) => { - const change = getChange(candles); +const SimpleMarketPercentChangeWrapper = (props: Props) => { + const [inView, setInView] = useState(false); + + return ( + // @ts-ignore falsy wrong type? + + {inView && } + + ); +}; + +const SimpleMarketPercentChange = ({ candles, marketId }: Props) => { + const { data: { candles: { close = undefined } = {} } = {} } = + useSubscription(CANDLE_SUB, { + variables: { marketId }, + }); + const change = getChange(candles, close); const color = getColor(change); return

{change}

; }; -export default SimpleMarketPercentChange; +export default SimpleMarketPercentChangeWrapper; diff --git a/apps/simple-trading-app/src/app/lib/apollo-client.tsx b/apps/simple-trading-app/src/app/lib/apollo-client.tsx index 2b2ed69bd..a687d6cbe 100644 --- a/apps/simple-trading-app/src/app/lib/apollo-client.tsx +++ b/apps/simple-trading-app/src/app/lib/apollo-client.tsx @@ -1,6 +1,15 @@ -import { ApolloClient, from, HttpLink, InMemoryCache } from '@apollo/client'; +import { + ApolloClient, + from, + HttpLink, + InMemoryCache, + split, +} from '@apollo/client'; import { onError } from '@apollo/client/link/error'; import { RetryLink } from '@apollo/client/link/retry'; +import { GraphQLWsLink } from '@apollo/client/link/subscriptions'; +import { createClient as createWSClient } from 'graphql-ws'; +import { getMainDefinition } from '@apollo/client/utilities'; export function createClient(base?: string) { if (!base) { @@ -40,6 +49,24 @@ export function createClient(base?: string) { credentials: 'same-origin', }); + const wsLink = new GraphQLWsLink( + createWSClient({ + url: urlWS.href, + }) + ); + + const splitLink = split( + ({ query }) => { + const definition = getMainDefinition(query); + return ( + definition.kind === 'OperationDefinition' && + definition.operation === 'subscription' + ); + }, + wsLink, + httpLink + ); + const errorLink = onError(({ graphQLErrors, networkError }) => { console.log(graphQLErrors); console.log(networkError); @@ -47,7 +74,7 @@ export function createClient(base?: string) { return new ApolloClient({ connectToDevTools: process.env['NODE_ENV'] === 'development', - link: from([errorLink, retryLink, httpLink]), + link: from([errorLink, retryLink, splitLink]), cache, }); } diff --git a/apps/simple-trading-app/src/app/routes/router-config.tsx b/apps/simple-trading-app/src/app/routes/router-config.tsx index fba2f22f7..29c7b8b20 100644 --- a/apps/simple-trading-app/src/app/routes/router-config.tsx +++ b/apps/simple-trading-app/src/app/routes/router-config.tsx @@ -28,13 +28,13 @@ export const routerConfig = [ path: ROUTES.TRADING, name: 'Trading', text: t('Trading'), - element: ( - - ), + element: , + children: [ + { + path: ':marketId', + element: , + }, + ], }, { path: ROUTES.LIQUIDITY, diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index d96ff0f00..008d40ff9 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "129999.45", + "locked_amount": "129682.36162978697785023", "deposits": [ { "amount": "129999.45", @@ -488,7 +488,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "71359.360754532949904994", + "locked_amount": "70428.517923311130372024", "deposits": [ { "amount": "97499.58", @@ -521,7 +521,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "0", - "locked_amount": "97535.92647176062142134244004", + "locked_amount": "96263.62502479102429967123688", "deposits": [ { "amount": "135173.4239508", @@ -554,7 +554,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "30019.65075918605161088", + "locked_amount": "29628.061254886910252894", "deposits": [ { "amount": "32499.86", @@ -587,7 +587,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "9771.105018319376583514", + "locked_amount": "9643.646434564611031407", "deposits": [ { "amount": "10833.29", @@ -675,7 +675,7 @@ "tranche_end": "2022-11-01T00:00:00.000Z", "total_added": "22500", "total_removed": "0", - "locked_amount": "18545.82059556159525", + "locked_amount": "18056.6590240036215", "deposits": [ { "amount": "15000", @@ -761,7 +761,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "0", - "locked_amount": "1938140.454506446441067284", + "locked_amount": "1916879.612086597542469696", "deposits": [ { "amount": "1852091.69", @@ -1776,8 +1776,8 @@ "tranche_start": "2021-09-05T00:00:00.000Z", "tranche_end": "2022-09-30T00:00:00.000Z", "total_added": "60916.66666633337", - "total_removed": "17568.575895506846757997", - "locked_amount": "18691.0451412597139137896684208311", + "total_removed": "17896.108295511846757997", + "locked_amount": "18066.219044199191697914984957962", "deposits": [ { "amount": "2833.333333", @@ -1876,6 +1876,11 @@ } ], "withdrawals": [ + { + "amount": "327.532400005", + "user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2", + "tx": "0x6002d50af16df494528c32126c3cb569f105ee8118a3f48a34e8883037132198" + }, { "amount": "490.9767850775", "user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05", @@ -2493,6 +2498,12 @@ } ], "withdrawals": [ + { + "amount": "327.532400005", + "user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2", + "tranche_id": 13, + "tx": "0x6002d50af16df494528c32126c3cb569f105ee8118a3f48a34e8883037132198" + }, { "amount": "327.7316817825", "user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2", @@ -2555,8 +2566,8 @@ } ], "total_tokens": "4250", - "withdrawn_tokens": "2653.6331018125", - "remaining_tokens": "1596.3668981875" + "withdrawn_tokens": "2981.1655018175", + "remaining_tokens": "1268.8344981825" }, { "address": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05", @@ -5040,8 +5051,8 @@ "tranche_start": "2021-09-03T00:00:00.000Z", "tranche_end": "2022-09-03T00:00:00.000Z", "total_added": "15073.000000000000000003", - "total_removed": "2897.73152892253", - "locked_amount": "3826.6258515664639083007616186263318113", + "total_removed": "2905.98379934253", + "locked_amount": "3661.43177476534672755072873982115677305", "deposits": [ { "amount": "10", @@ -7685,6 +7696,11 @@ } ], "withdrawals": [ + { + "amount": "8.25227042", + "user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b", + "tx": "0xce562db43fe92ccd76b20c462f4f680336542c064020dfb646512994c4f229d9" + }, { "amount": "7.42538686", "user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b", @@ -12837,6 +12853,12 @@ } ], "withdrawals": [ + { + "amount": "8.25227042", + "user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b", + "tranche_id": 11, + "tx": "0xce562db43fe92ccd76b20c462f4f680336542c064020dfb646512994c4f229d9" + }, { "amount": "7.42538686", "user": "0xc5467213593778E528f0eB8117cc7AFBC5b7491b", @@ -12881,8 +12903,8 @@ } ], "total_tokens": "200", - "withdrawn_tokens": "142.68559107", - "remaining_tokens": "57.31440893" + "withdrawn_tokens": "150.93786149", + "remaining_tokens": "49.06213851" }, { "address": "0x1775cc97E5c05Fde8b571ef75CA52d0A9ff19025", @@ -12907,7 +12929,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "74162.9780761646031", - "locked_amount": "3002748.41446940756547250008", + "locked_amount": "2970077.9019092314063579103", "deposits": [ { "amount": "1998.95815", @@ -13620,7 +13642,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15788853.065470999700000001", "total_removed": "0", - "locked_amount": "15788853.065470999700000001", + "locked_amount": "15750341.6587991785653755639256064283711814", "deposits": [ { "amount": "16249.93", @@ -15571,8 +15593,8 @@ "tranche_start": "2021-11-05T00:00:00.000Z", "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", - "total_removed": "1563001.535509480008878233", - "locked_amount": "9000945.53475187470322777308704175", + "total_removed": "2039274.435730582717034572", + "locked_amount": "8893995.82561804098467636432418579", "deposits": [ { "amount": "129284.449", @@ -15781,6 +15803,11 @@ } ], "withdrawals": [ + { + "amount": "652.48254356494551875", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tx": "0x4a972ce40877c56aaa0fb53f479666723bf6a89ea9df5a18ffeffb2580d416d4" + }, { "amount": "1384.357697656285885", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -15916,6 +15943,26 @@ "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "tx": "0x3f1666dc9c401996a37b4543b7945d14f6d66561cd1d0a84caa30b92fc55408e" }, + { + "amount": "755.451613460173812", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tx": "0x41ce3e549c72e49df5ebe782512c14113e7df9258c9d4c8bccd2dcb654fbb898" + }, + { + "amount": "31474.290919355234375", + "user": "0xDdc5F6484349B242A9B23382Aa21f49B5Dc9a089", + "tx": "0x34be40dc6242c39c9fefcebcdf7f09167297c0a3e099e9b76428758a9953fd48" + }, + { + "amount": "442758.921918885747690339", + "user": "0xa8679b60612Fb2e19d68964326CA02dCe6a08D08", + "tx": "0x232e5795842832a7105c1e8cff4125da02020c1b348758998f06d71fbd674a9d" + }, + { + "amount": "631.75322583660676025", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tx": "0x9d31de2586ae4a6ce3525a15347349febfd085aa6e202b9641621e12e58909e2" + }, { "amount": "58127.81687116447134108", "user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8", @@ -17237,6 +17284,12 @@ } ], "withdrawals": [ + { + "amount": "652.48254356494551875", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tranche_id": 3, + "tx": "0x4a972ce40877c56aaa0fb53f479666723bf6a89ea9df5a18ffeffb2580d416d4" + }, { "amount": "1384.357697656285885", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -17327,6 +17380,18 @@ "tranche_id": 3, "tx": "0x3f1666dc9c401996a37b4543b7945d14f6d66561cd1d0a84caa30b92fc55408e" }, + { + "amount": "755.451613460173812", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tranche_id": 3, + "tx": "0x41ce3e549c72e49df5ebe782512c14113e7df9258c9d4c8bccd2dcb654fbb898" + }, + { + "amount": "631.75322583660676025", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tranche_id": 3, + "tx": "0x9d31de2586ae4a6ce3525a15347349febfd085aa6e202b9641621e12e58909e2" + }, { "amount": "673.86655774325828525", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -18349,8 +18414,8 @@ } ], "total_tokens": "359123.469575", - "withdrawn_tokens": "136988.021403105210273", - "remaining_tokens": "222135.448171894789727" + "withdrawn_tokens": "139027.708785966936364", + "remaining_tokens": "220095.760789033063636" }, { "address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB", @@ -18795,6 +18860,12 @@ } ], "withdrawals": [ + { + "amount": "31474.290919355234375", + "user": "0xDdc5F6484349B242A9B23382Aa21f49B5Dc9a089", + "tranche_id": 3, + "tx": "0x34be40dc6242c39c9fefcebcdf7f09167297c0a3e099e9b76428758a9953fd48" + }, { "amount": "250551.564697895841552119", "user": "0xDdc5F6484349B242A9B23382Aa21f49B5Dc9a089", @@ -18809,8 +18880,8 @@ } ], "total_tokens": "805741.847535494", - "withdrawn_tokens": "278425.719380526241012802", - "remaining_tokens": "527316.128154967758987198" + "withdrawn_tokens": "309900.010299881475387802", + "remaining_tokens": "495841.837235612524612198" }, { "address": "0x47FbE34Fd93416c63d35544dEE6c6f442Db8513d", @@ -19068,10 +19139,17 @@ "tx": "0xff922daf92e19ea138491989d06d76cc4e92011c1cbf2b83e3caebe7179594fb" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "442758.921918885747690339", + "user": "0xa8679b60612Fb2e19d68964326CA02dCe6a08D08", + "tranche_id": 3, + "tx": "0x232e5795842832a7105c1e8cff4125da02020c1b348758998f06d71fbd674a9d" + } + ], "total_tokens": "1151073.5946347", - "withdrawn_tokens": "0", - "remaining_tokens": "1151073.5946347" + "withdrawn_tokens": "442758.921918885747690339", + "remaining_tokens": "708314.672715814252309661" }, { "address": "0x97E5985117F47c8d110Be1c422DdCB9bE9b46e62", @@ -19321,8 +19399,8 @@ "tranche_start": "2021-10-05T00:00:00.000Z", "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", - "total_removed": "1292362.811321297942666742", - "locked_amount": "3239424.61450565009103150955191263", + "total_removed": "1341050.833974497576969742", + "locked_amount": "3197168.13760041683379941673182373", "deposits": [ { "amount": "552496.6455", @@ -19486,6 +19564,11 @@ "user": "0xdbC5d439F373EB646345e1c67D1d46231ACE7dD3", "tx": "0x8e9f65bfea45a61e4f20108bc0ad1ce2bdeba8e355c7b1d4e024a3ffd73065a4" }, + { + "amount": "48688.022653199634303", + "user": "0xa8679b60612Fb2e19d68964326CA02dCe6a08D08", + "tx": "0xd6949910fc00a00de948bf09b7d8d294d0bda6f50c960432679cfd18bd15cd18" + }, { "amount": "7013.412182841867109", "user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75", @@ -19753,10 +19836,17 @@ "tx": "0x9d3432b818054796489848c415af5c523acb16c1540e5865010baf71964c03a7" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "48688.022653199634303", + "user": "0xa8679b60612Fb2e19d68964326CA02dCe6a08D08", + "tranche_id": 4, + "tx": "0xd6949910fc00a00de948bf09b7d8d294d0bda6f50c960432679cfd18bd15cd18" + } + ], "total_tokens": "110499.5291", - "withdrawn_tokens": "0", - "remaining_tokens": "110499.5291" + "withdrawn_tokens": "48688.022653199634303", + "remaining_tokens": "61811.506446800365697" }, { "address": "0x8767d65677CabaD2050b764AEf40610f2f9796F5", @@ -20366,7 +20456,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", "total_removed": "0", - "locked_amount": "472355.6199999996", + "locked_amount": "470625.82124660851890028549873164", "deposits": [ { "amount": "3000", @@ -46019,8 +46109,8 @@ "tranche_start": "2021-12-05T00:00:00.000Z", "tranche_end": "2022-06-05T00:00:00.000Z", "total_added": "171288.42", - "total_removed": "17067.9263679026803", - "locked_amount": "2506.83382533958609789446", + "total_removed": "20783.0438075488377", + "locked_amount": "0", "deposits": [ { "amount": "250", @@ -50244,6 +50334,116 @@ } ], "withdrawals": [ + { + "amount": "183.6335597275", + "user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786", + "tx": "0xba954a4d4b886c981f4965fe7da9c29120a6f1b44c4ce73a94bd7f2eff690434" + }, + { + "amount": "183.6407458275", + "user": "0xb7f112a2Ff7984Ef494E87b2539059d77d500f37", + "tx": "0x66ecf2cabcc78741ac276ace894a5d203475284a7dbaa3df624dd9765b5e34f8" + }, + { + "amount": "183.6396170375", + "user": "0x391484aff3906c940325C63Cf69dC5509710eA6a", + "tx": "0x70e570adefd4935fcf33ef02e007a3f584a55def453b8d3cddba657e21858af5" + }, + { + "amount": "249.9983783575", + "user": "0xf0352681BcDBA8296b716d4EE085C27433Fff690", + "tx": "0x65af2b3830875fac6eddb4ccdbaa62a6a43c9be071af5c4a201742704141e388" + }, + { + "amount": "34.2757777525", + "user": "0x967C162B564c83A30509CB77e572A98e1150a9D3", + "tx": "0x41d9be48e1130b253b786db21c3d6f8ab169114251b3a3866ce6e2d4ef1870e0" + }, + { + "amount": "23.0332214085", + "user": "0x8433BF349d4f71FB37c8CacF5D6D4863D43EaE23", + "tx": "0xf55524a47a3b15876ef7965636d7caff9a0bb11566f359998bf4c79afad22673" + }, + { + "amount": "46.069991352", + "user": "0x2814A08930732406a99139B6856001023239F9Dd", + "tx": "0xa047a1c664e5c65286e3e4470d85497376315623a6be3c7f9289d1484d2209dc" + }, + { + "amount": "120", + "user": "0xA301F00b655a45D454747B4b8fb3dcEFD8C4D66f", + "tx": "0x7f3a81b45debc735d8c383ed438f92627b2f2a3021b92ffc3db4c6d8261f28f2" + }, + { + "amount": "25.40420228", + "user": "0xfeF4b56A370135A17Eef5ba8f5c308451a64Cf86", + "tx": "0x6ecd654e1aeecdcc7073e1ec881b5b0fe4e24df9e9e226470aa35671e5c0be29" + }, + { + "amount": "157.270935085", + "user": "0x2740029049CF5d991202DeEd75965C6F34Aa36B9", + "tx": "0xe613bfdfe6d2dbbff73e7089139458b2d905150649739392e067a652d3226557" + }, + { + "amount": "250", + "user": "0x7b82f39c7AC83442EE6b025D5B69b4A49E6FD168", + "tx": "0xffcba1d72b13cb6fe9c5cf269b956cf55d54367d1dbb2d48ba6c03a2b94e9434" + }, + { + "amount": "250", + "user": "0x02B5Fd5A74367D192b807Da32EaF1adF5E683573", + "tx": "0xc833bf14e1891f661410ddafd4f97f009f63d4ac2ca906d19c74cf2223ff430a" + }, + { + "amount": "250", + "user": "0xBD352479242D160FaEeFB21422786C5B201A1cEc", + "tx": "0x90a9ff32dac087d7f7a7050ee67a9fdf3fe2cc2dacf5f1e900dac53b9741fd68" + }, + { + "amount": "250", + "user": "0x9b427a2EA3536FbFcD37E48c9EC798ea11E2Ec87", + "tx": "0xe5355d8976bf4d26018fea411fb0f01cbc1c43ebda8ad48970df81ae1ef7e8d6" + }, + { + "amount": "250", + "user": "0xe87aE24a4bE0158f25DFC6733c48177C86998B6F", + "tx": "0x98625ddb4f277e915fd5478dc6f97b22ec9793a668bba209c9c82805da44f403" + }, + { + "amount": "250", + "user": "0x5417806fA87Be7F9bA7aEf4FFB9277E8922Fd9BE", + "tx": "0x1c8ed1ce8127a594747ae20827659e12715af4dae9f855a8b641c1b35412b8a3" + }, + { + "amount": "0.0016216425", + "user": "0xf0352681BcDBA8296b716d4EE085C27433Fff690", + "tx": "0xbe02347c087aaed316855eca5a01116fc178087524e3e1885d20c0b119a2af2a" + }, + { + "amount": "250", + "user": "0xD1C87a90455f2765bE275c506738C026B011a6C8", + "tx": "0xf19508bf8446e4c1bf9cf879df87087a3c0ee57a175ecae7a75e4a6442c08270" + }, + { + "amount": "150", + "user": "0x92f15CBCE433FDcA0a87FD778e5BBbD0B3d7eE0f", + "tx": "0x2638a870214b1d904c8e8e80cf35d4bc1b382801b9c16239af41ca2cf06a1654" + }, + { + "amount": "250", + "user": "0xc8aC50a6BA786450676f278Aa716Eb2bA7249B22", + "tx": "0x91d6f0ea8c0c19d00a1c08a53fdf7e839a35fce2c3a80ce993f058c0b4ae9cac" + }, + { + "amount": "29.9837040875", + "user": "0xF047Ee0434548d5E2cA1F080ba6208A22C97d290", + "tx": "0xe2885fb42ec26c0d8964c772f19eff356947add7b0b44f1cb7a3d49c0fdc830b" + }, + { + "amount": "64.1735506975", + "user": "0xe8F33102aDD808E841268E4161326C76A3D31d24", + "tx": "0xaf108d45b68929af7d63e571bf6f4d9d2415437c95470d70d70786398aa192ab" + }, { "amount": "65.56208982", "user": "0x36d345DCEB35816AD557355fA51E7C99FEE7d18E", @@ -50339,6 +50539,16 @@ "user": "0x27049a430Df8b89Ae4f899b0383B0C876F9cAcEb", "tx": "0x2dadd46cff92cdcb974ba556776f69b136a526e05391e07618503526ef3667e0" }, + { + "amount": "17.2834799106574", + "user": "0x34D4Da6a0829607fFAb8C0aFfDf6aBe059d89b86", + "tx": "0x9d5a7c722b96c3a94457ca95d2152c3decdaf3ae89634a7503b0ff1e131d2960" + }, + { + "amount": "246.70865448", + "user": "0x00Ddf1483dEc8E5A38D0fFE78b6C14D0954D255e", + "tx": "0xd1c9e882269eee55b067744c783ed78b875c7f3602bac5e8ed0e7c0b1fe623e6" + }, { "amount": "252.08931115", "user": "0xE245503f8a33eBDDD718915132869C9Ba9df37A8", @@ -51691,10 +51901,17 @@ "tx": "0xee02e72ef8e0755982704385da512cc12c8ff0ea7b58056fa3d5a5932394e9ca" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0xD1C87a90455f2765bE275c506738C026B011a6C8", + "tranche_id": 6, + "tx": "0xf19508bf8446e4c1bf9cf879df87087a3c0ee57a175ecae7a75e4a6442c08270" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x3A7983a6e8AaC25E5DeB6Bf1D0F21c8b8a17D1a6", @@ -54263,6 +54480,12 @@ } ], "withdrawals": [ + { + "amount": "183.6335597275", + "user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786", + "tranche_id": 6, + "tx": "0xba954a4d4b886c981f4965fe7da9c29120a6f1b44c4ce73a94bd7f2eff690434" + }, { "amount": "66.325088395", "user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786", @@ -54271,8 +54494,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "66.325088395", - "remaining_tokens": "183.674911605" + "withdrawn_tokens": "249.9586481225", + "remaining_tokens": "0.0413518775" }, { "address": "0x391484aff3906c940325C63Cf69dC5509710eA6a", @@ -54285,6 +54508,12 @@ } ], "withdrawals": [ + { + "amount": "183.6396170375", + "user": "0x391484aff3906c940325C63Cf69dC5509710eA6a", + "tranche_id": 6, + "tx": "0x70e570adefd4935fcf33ef02e007a3f584a55def453b8d3cddba657e21858af5" + }, { "amount": "66.3216225325", "user": "0x391484aff3906c940325C63Cf69dC5509710eA6a", @@ -54293,8 +54522,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "66.3216225325", - "remaining_tokens": "183.6783774675" + "withdrawn_tokens": "249.96123957", + "remaining_tokens": "0.03876043" }, { "address": "0xb7f112a2Ff7984Ef494E87b2539059d77d500f37", @@ -54307,6 +54536,12 @@ } ], "withdrawals": [ + { + "amount": "183.6407458275", + "user": "0xb7f112a2Ff7984Ef494E87b2539059d77d500f37", + "tranche_id": 6, + "tx": "0x66ecf2cabcc78741ac276ace894a5d203475284a7dbaa3df624dd9765b5e34f8" + }, { "amount": "66.3197624125", "user": "0xb7f112a2Ff7984Ef494E87b2539059d77d500f37", @@ -54315,8 +54550,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "66.3197624125", - "remaining_tokens": "183.6802375875" + "withdrawn_tokens": "249.96050824", + "remaining_tokens": "0.03949176" }, { "address": "0x967C162B564c83A30509CB77e572A98e1150a9D3", @@ -54329,6 +54564,12 @@ } ], "withdrawals": [ + { + "amount": "34.2757777525", + "user": "0x967C162B564c83A30509CB77e572A98e1150a9D3", + "tranche_id": 6, + "tx": "0x41d9be48e1130b253b786db21c3d6f8ab169114251b3a3866ce6e2d4ef1870e0" + }, { "amount": "53.1604376525", "user": "0x967C162B564c83A30509CB77e572A98e1150a9D3", @@ -54367,8 +54608,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "215.7242222475", - "remaining_tokens": "34.2757777525" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0xC9ce3264ac5d3Ff850744043c8F3f87899c1a1D2", @@ -54598,6 +54839,12 @@ } ], "withdrawals": [ + { + "amount": "157.270935085", + "user": "0x2740029049CF5d991202DeEd75965C6F34Aa36B9", + "tranche_id": 6, + "tx": "0xe613bfdfe6d2dbbff73e7089139458b2d905150649739392e067a652d3226557" + }, { "amount": "92.729064915", "user": "0x2740029049CF5d991202DeEd75965C6F34Aa36B9", @@ -54606,8 +54853,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "92.729064915", - "remaining_tokens": "157.270935085" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x573bd9385a35708C24478A385B2Cac38F2a247D3", @@ -55369,6 +55616,12 @@ } ], "withdrawals": [ + { + "amount": "25.40420228", + "user": "0xfeF4b56A370135A17Eef5ba8f5c308451a64Cf86", + "tranche_id": 6, + "tx": "0x6ecd654e1aeecdcc7073e1ec881b5b0fe4e24df9e9e226470aa35671e5c0be29" + }, { "amount": "41.18821861", "user": "0xfeF4b56A370135A17Eef5ba8f5c308451a64Cf86", @@ -55395,8 +55648,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "224.59579772", - "remaining_tokens": "25.40420228" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0xA8D108c6e91f16CEecDAC9d0230946D02D98d931", @@ -56906,10 +57159,17 @@ "tx": "0x057f65938b1360b6d2c4ebf5e789c67897cf60cb6a13f947004def03891afbd8" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0xe87aE24a4bE0158f25DFC6733c48177C86998B6F", + "tranche_id": 6, + "tx": "0x98625ddb4f277e915fd5478dc6f97b22ec9793a668bba209c9c82805da44f403" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x2735b9c217291c3FAa42FDF4bb130b504AAf4f9D", @@ -57768,6 +58028,12 @@ } ], "withdrawals": [ + { + "amount": "17.2834799106574", + "user": "0x34D4Da6a0829607fFAb8C0aFfDf6aBe059d89b86", + "tranche_id": 6, + "tx": "0x9d5a7c722b96c3a94457ca95d2152c3decdaf3ae89634a7503b0ff1e131d2960" + }, { "amount": "21.9796154290108", "user": "0x34D4Da6a0829607fFAb8C0aFfDf6aBe059d89b86", @@ -57776,8 +58042,8 @@ } ], "total_tokens": "39.82", - "withdrawn_tokens": "21.9796154290108", - "remaining_tokens": "17.8403845709892" + "withdrawn_tokens": "39.2630953396682", + "remaining_tokens": "0.5569046603318" }, { "address": "0xd2FeED64D23115162723F1BBd97770190a1f4ffB", @@ -59852,10 +60118,17 @@ "tx": "0x716a7be06da5a7a3d8038907974887a31805ea8eeab5d130cba528e4d2094d9f" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0x7b82f39c7AC83442EE6b025D5B69b4A49E6FD168", + "tranche_id": 6, + "tx": "0xffcba1d72b13cb6fe9c5cf269b956cf55d54367d1dbb2d48ba6c03a2b94e9434" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x645c8D7Bc67b6c1E17d1dCA6621972ce50F3b373", @@ -60167,10 +60440,17 @@ "tx": "0x9f916cf09e8a3c4ade0ffce5190db464d0a2b1dadba78e1ee7ba5d6e751d6148" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0xc8aC50a6BA786450676f278Aa716Eb2bA7249B22", + "tranche_id": 6, + "tx": "0x91d6f0ea8c0c19d00a1c08a53fdf7e839a35fce2c3a80ce993f058c0b4ae9cac" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x92f15CBCE433FDcA0a87FD778e5BBbD0B3d7eE0f", @@ -60182,10 +60462,17 @@ "tx": "0x9f916cf09e8a3c4ade0ffce5190db464d0a2b1dadba78e1ee7ba5d6e751d6148" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "150", + "user": "0x92f15CBCE433FDcA0a87FD778e5BBbD0B3d7eE0f", + "tranche_id": 6, + "tx": "0x2638a870214b1d904c8e8e80cf35d4bc1b382801b9c16239af41ca2cf06a1654" + } + ], "total_tokens": "150", - "withdrawn_tokens": "0", - "remaining_tokens": "150" + "withdrawn_tokens": "150", + "remaining_tokens": "0" }, { "address": "0x0e80E23fa0ae15A06Ad986792F02625f80955E6E", @@ -60309,6 +60596,12 @@ } ], "withdrawals": [ + { + "amount": "46.069991352", + "user": "0x2814A08930732406a99139B6856001023239F9Dd", + "tranche_id": 6, + "tx": "0xa047a1c664e5c65286e3e4470d85497376315623a6be3c7f9289d1484d2209dc" + }, { "amount": "18.472387566", "user": "0x2814A08930732406a99139B6856001023239F9Dd", @@ -60323,8 +60616,8 @@ } ], "total_tokens": "100", - "withdrawn_tokens": "53.930008648", - "remaining_tokens": "46.069991352" + "withdrawn_tokens": "100", + "remaining_tokens": "0" }, { "address": "0xdE6b94C3a927FF6c5b4E4C53556b155C6A7b1d94", @@ -60367,6 +60660,12 @@ } ], "withdrawals": [ + { + "amount": "23.0332214085", + "user": "0x8433BF349d4f71FB37c8CacF5D6D4863D43EaE23", + "tranche_id": 6, + "tx": "0xf55524a47a3b15876ef7965636d7caff9a0bb11566f359998bf4c79afad22673" + }, { "amount": "26.9667785915", "user": "0x8433BF349d4f71FB37c8CacF5D6D4863D43EaE23", @@ -60375,8 +60674,8 @@ } ], "total_tokens": "50", - "withdrawn_tokens": "26.9667785915", - "remaining_tokens": "23.0332214085" + "withdrawn_tokens": "50", + "remaining_tokens": "0" }, { "address": "0xaD8a77d52Dd1E2dDD364AfE960DF49CAe1124C6D", @@ -61809,10 +62108,23 @@ "tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "249.9983783575", + "user": "0xf0352681BcDBA8296b716d4EE085C27433Fff690", + "tranche_id": 6, + "tx": "0x65af2b3830875fac6eddb4ccdbaa62a6a43c9be071af5c4a201742704141e388" + }, + { + "amount": "0.0016216425", + "user": "0xf0352681BcDBA8296b716d4EE085C27433Fff690", + "tranche_id": 6, + "tx": "0xbe02347c087aaed316855eca5a01116fc178087524e3e1885d20c0b119a2af2a" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x7D3e0aC6aB823fC1757B2B0F929c06BE155339Ae", @@ -62086,10 +62398,17 @@ "tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "246.70865448", + "user": "0x00Ddf1483dEc8E5A38D0fFE78b6C14D0954D255e", + "tranche_id": 6, + "tx": "0xd1c9e882269eee55b067744c783ed78b875c7f3602bac5e8ed0e7c0b1fe623e6" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "246.70865448", + "remaining_tokens": "3.29134552" }, { "address": "0xe2A2F60ed248CDE54Fc2e3f5d4c00fDf3138CbBB", @@ -62676,6 +62995,12 @@ } ], "withdrawals": [ + { + "amount": "29.9837040875", + "user": "0xF047Ee0434548d5E2cA1F080ba6208A22C97d290", + "tranche_id": 6, + "tx": "0xe2885fb42ec26c0d8964c772f19eff356947add7b0b44f1cb7a3d49c0fdc830b" + }, { "amount": "220.0162959125", "user": "0xF047Ee0434548d5E2cA1F080ba6208A22C97d290", @@ -62684,8 +63009,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "220.0162959125", - "remaining_tokens": "29.9837040875" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0xd7DDc95Ac6FEe7069e913218545b7D6Fdfea0E05", @@ -62896,10 +63221,17 @@ "tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0x5417806fA87Be7F9bA7aEf4FFB9277E8922Fd9BE", + "tranche_id": 6, + "tx": "0x1c8ed1ce8127a594747ae20827659e12715af4dae9f855a8b641c1b35412b8a3" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x9b427a2EA3536FbFcD37E48c9EC798ea11E2Ec87", @@ -62911,10 +63243,17 @@ "tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0x9b427a2EA3536FbFcD37E48c9EC798ea11E2Ec87", + "tranche_id": 6, + "tx": "0xe5355d8976bf4d26018fea411fb0f01cbc1c43ebda8ad48970df81ae1ef7e8d6" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0xBD352479242D160FaEeFB21422786C5B201A1cEc", @@ -62926,10 +63265,17 @@ "tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0xBD352479242D160FaEeFB21422786C5B201A1cEc", + "tranche_id": 6, + "tx": "0x90a9ff32dac087d7f7a7050ee67a9fdf3fe2cc2dacf5f1e900dac53b9741fd68" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x1D92cb812FdeDF1a5aFE3c5080B2D3Ec102694c6", @@ -62956,10 +63302,17 @@ "tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "250", + "user": "0x02B5Fd5A74367D192b807Da32EaF1adF5E683573", + "tranche_id": 6, + "tx": "0xc833bf14e1891f661410ddafd4f97f009f63d4ac2ca906d19c74cf2223ff430a" + } + ], "total_tokens": "250", - "withdrawn_tokens": "0", - "remaining_tokens": "250" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0xc008A0b1DF32d27A7283c199a7F2a92266029079", @@ -63655,6 +64008,12 @@ } ], "withdrawals": [ + { + "amount": "64.1735506975", + "user": "0xe8F33102aDD808E841268E4161326C76A3D31d24", + "tranche_id": 6, + "tx": "0xaf108d45b68929af7d63e571bf6f4d9d2415437c95470d70d70786398aa192ab" + }, { "amount": "25.4253631225", "user": "0xe8F33102aDD808E841268E4161326C76A3D31d24", @@ -63681,8 +64040,8 @@ } ], "total_tokens": "250", - "withdrawn_tokens": "185.8264493025", - "remaining_tokens": "64.1735506975" + "withdrawn_tokens": "250", + "remaining_tokens": "0" }, { "address": "0x57E427ad38383A4A97D0CC4c3Cc4bb9187ec3A00", @@ -63857,10 +64216,17 @@ "tx": "0xe32a466fc780a0fb3fd84a804f622931ebfaf3f428bff0dc6d141270410e75f8" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "120", + "user": "0xA301F00b655a45D454747B4b8fb3dcEFD8C4D66f", + "tranche_id": 6, + "tx": "0x7f3a81b45debc735d8c383ed438f92627b2f2a3021b92ffc3db4c6d8261f28f2" + } + ], "total_tokens": "120", - "withdrawn_tokens": "0", - "remaining_tokens": "120" + "withdrawn_tokens": "120", + "remaining_tokens": "0" }, { "address": "0x075A6AF774C9Ef7315879231730916ceD13e84f8", diff --git a/apps/static/src/assets/stagnet1-tranches.json b/apps/static/src/assets/stagnet1-tranches.json index 883180732..e1eaf383e 100644 --- a/apps/static/src/assets/stagnet1-tranches.json +++ b/apps/static/src/assets/stagnet1-tranches.json @@ -38,7 +38,7 @@ "tranche_end": "2022-11-26T13:48:10.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "48.558558472856417", + "locked_amount": "47.46261732623034", "deposits": [ { "amount": "100", @@ -242,7 +242,7 @@ "tranche_end": "2022-10-12T00:53:20.000Z", "total_added": "100", "total_removed": "0", - "locked_amount": "36.08237252663623", + "locked_amount": "34.98643138001015", "deposits": [ { "amount": "100", diff --git a/apps/static/src/assets/testnet-tranches.json b/apps/static/src/assets/testnet-tranches.json index 31dede387..4b358bf5d 100644 --- a/apps/static/src/assets/testnet-tranches.json +++ b/apps/static/src/assets/testnet-tranches.json @@ -78,18 +78,51 @@ "tranche_id": 3, "tranche_start": "2021-05-15T09:09:30.000Z", "tranche_end": "2022-05-15T09:09:30.000Z", - "total_added": "10000", - "total_removed": "0", + "total_added": "10000.0000000000000005", + "total_removed": "5e-16", "locked_amount": "0", "deposits": [ + { + "amount": "5e-16", + "user": "0x72c22822A19D20DE7e426fB84aa047399Ddd8853", + "tx": "0xe8f875e4c7e291676d4de38b40ac75cdb7ae23c3cbfc389152a67d25e557a632" + }, { "amount": "10000", "user": "0x1FF32F8A5895DAcdcC0Fe8bC6dDAa7D2c7717f3e", "tx": "0x085994c06447bb8ed4b305a5fa5485e38e34b0347a40ee4a66233c8151fac0be" } ], - "withdrawals": [], + "withdrawals": [ + { + "amount": "5e-16", + "user": "0x72c22822A19D20DE7e426fB84aa047399Ddd8853", + "tx": "0x454b4116649ee74c7fb6223ffe0d475128fdd8a5b9dca14a9ad3def605abd38c" + } + ], "users": [ + { + "address": "0x72c22822A19D20DE7e426fB84aa047399Ddd8853", + "deposits": [ + { + "amount": "5e-16", + "user": "0x72c22822A19D20DE7e426fB84aa047399Ddd8853", + "tranche_id": 3, + "tx": "0xe8f875e4c7e291676d4de38b40ac75cdb7ae23c3cbfc389152a67d25e557a632" + } + ], + "withdrawals": [ + { + "amount": "5e-16", + "user": "0x72c22822A19D20DE7e426fB84aa047399Ddd8853", + "tranche_id": 3, + "tx": "0x454b4116649ee74c7fb6223ffe0d475128fdd8a5b9dca14a9ad3def605abd38c" + } + ], + "total_tokens": "5e-16", + "withdrawn_tokens": "5e-16", + "remaining_tokens": "0" + }, { "address": "0x1FF32F8A5895DAcdcC0Fe8bC6dDAa7D2c7717f3e", "deposits": [ diff --git a/apps/token/src/config/links.ts b/apps/token/src/config/links.ts index 0b1d05f8e..cfd953468 100644 --- a/apps/token/src/config/links.ts +++ b/apps/token/src/config/links.ts @@ -1,7 +1,7 @@ export const Links = { WALLET_RELEASES: 'https://github.com/vegaprotocol/vegawallet-desktop/releases', - WALLET_GUIDE: 'https://docs.vega.xyz/docs/mainnet/tools/vega-wallet', + WALLET_GUIDE: 'https://vega.xyz/wallet', SUSHI_PAIRS: 'https://analytics.sushi.com/pairs/', SUSHI_ONSEN_MENU: 'https://app.sushi.com/farm', SUSHI_ONSEN_WHAT_IS: diff --git a/apps/trading-e2e/src/support/mocks/generate-deal-ticket-query.ts b/apps/trading-e2e/src/support/mocks/generate-deal-ticket-query.ts index 961d17d0f..b7ff0b6c9 100644 --- a/apps/trading-e2e/src/support/mocks/generate-deal-ticket-query.ts +++ b/apps/trading-e2e/src/support/mocks/generate-deal-ticket-query.ts @@ -9,6 +9,7 @@ export const generateDealTicketQuery = ( const defaultResult: DealTicketQuery = { market: { id: 'market-id', + name: 'ETHBTC Quarterly (30 Jun 2022)', decimalPlaces: 2, positionDecimalPlaces: 1, state: MarketState.Active, diff --git a/apps/trading-e2e/src/support/pages/base-page.ts b/apps/trading-e2e/src/support/pages/base-page.ts index bc5861d6f..85aeebc68 100644 --- a/apps/trading-e2e/src/support/pages/base-page.ts +++ b/apps/trading-e2e/src/support/pages/base-page.ts @@ -1,6 +1,6 @@ export default class BasePage { closeDialogBtn = 'dialog-close'; - porfolioUrl = '/portfolio'; + portfolioUrl = '/portfolio'; marketsUrl = '/markets'; assetSelectField = 'select[name="asset"]'; toAddressField = 'input[name="to"]'; @@ -10,21 +10,22 @@ export default class BasePage { dialogText = 'dialog-text'; closeDialog() { - cy.getByTestId(this.closeDialogBtn, { timeout: 8000 }).click({ + cy.getByTestId(this.closeDialogBtn, { timeout: 8000 })?.click({ force: true, }); } navigateToPortfolio() { - cy.get(`a[href='${this.porfolioUrl}']`).should('be.visible').click(); + cy.get(`a[href='${this.portfolioUrl}']`) + .should('be.visible') + .click({ force: true }); cy.url().should('include', '/portfolio'); cy.getByTestId('portfolio'); } navigateToMarkets() { - cy.get(`a[href='${this.marketsUrl}']`).should('be.visible').click(); + cy.getByTestId('markets-link').should('be.visible').click({ force: true }); cy.url().should('include', '/markets'); - cy.getByTestId('markets'); } verifyFormErrorDisplayed(expectedError: string, expectedNumErrors: number) { @@ -35,7 +36,7 @@ export default class BasePage { ); } - updateTransactionform(args?: { + updateTransactionForm(args?: { asset?: string; to?: string; amount?: string; diff --git a/apps/trading-e2e/src/support/pages/markets-page.ts b/apps/trading-e2e/src/support/pages/markets-page.ts index 7d16abd0f..b787da89b 100644 --- a/apps/trading-e2e/src/support/pages/markets-page.ts +++ b/apps/trading-e2e/src/support/pages/markets-page.ts @@ -1,13 +1,14 @@ import BasePage from './base-page'; export default class MarketPage extends BasePage { - marketRowHeaderClassname = '.ag-header-cell-text'; + marketRowHeaderClassname = 'div > span.ag-header-cell-text'; marketRowNameColumn = 'tradableInstrument.instrument.code'; marketRowSymbolColumn = 'tradableInstrument.instrument.product.settlementAsset.symbol'; marketRowPrices = 'flash-cell'; marketRowDescription = 'name'; marketStateColId = 'data'; + openMarketMenu = 'arrow-down'; validateMarketsAreDisplayed() { // We need this to ensure that ag-grid is fully rendered before asserting @@ -27,16 +28,12 @@ export default class MarketPage extends BasePage { 'Description', ]; - cy.get(this.marketRowHeaderClassname) - .each(($marketHeader, index) => { - cy.wrap($marketHeader).should( - 'have.text', - expectedMarketHeaders[index] - ); - }) - .then(($list) => { - cy.wrap($list).should('have.length', expectedMarketHeaders.length); - }); + for (let index = 0; index < expectedMarketHeaders.length; index++) { + cy.get(this.marketRowHeaderClassname).should( + 'contain.text', + expectedMarketHeaders[index] + ); + } cy.get(`[col-id='${this.marketRowNameColumn}']`).each(($marketName) => { cy.wrap($marketName).should('not.be.empty'); @@ -65,4 +62,8 @@ export default class MarketPage extends BasePage { 'portfolio=orders&trade=orderbook' ); } + + clickOpenMarketMenu() { + cy.getByTestId(this.openMarketMenu).click(); + } } diff --git a/apps/trading-e2e/src/support/step_definitions/common.step.ts b/apps/trading-e2e/src/support/step_definitions/common.step.ts index 132a6fa2d..5cf95f90f 100644 --- a/apps/trading-e2e/src/support/step_definitions/common.step.ts +++ b/apps/trading-e2e/src/support/step_definitions/common.step.ts @@ -2,8 +2,10 @@ import { Given } from 'cypress-cucumber-preprocessor/steps'; import { hasOperationName } from '..'; import { generateMarketList } from '../mocks/generate-market-list'; import BasePage from '../pages/base-page'; +import MarketPage from '../pages/markets-page'; const basePage = new BasePage(); +const marketPage = new MarketPage(); Given('I am on the homepage', () => { cy.mockGQL('MarketsList', (req) => { @@ -15,4 +17,5 @@ Given('I am on the homepage', () => { }); cy.visit('/'); basePage.closeDialog(); + marketPage.validateMarketsAreDisplayed(); }); diff --git a/apps/trading-e2e/src/support/step_definitions/deposits.step.ts b/apps/trading-e2e/src/support/step_definitions/deposits.step.ts index dc47c6e47..025264a13 100644 --- a/apps/trading-e2e/src/support/step_definitions/deposits.step.ts +++ b/apps/trading-e2e/src/support/step_definitions/deposits.step.ts @@ -30,7 +30,7 @@ Then('I can see the deposit form', () => { }); When('I submit a deposit with empty fields', () => { - depositsPage.updateTransactionform(); + depositsPage.updateTransactionForm(); depositsPage.submitForm(); }); @@ -39,7 +39,7 @@ Then('I can see empty form validation errors present', () => { }); Then('I enter the following deposit details in deposit form', (table) => { - depositsPage.updateTransactionform({ + depositsPage.updateTransactionForm({ asset: table.rowsHash().asset, to: Cypress.env(table.rowsHash().to), amount: table.rowsHash().amount, @@ -59,7 +59,7 @@ Then('Amount too small message shown', () => { }); And('I enter a valid amount', () => { - depositsPage.updateTransactionform({ amount: '1' }); + depositsPage.updateTransactionForm({ amount: '1' }); }); Then('Not approved message shown', () => { diff --git a/apps/trading-e2e/src/support/step_definitions/markets-page.step.ts b/apps/trading-e2e/src/support/step_definitions/markets-page.step.ts index 73d3952d2..a9722a928 100644 --- a/apps/trading-e2e/src/support/step_definitions/markets-page.step.ts +++ b/apps/trading-e2e/src/support/step_definitions/markets-page.step.ts @@ -19,6 +19,7 @@ const mockMarkets = () => { Then('I navigate to markets page', () => { mockMarkets(); marketsPage.navigateToMarkets(); + marketsPage.clickOpenMarketMenu(); cy.wait('@Markets'); }); diff --git a/apps/trading-e2e/src/support/step_definitions/trading-page.step.ts b/apps/trading-e2e/src/support/step_definitions/trading-page.step.ts index 13116f1a3..c082115ad 100644 --- a/apps/trading-e2e/src/support/step_definitions/trading-page.step.ts +++ b/apps/trading-e2e/src/support/step_definitions/trading-page.step.ts @@ -88,7 +88,7 @@ Given('I am on the trading page for an active market', () => { cy.visit('/markets/market-id'); cy.wait('@Market'); - cy.contains('Market: ACTIVE MARKET'); + cy.contains('ACTIVE MARKET'); }); Given('I am on the trading page for a suspended market', () => { @@ -96,7 +96,7 @@ Given('I am on the trading page for a suspended market', () => { cy.visit('/markets/market-id'); cy.wait('@Market'); - cy.contains('Market: SUSPENDED MARKET'); + cy.contains('SUSPENDED MARKET'); }); When('I click on {string} mocked market', (marketType) => { @@ -115,11 +115,11 @@ Then('trading page for {string} market is displayed', (marketType) => { switch (marketType) { case 'active': cy.wait('@Market'); - cy.contains('Market: ACTIVE MARKET'); + cy.contains('ACTIVE MARKET'); break; case 'suspended': cy.wait('@Market'); - cy.contains('Market: SUSPENDED MARKET'); + cy.contains('SUSPENDED MARKET'); break; } tradingPage.clickOnTradesTab(); diff --git a/apps/trading-e2e/src/support/step_definitions/vega-wallet.step.ts b/apps/trading-e2e/src/support/step_definitions/vega-wallet.step.ts index 9c4026a1c..52590e636 100644 --- a/apps/trading-e2e/src/support/step_definitions/vega-wallet.step.ts +++ b/apps/trading-e2e/src/support/step_definitions/vega-wallet.step.ts @@ -27,6 +27,7 @@ When('I connect to Vega Wallet', () => { Cypress.env('TRADING_TEST_VEGA_WALLET_PASSPHRASE') ); vegaWallet.clickConnectVegaWallet(); + vegaWallet.validateWalletConnected(); }); When('I open wallet dialog', () => { diff --git a/apps/trading-e2e/src/support/step_definitions/withdrawals.step.ts b/apps/trading-e2e/src/support/step_definitions/withdrawals.step.ts index e02f7bb43..169f8fb1a 100644 --- a/apps/trading-e2e/src/support/step_definitions/withdrawals.step.ts +++ b/apps/trading-e2e/src/support/step_definitions/withdrawals.step.ts @@ -1,13 +1,16 @@ import { Given, When, Then } from 'cypress-cucumber-preprocessor/steps'; +import MarketPage from '../pages/markets-page'; import PortfolioPage from '../pages/portfolio-page'; import WithdrawalsPage from '../pages/withdrawals-page'; +const marketPage = new MarketPage(); const portfolioPage = new PortfolioPage(); const withdrawalsPage = new WithdrawalsPage(); Given('I navigate to withdrawal page', () => { cy.visit('/'); portfolioPage.closeDialog(); + marketPage.validateMarketsAreDisplayed(); portfolioPage.navigateToPortfolio(); portfolioPage.navigateToWithdraw(); }); @@ -26,14 +29,14 @@ When('click submit', () => { }); When('I enter an invalid ethereum address', () => { - withdrawalsPage.updateTransactionform({ + withdrawalsPage.updateTransactionForm({ to: '0x0dAAACaa868f87BB4666F918742141cAEAe893Fa', }); withdrawalsPage.clickSubmit(); }); When('I select {string}', (selectedAsset) => { - withdrawalsPage.updateTransactionform({ + withdrawalsPage.updateTransactionForm({ asset: selectedAsset, }); }); @@ -47,7 +50,7 @@ When('I click Use maximum', () => { }); When('I enter the following details in withdrawal form', (table) => { - withdrawalsPage.updateTransactionform({ + withdrawalsPage.updateTransactionForm({ asset: table.rowsHash().asset, to: table.rowsHash().to, amount: table.rowsHash().amount, @@ -56,7 +59,7 @@ When('I enter the following details in withdrawal form', (table) => { }); When('I succesfully fill in and submit withdrawal form', () => { - withdrawalsPage.updateTransactionform({ + withdrawalsPage.updateTransactionForm({ asset: Cypress.env('WITHDRAWAL_ASSET_ID'), amount: '0.1', }); diff --git a/apps/trading-e2e/src/support/trading-windows/deal-ticket.ts b/apps/trading-e2e/src/support/trading-windows/deal-ticket.ts index 8ba4c6c74..0821ad813 100644 --- a/apps/trading-e2e/src/support/trading-windows/deal-ticket.ts +++ b/apps/trading-e2e/src/support/trading-windows/deal-ticket.ts @@ -20,11 +20,11 @@ export default class DealTicket { ); if (isBuy == false) { - cy.getByTestId(this.sellOrder).click(); + cy.getByTestId(this.sellOrder)?.click(); } - cy.getByTestId(this.orderSizeField).clear().type(orderSize); - cy.getByTestId(this.orderTypeDropDown).select(orderType); + cy.getByTestId(this.orderSizeField)?.clear().type(orderSize); + cy.getByTestId(this.orderTypeDropDown)?.select(orderType); } placeLimitOrder( @@ -33,10 +33,10 @@ export default class DealTicket { orderPrice: string, orderType: string ) { - cy.getByTestId(this.limitOrderType).click(); + cy.getByTestId(this.limitOrderType)?.click(); if (isBuy == false) { - cy.getByTestId(this.sellOrder).click(); + cy.getByTestId(this.sellOrder)?.click(); } cy.getByTestId(this.orderSizeField).clear().type(orderSize); diff --git a/apps/trading-e2e/src/support/vega-wallet/index.ts b/apps/trading-e2e/src/support/vega-wallet/index.ts index bdf88966b..ce921871d 100644 --- a/apps/trading-e2e/src/support/vega-wallet/index.ts +++ b/apps/trading-e2e/src/support/vega-wallet/index.ts @@ -50,6 +50,10 @@ export default class VegaWallet { ); } + validateWalletConnected() { + cy.getByTestId(this.connectVegaBtn).should('contain.text', '…'); + } + selectPublicKey() { cy.getByTestId(this.selectPublicKeyBtn).click(); } diff --git a/apps/trading/components/navbar/navbar.tsx b/apps/trading/components/navbar/navbar.tsx index 51df59f8d..236ef8a0a 100644 --- a/apps/trading/components/navbar/navbar.tsx +++ b/apps/trading/components/navbar/navbar.tsx @@ -2,9 +2,38 @@ import { useRouter } from 'next/router'; import { Vega } from '../icons/vega'; import Link from 'next/link'; import { AnchorButton } from '@vegaprotocol/ui-toolkit'; -import { t } from '@vegaprotocol/react-helpers'; +import { LocalStorage, t } from '@vegaprotocol/react-helpers'; +import { useEffect, useState } from 'react'; export const Navbar = () => { + const initNavItemsState = [ + { + name: t('Portfolio'), + path: '/portfolio', + testId: 'portfolio-link', + slug: '', + }, + ]; + const [navItems, setNavItems] = useState(initNavItemsState); + const marketId = LocalStorage.getItem('marketId') ?? ''; + + useEffect(() => { + setNavItems([ + { + name: t('Trading'), + path: '/markets', + testId: 'markets-link', + slug: marketId, + }, + { + name: t('Portfolio'), + path: '/portfolio', + testId: 'portfolio-link', + slug: '', + }, + ]); + }, [marketId]); + return ( @@ -26,20 +52,30 @@ interface NavLinkProps { name: string; path: string; exact?: boolean; + testId?: string; + slug?: string; } -const NavLink = ({ name, path, exact }: NavLinkProps) => { +const NavLink = ({ + name, + path, + exact, + testId = name, + slug = '', +}: NavLinkProps) => { const router = useRouter(); const isActive = router.asPath === path || (!exact && router.asPath.startsWith(path)); + const href = slug !== '' ? `${path}/${slug}` : path; return ( { e.preventDefault(); - router.push(path); + router.push(href); }} > {name} diff --git a/apps/trading/pages/index.page.tsx b/apps/trading/pages/index.page.tsx index ee52b352b..0f36de7b2 100644 --- a/apps/trading/pages/index.page.tsx +++ b/apps/trading/pages/index.page.tsx @@ -1,4 +1,5 @@ import { gql, useQuery } from '@apollo/client'; +import { LocalStorage } from '@vegaprotocol/react-helpers'; import { MarketTradingMode } from '@vegaprotocol/types'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import sortBy from 'lodash/sortBy'; @@ -35,10 +36,13 @@ export function Index() { // should be the oldest market that is currently trading in continuous mode(i.e. not in auction). const { data, error, loading } = useQuery(MARKETS_QUERY); const setLandingDialog = useGlobalStore((state) => state.setLandingDialog); + const lastSelectedMarketId = LocalStorage.getItem('marketId'); useEffect(() => { if (data) { - const marketId = marketList(data)[0]?.id; + const marketId = lastSelectedMarketId + ? lastSelectedMarketId + : marketList(data)[0]?.id; // If a default market is found, go to it with the landing dialog open if (marketId) { @@ -50,7 +54,7 @@ export function Index() { replace('/markets'); } } - }, [data, replace, setLandingDialog]); + }, [data, lastSelectedMarketId, replace, setLandingDialog]); return ( diff --git a/apps/trading/pages/markets/[marketId].page.tsx b/apps/trading/pages/markets/[marketId].page.tsx index 9231e6958..9fbdc0315 100644 --- a/apps/trading/pages/markets/[marketId].page.tsx +++ b/apps/trading/pages/markets/[marketId].page.tsx @@ -1,21 +1,55 @@ import { gql } from '@apollo/client'; -import type { Market, MarketVariables } from './__generated__/Market'; import { Splash } from '@vegaprotocol/ui-toolkit'; import { useRouter } from 'next/router'; import React, { useEffect, useState } from 'react'; import debounce from 'lodash/debounce'; import { PageQueryContainer } from '../../components/page-query-container'; import { TradeGrid, TradePanels } from './trade-grid'; -import { t } from '@vegaprotocol/react-helpers'; +import { LocalStorage, t } from '@vegaprotocol/react-helpers'; import { useGlobalStore } from '../../stores'; import { LandingDialog } from '@vegaprotocol/market-list'; +import type { Market, MarketVariables } from './__generated__/Market'; +import { Interval } from '@vegaprotocol/types'; // Top level page query const MARKET_QUERY = gql` - query Market($marketId: ID!) { + query Market($marketId: ID!, $interval: Interval!, $since: String!) { market(id: $marketId) { id name + tradingMode + state + decimalPlaces + data { + market { + id + } + markPrice + indicativeVolume + bestBidVolume + bestOfferVolume + bestStaticBidVolume + bestStaticOfferVolume + indicativeVolume + } + tradableInstrument { + instrument { + name + code + metadata { + tags + } + } + } + marketTimestamps { + open + close + } + candles(interval: $interval, since: $since) { + open + close + volume + } } } `; @@ -29,6 +63,9 @@ const MarketPage = ({ id }: { id?: string }) => { const marketId = id || (Array.isArray(query.marketId) ? query.marketId[0] : query.marketId); + const yesterday = Math.round(new Date().getTime() / 1000) - 24 * 3600; + const yTimestamp = new Date(yesterday * 1000).toISOString(); + if (!marketId) { return ( @@ -37,12 +74,15 @@ const MarketPage = ({ id }: { id?: string }) => { ); } + LocalStorage.setItem('marketId', marketId); return ( query={MARKET_QUERY} options={{ variables: { marketId, + interval: Interval.I1H, + since: yTimestamp, }, fetchPolicy: 'network-only', }} diff --git a/apps/trading/pages/markets/__generated__/Market.ts b/apps/trading/pages/markets/__generated__/Market.ts index cc812fc8c..683136706 100644 --- a/apps/trading/pages/markets/__generated__/Market.ts +++ b/apps/trading/pages/markets/__generated__/Market.ts @@ -3,10 +3,112 @@ // @generated // This file was automatically generated and should not be edited. +import { Interval, MarketTradingMode, MarketState } from "@vegaprotocol/types"; + // ==================================================== // GraphQL query operation: Market // ==================================================== +export interface Market_market_data_market { + __typename: "Market"; + /** + * Market ID + */ + id: string; +} + +export interface Market_market_data { + __typename: "MarketData"; + /** + * market id of the associated mark price + */ + market: Market_market_data_market; + /** + * the mark price (actually an unsigned int) + */ + markPrice: string; + /** + * indicative volume if the auction ended now, 0 if not in auction mode + */ + indicativeVolume: string; + /** + * the aggregated volume being bid at the best bid price. + */ + bestBidVolume: string; + /** + * the aggregated volume being offered at the best offer price. + */ + bestOfferVolume: string; + /** + * the aggregated volume being offered at the best static bid price, excluding pegged orders + */ + bestStaticBidVolume: string; + /** + * the aggregated volume being offered at the best static offer price, excluding pegged orders. + */ + bestStaticOfferVolume: string; +} + +export interface Market_market_tradableInstrument_instrument_metadata { + __typename: "InstrumentMetadata"; + /** + * An arbitrary list of tags to associated to associate to the Instrument (string list) + */ + tags: string[] | null; +} + +export interface Market_market_tradableInstrument_instrument { + __typename: "Instrument"; + /** + * Full and fairly descriptive name for the instrument + */ + name: string; + /** + * A short non necessarily unique code used to easily describe the instrument (e.g: FX:BTCUSD/DEC18) (string) + */ + code: string; + /** + * Metadata for this instrument + */ + metadata: Market_market_tradableInstrument_instrument_metadata; +} + +export interface Market_market_tradableInstrument { + __typename: "TradableInstrument"; + /** + * An instance of or reference to a fully specified instrument. + */ + instrument: Market_market_tradableInstrument_instrument; +} + +export interface Market_market_marketTimestamps { + __typename: "MarketTimestamps"; + /** + * Time when the market is open and ready to accept trades + */ + open: string | null; + /** + * Time when the market is closed + */ + close: string | null; +} + +export interface Market_market_candles { + __typename: "Candle"; + /** + * Open price (uint64) + */ + open: string; + /** + * Close price (uint64) + */ + close: string; + /** + * Volume price (uint64) + */ + volume: string; +} + export interface Market_market { __typename: "Market"; /** @@ -17,6 +119,47 @@ export interface Market_market { * Market full name */ name: string; + /** + * Current mode of execution of the market + */ + tradingMode: MarketTradingMode; + /** + * Current state of the market + */ + state: MarketState; + /** + * 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; + /** + * marketData for the given market + */ + data: Market_market_data | null; + /** + * An instance of or reference to a tradable instrument. + */ + tradableInstrument: Market_market_tradableInstrument; + /** + * timestamps for state changes in the market + */ + marketTimestamps: Market_market_marketTimestamps; + /** + * Candles on a market, for the 'last' n candles, at 'interval' seconds as specified by params + */ + candles: (Market_market_candles | null)[] | null; } export interface Market { @@ -28,4 +171,6 @@ export interface Market { export interface MarketVariables { marketId: string; + interval: Interval; + since: string; } diff --git a/apps/trading/pages/markets/index.page.tsx b/apps/trading/pages/markets/index.page.tsx index 892cf76e0..67bb8bcbf 100644 --- a/apps/trading/pages/markets/index.page.tsx +++ b/apps/trading/pages/markets/index.page.tsx @@ -1,8 +1,6 @@ import { MarketsContainer } from '@vegaprotocol/market-list'; -const Markets = () => { - return ; -}; +const Markets = () => ; Markets.getInitialProps = () => ({ page: 'markets', diff --git a/apps/trading/pages/markets/trade-grid.tsx b/apps/trading/pages/markets/trade-grid.tsx index 8a80d5afd..7e6eb7fed 100644 --- a/apps/trading/pages/markets/trade-grid.tsx +++ b/apps/trading/pages/markets/trade-grid.tsx @@ -13,6 +13,9 @@ import { t } from '@vegaprotocol/react-helpers'; import { AccountsContainer } from '@vegaprotocol/accounts'; import { DepthChartContainer } from '@vegaprotocol/market-depth'; import { CandlesChartContainer } from '@vegaprotocol/candles-chart'; +import { SelectMarketDialog } from '@vegaprotocol/market-list'; +import { ArrowDown, PriceCellChange } from '@vegaprotocol/ui-toolkit'; +import type { CandleClose } from '@vegaprotocol/types'; const TradingViews = { Candles: CandlesChartContainer, @@ -31,6 +34,58 @@ interface TradeGridProps { market: Market_market; } +export const TradeMarketHeader = ({ market }: TradeGridProps) => { + const [open, setOpen] = useState(false); + const candlesClose: string[] = (market?.candles || []) + .map((candle) => candle?.close) + .filter((c): c is CandleClose => c !== null); + const headerItemClassName = 'whitespace-nowrap flex flex-col'; + const itemClassName = + 'font-sans font-normal mb-0 text-dark/80 dark:text-white/80 text-ui-small'; + const itemValueClassName = + 'capitalize font-sans tracking-tighter text-black dark:text-white text-ui'; + return ( +
+ +
+ + +
+
+ Change (24h) + +
+
+ Volume + + {market.data && market.data.indicativeVolume !== '0' + ? market.data.indicativeVolume + : '-'} + +
+
+ Trading mode + {market.tradingMode} +
+
+ State + {market.state} +
+
+
+
+ ); +}; + export const TradeGrid = ({ market }: TradeGridProps) => { const wrapperClasses = classNames( 'h-full max-h-full', @@ -38,50 +93,49 @@ export const TradeGrid = ({ market }: TradeGridProps) => { 'bg-black-10 dark:bg-white-10', 'text-ui' ); + return ( -
-
-

- {t('Market')}: {market.name} -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ <> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); }; @@ -124,11 +178,7 @@ export const TradePanels = ({ market }: TradePanelsProps) => { return (
-
-

- {t('Market')}: {market.name} -

-
+
{({ width, height }) => ( diff --git a/libs/deal-ticket/src/__generated__/DealTicketQuery.ts b/libs/deal-ticket/src/__generated__/DealTicketQuery.ts index efb10e5c5..5bb0c3d36 100644 --- a/libs/deal-ticket/src/__generated__/DealTicketQuery.ts +++ b/libs/deal-ticket/src/__generated__/DealTicketQuery.ts @@ -55,6 +55,10 @@ export interface DealTicketQuery_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) diff --git a/libs/deal-ticket/src/components/deal-ticket-container.tsx b/libs/deal-ticket/src/components/deal-ticket-container.tsx index 4f3c0f6e1..03d7bfc9a 100644 --- a/libs/deal-ticket/src/components/deal-ticket-container.tsx +++ b/libs/deal-ticket/src/components/deal-ticket-container.tsx @@ -1,5 +1,5 @@ -import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit'; import { gql, useQuery } from '@apollo/client'; +import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit'; import { DealTicketManager } from './deal-ticket-manager'; import type { DealTicketQuery, @@ -11,6 +11,7 @@ const DEAL_TICKET_QUERY = gql` query DealTicketQuery($marketId: ID!) { market(id: $marketId) { id + name decimalPlaces positionDecimalPlaces state diff --git a/libs/market-list/src/lib/components/landing/index.ts b/libs/market-list/src/lib/components/landing/index.ts index 1acd77dfa..8fbbbc4d4 100644 --- a/libs/market-list/src/lib/components/landing/index.ts +++ b/libs/market-list/src/lib/components/landing/index.ts @@ -1,2 +1,3 @@ export * from './landing-dialog'; +export * from './select-market-dialog'; export * from './select-market-list'; diff --git a/libs/market-list/src/lib/components/landing/select-market-dialog.spec.tsx b/libs/market-list/src/lib/components/landing/select-market-dialog.spec.tsx new file mode 100644 index 000000000..4e0ff4181 --- /dev/null +++ b/libs/market-list/src/lib/components/landing/select-market-dialog.spec.tsx @@ -0,0 +1,33 @@ +import { render, screen } from '@testing-library/react'; +import type { ReactNode } from 'react'; +import { SelectMarketDialog } from './select-market-dialog'; +import { MockedProvider } from '@apollo/client/testing'; + +jest.mock( + 'next/link', + () => + ({ children }: { children: ReactNode }) => + children +); + +jest.mock('next/router', () => ({ + useRouter() { + return { + route: '/', + pathname: '', + query: '', + asPath: '', + }; + }, +})); + +describe('SelectMarketDialog', () => { + it('should render select a market dialog', () => { + render( + + jest.fn()} /> + + ); + expect(screen.getByText('Select a market')).toBeTruthy(); + }); +}); 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 new file mode 100644 index 000000000..41e2cb24b --- /dev/null +++ b/libs/market-list/src/lib/components/landing/select-market-dialog.tsx @@ -0,0 +1,28 @@ +import { Dialog, Intent } from '@vegaprotocol/ui-toolkit'; +import { t } from '@vegaprotocol/react-helpers'; +import { MarketsContainer } from '../markets-container'; + +export interface SelectMarketListProps { + dialogOpen: boolean; + setDialogOpen: (open: boolean) => void; +} + +export const SelectMarketDialog = ({ + dialogOpen, + setDialogOpen, +}: SelectMarketListProps) => { + return ( + setDialogOpen(false)} + titleClassNames="font-bold font-sans text-3xl tracking-tight mb-0 pl-8" + contentClassNames="w-full md:w-[1120px]" + > +
+ +
+
+ ); +}; diff --git a/libs/market-list/src/lib/components/landing/select-market-list.tsx b/libs/market-list/src/lib/components/landing/select-market-list.tsx index 61b74060b..0f62f6b72 100644 --- a/libs/market-list/src/lib/components/landing/select-market-list.tsx +++ b/libs/market-list/src/lib/components/landing/select-market-list.tsx @@ -3,19 +3,21 @@ import { PriceCell, t, } from '@vegaprotocol/react-helpers'; +import type { CandleClose } from '@vegaprotocol/types'; import { PriceCellChange, Sparkline } from '@vegaprotocol/ui-toolkit'; import Link from 'next/link'; import { mapDataToMarketList } from '../../utils'; import type { MarketList } from '../markets-container/__generated__/MarketList'; -export interface SelectMarketListProps { +export interface SelectMarketListDataProps { data: MarketList | undefined; onSelect: (id: string) => void; } -type CandleClose = Required; - -export const SelectMarketList = ({ data, onSelect }: SelectMarketListProps) => { +export const SelectMarketList = ({ + data, + onSelect, +}: SelectMarketListDataProps) => { const thClassNames = (direction: 'left' | 'right') => `px-8 text-${direction} font-sans font-normal text-ui-small leading-9 mb-0 text-dark/80 dark:text-white/80`; const tdClassNames = diff --git a/libs/market-list/src/lib/components/markets-container/markets-container.tsx b/libs/market-list/src/lib/components/markets-container/markets-container.tsx index 59064ade3..a80afa2d7 100644 --- a/libs/market-list/src/lib/components/markets-container/markets-container.tsx +++ b/libs/market-list/src/lib/components/markets-container/markets-container.tsx @@ -13,7 +13,7 @@ import type { import { marketsDataProvider } from './markets-data-provider'; export const MarketsContainer = () => { - const { pathname, push } = useRouter(); + const { push } = useRouter(); const gridRef = useRef(null); const update = useCallback( (delta: Markets_markets_data) => { @@ -57,7 +57,7 @@ export const MarketsContainer = () => { ref={gridRef} data={data} onRowClicked={(id) => - push(`${pathname}/${id}?portfolio=orders&trade=orderbook`) + push(`/markets/${id}?portfolio=orders&trade=orderbook`) } /> diff --git a/libs/positions/src/lib/positions-table.spec.tsx b/libs/positions/src/lib/positions-table.spec.tsx index 70b84e231..7f5326399 100644 --- a/libs/positions/src/lib/positions-table.spec.tsx +++ b/libs/positions/src/lib/positions-table.spec.tsx @@ -4,7 +4,7 @@ import type { Positions_party_positions } from './__generated__/Positions'; import { MarketTradingMode } from '@vegaprotocol/types'; const singleRow: Positions_party_positions = { - realisedPNL: '5', + realisedPNL: '520000000', openVolume: '100', unrealisedPNL: '895000', averageEntryPrice: '1129935', @@ -93,7 +93,7 @@ it('Correct formatting applied', async () => { '+100', '11.29935', '11.38885', - '+5', + '+5,200.000', ]; cells.forEach((cell, i) => { expect(cell).toHaveTextContent(expectedValues[i]); diff --git a/libs/positions/src/lib/positions-table.tsx b/libs/positions/src/lib/positions-table.tsx index c5a420920..e70c01e1e 100644 --- a/libs/positions/src/lib/positions-table.tsx +++ b/libs/positions/src/lib/positions-table.tsx @@ -131,8 +131,10 @@ export const PositionsTable = forwardRef( 'color-vega-red': ({ value }: { value: string }) => Number(value) < 0, }} - valueFormatter={({ value }: ValueFormatterParams) => - volumePrefix(value) + valueFormatter={({ value, data }: ValueFormatterParams) => + volumePrefix( + addDecimalsFormatNumber(value, data.market.decimalPlaces, 3) + ) } cellRenderer="PriceFlashCell" /> diff --git a/libs/react-helpers/src/lib/generic-data-provider.ts b/libs/react-helpers/src/lib/generic-data-provider.ts index f211c24fe..70d44c8eb 100644 --- a/libs/react-helpers/src/lib/generic-data-provider.ts +++ b/libs/react-helpers/src/lib/generic-data-provider.ts @@ -46,8 +46,8 @@ interface GetDelta { } /** - * @param subscriptionQuery query that will beused for subscription - * @param update function that will be execued on each onNext, it should update data base on delta, it can restart data provider + * @param subscriptionQuery query that will be used for subscription + * @param update function that will be executed on each onNext, it should update data base on delta, it can restart data provider * @param getData transforms received query data to format that will be stored in data provider * @param getDelta transforms delta data to format that will be stored in data provider * @param fetchPolicy @@ -63,7 +63,7 @@ function makeDataProviderInternal( ): Subscribe { // list of callbacks passed through subscribe call const callbacks: UpdateCallback[] = []; - // subscription is started before inital query, all deltas that will arrive before inital query response are put on queue + // subscription is started before initial query, all deltas that will arrive before initial query response are put on queue const updateQueue: Delta[] = []; let variables: OperationVariables | undefined = undefined; @@ -88,7 +88,7 @@ function makeDataProviderInternal( callbacks.forEach((callback) => notify(callback, delta)); }; - const initalFetch = async () => { + const initialFetch = async () => { if (!client) { return; } @@ -99,7 +99,7 @@ function makeDataProviderInternal( fetchPolicy, }); data = getData(res.data); - // if there was some updates received from subscription during initial query loading apply them on just reveived data + // if there was some updates received from subscription during initial query loading apply them on just received data if (data && updateQueue && updateQueue.length > 0) { data = produce(data, (draft) => { while (updateQueue.length) { @@ -135,7 +135,7 @@ function makeDataProviderInternal( } else { loading = true; error = undefined; - initalFetch(); + initialFetch(); } }; @@ -176,7 +176,7 @@ function makeDataProviderInternal( }, () => restart() ); - await initalFetch(); + await initialFetch(); }; const reset = () => { @@ -242,8 +242,8 @@ const memoize = ( /** * @param query Query - * @param subscriptionQuery Query query that will beused for subscription - * @param update Update function that will be execued on each onNext, it should update data base on delta, it can restart data provider + * @param subscriptionQuery Query query that will be used for subscription + * @param update Update function that will be executed on each onNext, it should update data base on delta, it can restart data provider * @param getData transforms received query data to format that will be stored in data provider * @param getDelta transforms delta data to format that will be stored in data provider * @param fetchPolicy diff --git a/libs/react-helpers/src/lib/grid/price-cell.tsx b/libs/react-helpers/src/lib/grid/price-cell.tsx index f645fd88b..e8200de9c 100644 --- a/libs/react-helpers/src/lib/grid/price-cell.tsx +++ b/libs/react-helpers/src/lib/grid/price-cell.tsx @@ -13,7 +13,7 @@ export const PriceCell = React.memo( return -; } return ( - + {valueFormatted} ); diff --git a/libs/types/src/candle.ts b/libs/types/src/candle.ts new file mode 100644 index 000000000..d4d1291d9 --- /dev/null +++ b/libs/types/src/candle.ts @@ -0,0 +1 @@ +export type CandleClose = Required; diff --git a/libs/types/src/index.ts b/libs/types/src/index.ts index f2426ad97..b4f74515a 100644 --- a/libs/types/src/index.ts +++ b/libs/types/src/index.ts @@ -1 +1,2 @@ export * from './__generated__/globalTypes'; +export * from './candle'; diff --git a/libs/ui-toolkit/src/components/arrows/arrow.tsx b/libs/ui-toolkit/src/components/arrows/arrow.tsx index 744ec7ef2..550c00302 100644 --- a/libs/ui-toolkit/src/components/arrows/arrow.tsx +++ b/libs/ui-toolkit/src/components/arrows/arrow.tsx @@ -1,13 +1,38 @@ -export const ArrowUp = () => ( +export interface ArrowStyleProps { + color?: string; + borderX?: number; + borderTop?: number; + borderBottom?: number; +} + +export const ArrowUp = ({ + color = 'green', + borderX = 4, + borderBottom = 4, +}: ArrowStyleProps) => ( ); -export const ArrowDown = () => ( +export const ArrowDown = ({ + color = 'red', + borderX = 4, + borderTop = 4, +}: ArrowStyleProps) => ( ); diff --git a/libs/ui-toolkit/src/components/button/button.tsx b/libs/ui-toolkit/src/components/button/button.tsx index 53824534e..361157a7a 100644 --- a/libs/ui-toolkit/src/components/button/button.tsx +++ b/libs/ui-toolkit/src/components/button/button.tsx @@ -215,12 +215,12 @@ export const AnchorButton = forwardRef( className, prependIconName, appendIconName, - ...prosp + ...props }, ref ) => { return ( - + {getContent(children, prependIconName, appendIconName)} ); diff --git a/libs/ui-toolkit/src/components/dialog/dialog.tsx b/libs/ui-toolkit/src/components/dialog/dialog.tsx index f838f9136..9433d0de7 100644 --- a/libs/ui-toolkit/src/components/dialog/dialog.tsx +++ b/libs/ui-toolkit/src/components/dialog/dialog.tsx @@ -12,6 +12,7 @@ interface DialogProps { title?: string; intent?: Intent; titleClassNames?: string; + contentClassNames?: string; } export function Dialog({ @@ -21,13 +22,15 @@ export function Dialog({ title, intent, titleClassNames, + contentClassNames, }: DialogProps) { const contentClasses = classNames( // Positions the modal in the center of screen 'z-20 fixed w-full md:w-[520px] px-28 py-24 top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]', // Need to apply background and text colors again as content is rendered in a portal 'dark:bg-black dark:text-white-95 bg-white text-black-95', - getIntentShadow(intent) + getIntentShadow(intent), + contentClassNames ); return ( onChange(x)}> diff --git a/package.json b/package.json index 6357ac198..8791933f7 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "react-dom": "17.0.2", "react-hook-form": "^7.27.0", "react-i18next": "^11.11.4", + "react-intersection-observer": "^9.2.2", "react-router-dom": "^6.3.0", "react-syntax-highlighter": "^15.4.5", "react-use-websocket": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 51089bb63..c2da3727e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18049,6 +18049,11 @@ react-inspector@^5.1.0: is-dom "^1.0.0" prop-types "^15.0.0" +react-intersection-observer@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-9.2.2.tgz#4f72487e2e9c62b6a6f55f38c5b0b6b7dc6c196d" + integrity sha512-xrFOWo5DbDzayAuDn6WpLrXfw73mvfx4WjuAjjy7dY7jWeTTle5+18nGtvMlR7q0npJanmmh6/CaSaSg4JxTJQ== + react-is@17.0.2, react-is@^17.0.1, react-is@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"