Commit 34bc7548 authored by isstuev's avatar isstuev

withdrawals

parent d045fdc5
......@@ -2,7 +2,16 @@ export const data = {
items: [
{
challenge_period_end: null,
from: '0x67aab90c548b284be30b05c376001b4db90b87ba',
from: {
hash: '0x67aab90c548b284be30b05c376001b4db90b87ba',
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
l1_tx_hash: null,
l2_timestamp: '2023-02-15T12:50:02.000000Z',
l2_tx_hash: '0x918cd8c5c24c17e06cd02b0379510c4ad56324bf153578fb9caaaa2fe4e7dc35',
......@@ -13,7 +22,16 @@ export const data = {
},
{
challenge_period_end: null,
from: '0x89e73303049ee32919903c09e8de5629b84f59eb',
from: {
hash: '0x89e73303049ee32919903c09e8de5629b84f59eb',
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
l1_tx_hash: '0xf652ce024516713af6047771cd9a44f43a3edb364dfc0605cf151582ad6704c8',
l2_timestamp: '2023-02-15T08:31:44.000000Z',
l2_tx_hash: '0xc6377fde82ab495b397ee60ab94001df044a9e519662813688d7de8ade9fb3df',
......@@ -24,7 +42,16 @@ export const data = {
},
{
challenge_period_end: null,
from: '0xa490856fcb168b2a555a237dc86c4166199b1115',
from: {
hash: '0xa490856fcb168b2a555a237dc86c4166199b1115',
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
l1_tx_hash: '0x392e521ec9519e37434e215720816443c39bfeee871f1f9969c897c649759ee4',
l2_timestamp: '2023-02-15T05:08:56.000000Z',
l2_tx_hash: '0x65b8db3f2b2cc88d61d18bcf8771101b1ad3bd643d4fd556a97ed05fc545a70c',
......@@ -35,7 +62,16 @@ export const data = {
},
{
challenge_period_end: null,
from: '0xa490856fcb168b2a555a237dc86c4166199b1115',
from: {
hash: '0xa490856fcb168b2a555a237dc86c4166199b1115',
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
l1_tx_hash: '0x41048f9e52f6320a82488acec0f82c35191d8fe683fcc945aa02f3646372d9a1',
l2_timestamp: '2023-02-15T04:59:32.000000Z',
l2_tx_hash: '0xdaf9231cd6fe8ec06bec233e9db0010ccd6e2a2004c9ccb5093f532d47d77854',
......@@ -46,7 +82,16 @@ export const data = {
},
{
challenge_period_end: null,
from: '0xa490856fcb168b2a555a237dc86c4166199b1115',
from: {
hash: '0xa490856fcb168b2a555a237dc86c4166199b1115',
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
l1_tx_hash: null,
l2_timestamp: '2023-02-15T04:33:08.000000Z',
l2_tx_hash: '0x5a72f0361777bebb0b7866f4a030aaa070b0abf625d9b96ea895d9bfa22de73c',
......
......@@ -314,13 +314,14 @@ frontend:
- "/search-results"
- "/token"
- "/tokens"
- "/accounts"
- "/visualize"
- "/api-docs"
- "/csv-export"
- "/verified-contracts"
- "/graphiql"
- "/accounts"
- "/visualize"
- "/api-docs"
- "/output-roots"
- "/withdrawals"
resources:
limits:
memory:
......
......@@ -38,6 +38,7 @@ import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/toke
import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction } from 'types/api/transaction';
import type { TTxsFilters } from 'types/api/txsFilters';
import type { VisualizedContract } from 'types/api/visualization';
import type { WithdrawalsResponse } from 'types/api/withdrawals';
import type ArrayElement from 'types/utils/ArrayElement';
import appConfig from 'configs/app/config';
......@@ -363,6 +364,13 @@ export const RESOURCES = {
path: '/graphql',
},
// L2
withdrawals: {
path: '/api/v2/optimism/withdrawals',
paginationFields: [ 'nonce' as const, 'items_count' as const ],
filterFields: [],
},
output_roots: {
path: '/api/v2/optimism/output-roots',
paginationFields: [ 'index' as const, 'items_count' as const ],
......@@ -428,7 +436,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'token_transfers' | 'token_holders' | 'token_inventory' | 'tokens' |
'token_instance_transfers' |
'verified_contracts' |
'output_roots';
'output_roots' | 'withdrawals';
export type PaginatedResponse<Q extends PaginatedResources> = ResourcePayload<Q>;
......@@ -491,6 +499,7 @@ Q extends 'verified_contracts_counters' ? VerifiedContractsCounters :
Q extends 'visualize_sol2uml' ? VisualizedContract :
Q extends 'contract_verification_config' ? SmartContractVerificationConfig :
Q extends 'output_roots' ? OutputRootsResponse :
Q extends 'withdrawals' ? WithdrawalsResponse :
never;
/* eslint-enable @typescript-eslint/indent */
......
......@@ -38,7 +38,7 @@ dayjs.updateLocale('en', {
relativeTime: {
s: 'a sec',
ss: '%d secs',
future: 'in %s',
future: '%s left',
past: '%s ago',
m: '1 min',
mm: '%d mins',
......
export const data = {
items: [
{
challenge_period_end: null,
from: {
hash: '0x67aab90c548b284be30b05c376001b4db90b87ba',
implementation_name: null,
is_contract: false,
is_verified: false,
name: null,
private_tags: [],
public_tags: [],
watchlist_names: [],
},
l1_tx_hash: null,
l2_timestamp: '2023-02-15T12:50:02.000000Z',
l2_tx_hash: '0x918cd8c5c24c17e06cd02b0379510c4ad56324bf153578fb9caaaa2fe4e7dc35',
msg_nonce: 396,
msg_nonce_raw: '1766847064778384329583297500742918515827483896875618958121606201292620172',
msg_nonce_version: 1,
status: 'Ready to prove',
},
{
challenge_period_end: null,
from: null,
l1_tx_hash: null,
l2_timestamp: null,
l2_tx_hash: '0x2f117bee32ac10cb7efdad98415737484ca66386e491cde9e17d42b136def593',
msg_nonce: 391,
msg_nonce_raw: '1766847064778384329583297500742918515827483896875618958121606201292620167',
msg_nonce_version: 1,
status: 'Ready to prove',
},
{
challenge_period_end: '2022-11-11T12:50:02.000000Z',
from: null,
l1_tx_hash: null,
l2_timestamp: null,
l2_tx_hash: '0xe14b1f46838176702244a5343629bcecf728ca2d9881d47b4ce46e00c387d7e3',
msg_nonce: 390,
msg_nonce_raw: '1766847064778384329583297500742918515827483896875618958121606201292620166',
msg_nonce_version: 1,
status: 'Ready for Relay',
},
],
next_page_params: {
items_count: 50,
nonce: '1766847064778384329583297500742918515827483896875618958121606201292620123',
},
total: 397,
};
import type { AddressParam } from './addressParams';
export type WithdrawalsItem = {
'challenge_period_end': string | null;
'from': string | null;
'from': AddressParam | null;
'l1_tx_hash': string | null;
'l2_timestamp': string | null;
'l2_tx_hash': string;
......
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import { data as withdrawalsData } from 'mocks/withdrawals/withdrawals';
import TestApp from 'playwright/TestApp';
import buildApiUrl from 'playwright/utils/buildApiUrl';
import Withdrawals from './Withdrawals';
const WITHDRAWALS_API_URL = buildApiUrl('withdrawals');
test('base view +@dark-mode', async({ mount, page }) => {
await page.route('https://request-global.czilladx.com/serve/native.php?z=19260bf627546ab7242', (route) => route.fulfill({
status: 200,
body: '',
}));
await page.route(WITHDRAWALS_API_URL, (route) => route.fulfill({
status: 200,
body: JSON.stringify(withdrawalsData),
}));
const component = await mount(
<TestApp>
<Withdrawals/>
</TestApp>,
);
await expect(component.locator('main')).toHaveScreenshot();
});
import { Flex, Hide, Show, Skeleton, Text } from '@chakra-ui/react';
import React from 'react';
import { data as dataMock } from 'data/withdrawals';
import { rightLineArrow } from 'lib/html-entities';
import isBrowser from 'lib/isBrowser';
import useIsMobile from 'lib/hooks/useIsMobile';
import useQueryWithPages from 'lib/hooks/useQueryWithPages';
import { rightLineArrow, nbsp } from 'lib/html-entities';
import ActionBar from 'ui/shared/ActionBar';
import DataFetchAlert from 'ui/shared/DataFetchAlert';
import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/Pagination';
import SkeletonList from 'ui/shared/skeletons/SkeletonList';
import SkeletonTable from 'ui/shared/skeletons/SkeletonTable';
import WithdrawalsListItem from 'ui/withdrawals/WithdrawalsListItem';
import WithdrawalsTable from 'ui/withdrawals/WithdrawalsTable';
const Withdrawals = () => {
// request!!
const [ isLoading, setIsLoading ] = React.useState(true);
React.useEffect(() => {
if (isBrowser()) {
setTimeout(() => setIsLoading(false), 2000);
}
}, []);
const isMobile = useIsMobile();
const data = dataMock;
const isPaginationVisible = false;
const { data, isError, isLoading, isPaginationVisible, pagination } = useQueryWithPages({
resourceName: 'withdrawals',
});
const content = (() => {
if (isError) {
return <DataFetchAlert/>;
}
const text = isLoading ?
<Skeleton w="400px" h="26px" mb={{ base: 6, lg: 0 }}/> :
<Text mb={{ base: 6, lg: 0 }}>A total of { data.total } withdrawals found</Text>;
const bar = (
<>
{ isMobile && text }
<ActionBar mt={ -6 }>
<Flex alignItems="center" justifyContent="space-between" w="100%">
{ !isMobile && text }
{ isPaginationVisible && <Pagination ml="auto" { ...pagination }/> }
</Flex>
</ActionBar>
</>
);
if (isLoading) {
return (
<>
{ bar }
<SkeletonList display={{ base: 'block', lg: 'none' }}/>
{ /* !!! */ }
<SkeletonTable display={{ base: 'none', lg: 'block' }} columns={ [ '130px', '120px', '15%', '45%', '35%' ] }/>
<SkeletonTable display={{ base: 'none', lg: 'block' }} columns={ Array(7).fill(`${ 100 / 7 }%`) }/>
</>
);
}
return (
<>
{ bar }
<Show below="lg" ssr={ false }>{ data.items.map((item => <WithdrawalsListItem key={ item.l2_tx_hash } { ...item }/>)) }</Show>
<Hide below="lg" ssr={ false }><WithdrawalsTable items={ data.items } top={ isPaginationVisible ? 80 : 0 }/></Hide>
</>
......@@ -43,9 +61,7 @@ const Withdrawals = () => {
return (
<Page>
<PageTitle text={ `Withdrawals (L2 ${ rightLineArrow } L1)` } withTextAd/>
{ isLoading ? <Skeleton w="400px" h="26px" mb={ 7 }/> : <Text>A total of { data.total } withdrawals found</Text> }
{ /* Pagination */ }
<PageTitle text={ `Withdrawals (L2${ nbsp }${ rightLineArrow }${ nbsp }L1)` } withTextAd/>
{ content }
</Page>
);
......
import React from "react";
/* eslint-disable @typescript-eslint/naming-convention */
import { Box, Flex, Text, HStack, Icon } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react';
const WithdrawalsListItem = () => {
return null;
}
import type { WithdrawalsItem } from 'types/api/withdrawals';
import appConfig from 'configs/app/config';
import txIcon from 'icons/transactions.svg';
import dayjs from 'lib/date/dayjs';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import HashStringShorten from 'ui/shared/HashStringShorten';
import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
import ListItemMobile from 'ui/shared/ListItemMobile';
type Props = WithdrawalsItem;
const WithdrawalsListItem = ({
msg_nonce,
msg_nonce_version,
l1_tx_hash,
l2_timestamp,
l2_tx_hash,
from,
status,
challenge_period_end,
}: Props) => {
const timeAgo = useTimeAgoIncrement(l2_timestamp, false);
const timeToEnd = dayjs(challenge_period_end).fromNow();
return (
<ListItemMobile rowGap={ 3 }>
<Flex alignItems="center" justifyContent="space-between" w="100%">
<LinkInternal href={ route({ pathname: '/tx/[hash]', query: { hash: l2_tx_hash } }) } display="flex" alignItems="center">
<Icon as={ txIcon } boxSize={ 6 } mr={ 1 }/>
<Box w="calc(100% - 36px)" overflow="hidden" whiteSpace="nowrap"><HashStringShorten hash={ l2_tx_hash }/></Box>
</LinkInternal>
<Text variant="secondary">{ timeAgo }</Text>
</Flex>
<HStack spacing={ 3 } width="100%">
<Text fontSize="sm" fontWeight={ 500 } whiteSpace="nowrap">Msg nonce</Text>
<Text variant="secondary">{ msg_nonce_version + '-' + msg_nonce }</Text>
</HStack>
<HStack spacing={ 3 } width="100%">
<Text fontSize="sm" fontWeight={ 500 } whiteSpace="nowrap">Status</Text>
{ status === 'Ready for Relay' ?
<LinkExternal href={ appConfig.L2.withdrawalUrl }>{ status }</LinkExternal> :
<Text variant="secondary">{ status }</Text>
}
</HStack>
{ from && (
<HStack spacing={ 3 } width="100%">
<Text fontSize="sm" fontWeight={ 500 } whiteSpace="nowrap">From</Text>
<Address>
<AddressIcon address={ from }/>
<AddressLink hash={ from.hash } type="address" truncation="constant" ml={ 2 }/>
</Address>
</HStack>
) }
{ l1_tx_hash && (
<HStack spacing={ 3 } width="100%">
<Text fontSize="sm" fontWeight={ 500 } whiteSpace="nowrap">L1 txn hash</Text>
<LinkExternal href={ appConfig.L2.withdrawalUrl }><HashStringShorten hash={ l1_tx_hash }/></LinkExternal>
</HStack>
) }
{ challenge_period_end && (
<HStack spacing={ 3 } width="100%">
<Text fontSize="sm" fontWeight={ 500 } whiteSpace="nowrap">Time left</Text>
<Text variant="secondary">{ timeToEnd }</Text>
</HStack>
) }
</ListItemMobile>
);
};
export default WithdrawalsListItem;
......@@ -14,7 +14,7 @@ import WithdrawalsTableItem from './WithdrawalsTableItem';
const WithdrawalsTable = ({ items, top }: Props) => {
return (
<Table variant="simple" size="sm" style={{ tableLayout: 'auto' }}>
<Table variant="simple" size="sm" style={{ tableLayout: 'auto' }} minW="950px">
<Thead top={ top }>
<Tr>
<Th>Msg nonce</Th>
......
/* eslint-disable @typescript-eslint/naming-convention */
import { Box, Flex, Td, Tr, Text, Icon } from '@chakra-ui/react';
import { Box, Td, Tr, Text, Icon } from '@chakra-ui/react';
import { route } from 'nextjs-routes';
import React from 'react';
......@@ -7,20 +7,17 @@ import type { WithdrawalsItem } from 'types/api/withdrawals';
import appConfig from 'configs/app/config';
import txIcon from 'icons/transactions.svg';
import txBatchIcon from 'icons/txBatch.svg';
import useTimeAgoIncrement from 'lib/hooks/useTimeAgoIncrement';
import Address from 'ui/shared/address/Address';
import AddressIcon from 'ui/shared/address/AddressIcon';
import AddressLink from 'ui/shared/address/AddressLink';
import CopyToClipboard from 'ui/shared/CopyToClipboard';
import HashStringShorten from 'ui/shared/HashStringShorten';
import HashStringShortenDynamic from 'ui/shared/HashStringShortenDynamic';
import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal';
type Props = WithdrawalsItem;
const OutputRootsTableItem = ({
const WithdrawalsTableItem = ({
msg_nonce,
msg_nonce_version,
l1_tx_hash,
......@@ -30,7 +27,10 @@ const OutputRootsTableItem = ({
status,
challenge_period_end,
}: Props) => {
const timeAgo = l2_timestamp ? useTimeAgoIncrement(l2_timestamp, false) : 'N/A';
const timeAgo = useTimeAgoIncrement(l2_timestamp, false) || 'N/A';
const timeToEnd = useTimeAgoIncrement(challenge_period_end, false) || '-';
// const timeToEnd = challenge_period_end ? dayjs(challenge_period_end).fromNow() : '-';
return (
<Tr>
<Td verticalAlign="middle" fontWeight={ 600 }>
......@@ -39,9 +39,8 @@ const OutputRootsTableItem = ({
<Td verticalAlign="middle">
{ from ? (
<Address>
{ /* address info?? */ }
<AddressIcon address={{ hash: from, is_contract: false, implementation_name: null }}/>
<AddressLink hash={ from } type="address" truncation="constant" ml={ 2 }/>
<AddressIcon address={ from }/>
<AddressLink hash={ from.hash } type="address" truncation="constant" ml={ 2 }/>
</Address>
) : 'N/A' }
</Td>
......@@ -56,22 +55,21 @@ const OutputRootsTableItem = ({
</Td>
<Td verticalAlign="middle">
{ status === 'Ready for Relay' ?
<LinkExternal title={ status } href={ appConfig.l2.withdrawalUrl }/> :
<LinkExternal href={ appConfig.L2.withdrawalUrl }>{ status }</LinkExternal> :
<Text>{ status }</Text>
}
</Td>
<Td verticalAlign="middle">
{ l1_tx_hash ?
//!!!
<LinkExternal title='aaa' href={ appConfig.l2.withdrawalUrl }/> :
<LinkExternal href={ appConfig.L2.withdrawalUrl }><HashStringShorten hash={ l1_tx_hash }/></LinkExternal> :
'N/A'
}
</Td>
<Td verticalAlign="middle">
{ challenge_period_end ? challenge_period_end : '-'}
{ timeToEnd }
</Td>
</Tr>
);
};
export default OutputRootsTableItem;
export default WithdrawalsTableItem;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment