Commit d667de4b authored by tom goriunov's avatar tom goriunov Committed by GitHub

Celo: Cross-link to epoch block on the regular block view and Epoch rewards CSV export (#2386)

* Celo: Cross-link to epoch block on the regular block view

Fixes #2365

* [skip ci] fix hover state for gray-blue tag

* CSV export of epoch transactions for address
parent 35133019
import type { Feature } from './types';
import { getEnvValue } from '../utils';
const title = 'Celo chain';
const config: Feature<{ L2UpgradeBlock: number | undefined; BLOCKS_PER_EPOCH: number }> = (() => {
if (getEnvValue('NEXT_PUBLIC_CELO_ENABLED') === 'true') {
return Object.freeze({
title,
isEnabled: true,
L2UpgradeBlock: getEnvValue('NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK') ? Number(getEnvValue('NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK')) : undefined,
BLOCKS_PER_EPOCH: 17_280,
});
}
return Object.freeze({
title,
isEnabled: false,
});
})();
export default config;
...@@ -6,6 +6,7 @@ export { default as adsText } from './adsText'; ...@@ -6,6 +6,7 @@ export { default as adsText } from './adsText';
export { default as beaconChain } from './beaconChain'; export { default as beaconChain } from './beaconChain';
export { default as bridgedTokens } from './bridgedTokens'; export { default as bridgedTokens } from './bridgedTokens';
export { default as blockchainInteraction } from './blockchainInteraction'; export { default as blockchainInteraction } from './blockchainInteraction';
export { default as celo } from './celo';
export { default as csvExport } from './csvExport'; export { default as csvExport } from './csvExport';
export { default as dataAvailability } from './dataAvailability'; export { default as dataAvailability } from './dataAvailability';
export { default as deFiDropdown } from './deFiDropdown'; export { default as deFiDropdown } from './deFiDropdown';
......
...@@ -9,6 +9,9 @@ NEXT_PUBLIC_APP_PORT=3000 ...@@ -9,6 +9,9 @@ NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_CELO_ENABLED=true
NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK=26369280
# Instance ENVs # Instance ENVs
NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=celo-alfajores.blockscout.com NEXT_PUBLIC_API_HOST=celo-alfajores.blockscout.com
......
...@@ -300,6 +300,22 @@ const rollupSchema = yup ...@@ -300,6 +300,22 @@ const rollupSchema = yup
}), }),
}); });
const celoSchema = yup
.object()
.shape({
NEXT_PUBLIC_CELO_ENABLED: yup.boolean(),
NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK: yup
.string()
.when('NEXT_PUBLIC_CELO_ENABLED', {
is: (value: boolean) => value,
then: (schema) => schema.min(0).optional(),
otherwise: (schema) => schema.max(
-1,
'NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK cannot not be used if NEXT_PUBLIC_CELO_ENABLED is not set to "true"',
),
}),
});
const adButlerConfigSchema = yup const adButlerConfigSchema = yup
.object<AdButlerConfig>() .object<AdButlerConfig>()
.transform(replaceQuotes) .transform(replaceQuotes)
...@@ -867,6 +883,7 @@ const schema = yup ...@@ -867,6 +883,7 @@ const schema = yup
.concat(adsBannerSchema) .concat(adsBannerSchema)
.concat(marketplaceSchema) .concat(marketplaceSchema)
.concat(rollupSchema) .concat(rollupSchema)
.concat(celoSchema)
.concat(beaconChainSchema) .concat(beaconChainSchema)
.concat(bridgedTokensSchema) .concat(bridgedTokensSchema)
.concat(sentrySchema); .concat(sentrySchema);
......
NEXT_PUBLIC_CELO_ENABLED=true
NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK=420
\ No newline at end of file
...@@ -703,6 +703,17 @@ For blockchains that implement SUAVE architecture additional fields will be show ...@@ -703,6 +703,17 @@ For blockchains that implement SUAVE architecture additional fields will be show
&nbsp; &nbsp;
### Celo chain
For blockchains that use the Celo platform. _Note_, that once the Celo mainnet becomes an L2 chain, these variables will be migrated to the Rollup configuration section.
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_CELO_ENABLED | `boolean` | Indicates that it is a Celo-based chain. | - | - | `true` | v1.37.0+ |
| NEXT_PUBLIC_CELO_L2_UPGRADE_BLOCK | `number` | Indicates the block number when the Celo-type chain transitioned to L2. This is used to display links to the Epoch block page from a regular block page. | - | - | `26369280` | v1.37.0+ |
&nbsp;
### MetaSuites extension ### MetaSuites extension
Enables [MetaSuites browser extension](https://github.com/blocksecteam/metasuites) to integrate with the app views. Enables [MetaSuites browser extension](https://github.com/blocksecteam/metasuites) to integrate with the app views.
......
...@@ -1069,6 +1069,9 @@ export const RESOURCES = { ...@@ -1069,6 +1069,9 @@ export const RESOURCES = {
csv_export_logs: { csv_export_logs: {
path: '/api/v1/logs-csv', path: '/api/v1/logs-csv',
}, },
csv_export_epoch_rewards: {
path: '/api/v1/celo-election-rewards-csv',
},
graphql: { graphql: {
path: '/api/v1/graphql', path: '/api/v1/graphql',
}, },
......
...@@ -40,5 +40,11 @@ export const epochRewards: AddressEpochRewardsResponse = { ...@@ -40,5 +40,11 @@ export const epochRewards: AddressEpochRewardsResponse = {
token: tokenInfo, token: tokenInfo,
}, },
], ],
next_page_params: null, next_page_params: {
amount: '71952055594478242556',
associated_account_address_hash: '0x30d060f129817c4de5fbc1366d53e19f43c8c64f',
block_number: 25954560,
items_count: 50,
type: 'delegated_payment',
},
}; };
...@@ -21,6 +21,9 @@ const variantSubtle = defineStyle((props) => { ...@@ -21,6 +21,9 @@ const variantSubtle = defineStyle((props) => {
return { return {
bg: mode('gray.100', 'gray.800')(props), bg: mode('gray.100', 'gray.800')(props),
color: mode('blackAlpha.800', 'whiteAlpha.800')(props), color: mode('blackAlpha.800', 'whiteAlpha.800')(props),
_hover: {
opacity: 0.76,
},
}; };
} }
......
...@@ -12,4 +12,8 @@ export type CsvExportParams = { ...@@ -12,4 +12,8 @@ export type CsvExportParams = {
type: 'holders'; type: 'holders';
filterType?: undefined; filterType?: undefined;
filterValue?: undefined; filterValue?: undefined;
} | {
type: 'epoch-rewards';
filterType?: undefined;
filterValue?: undefined;
}; };
...@@ -12,6 +12,7 @@ import DataListDisplay from 'ui/shared/DataListDisplay'; ...@@ -12,6 +12,7 @@ import DataListDisplay from 'ui/shared/DataListDisplay';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages'; import useQueryWithPages from 'ui/shared/pagination/useQueryWithPages';
import AddressCsvExportLink from './AddressCsvExportLink';
import AddressEpochRewardsListItem from './epochRewards/AddressEpochRewardsListItem'; import AddressEpochRewardsListItem from './epochRewards/AddressEpochRewardsListItem';
type Props = { type Props = {
...@@ -71,7 +72,13 @@ const AddressEpochRewards = ({ scrollRef, shouldRender = true, isQueryEnabled = ...@@ -71,7 +72,13 @@ const AddressEpochRewards = ({ scrollRef, shouldRender = true, isQueryEnabled =
const actionBar = rewardsQuery.pagination.isVisible ? ( const actionBar = rewardsQuery.pagination.isVisible ? (
<ActionBar mt={ -6 }> <ActionBar mt={ -6 }>
<Pagination ml="auto" { ...rewardsQuery.pagination }/> <AddressCsvExportLink
address={ hash }
isLoading={ rewardsQuery.pagination.isLoading }
params={{ type: 'epoch-rewards' }}
ml={{ lg: 'auto' }}
/>
<Pagination ml={{ base: 0, lg: 8 }} { ...rewardsQuery.pagination }/>
</ActionBar> </ActionBar>
) : null; ) : null;
......
...@@ -6,6 +6,8 @@ import React from 'react'; ...@@ -6,6 +6,8 @@ import React from 'react';
import type { PaginationParams } from 'ui/shared/pagination/types'; import type { PaginationParams } from 'ui/shared/pagination/types';
import type { RoutedTab } from 'ui/shared/Tabs/types'; import type { RoutedTab } from 'ui/shared/Tabs/types';
import { route } from 'nextjs-routes';
import config from 'configs/app'; import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import throwOnAbsentParamError from 'lib/errors/throwOnAbsentParamError'; import throwOnAbsentParamError from 'lib/errors/throwOnAbsentParamError';
...@@ -24,6 +26,7 @@ import TextAd from 'ui/shared/ad/TextAd'; ...@@ -24,6 +26,7 @@ import TextAd from 'ui/shared/ad/TextAd';
import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning'; import ServiceDegradationWarning from 'ui/shared/alerts/ServiceDegradationWarning';
import Tag from 'ui/shared/chakra/Tag'; import Tag from 'ui/shared/chakra/Tag';
import AddressEntity from 'ui/shared/entities/address/AddressEntity'; import AddressEntity from 'ui/shared/entities/address/AddressEntity';
import LinkInternal from 'ui/shared/links/LinkInternal';
import NetworkExplorers from 'ui/shared/NetworkExplorers'; import NetworkExplorers from 'ui/shared/NetworkExplorers';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
import Pagination from 'ui/shared/pagination/Pagination'; import Pagination from 'ui/shared/pagination/Pagination';
...@@ -152,9 +155,20 @@ const BlockPageContent = () => { ...@@ -152,9 +155,20 @@ const BlockPageContent = () => {
} }
if (!blockQuery.data.celo.is_epoch_block) { if (!blockQuery.data.celo.is_epoch_block) {
const celoConfig = config.features.celo;
const epochBlockNumber = celoConfig.isEnabled && celoConfig.L2UpgradeBlock && blockQuery.data.height <= celoConfig.L2UpgradeBlock ?
blockQuery.data.celo.epoch_number * celoConfig.BLOCKS_PER_EPOCH :
undefined;
const tag = <Tag colorScheme={ epochBlockNumber ? 'gray-blue' : 'gray' }>Epoch #{ blockQuery.data.celo.epoch_number }</Tag>;
const content = epochBlockNumber ? (
<LinkInternal href={ route({ pathname: '/block/[height_or_hash]', query: { height_or_hash: String(epochBlockNumber) } }) }>
{ tag }
</LinkInternal>
) : tag;
return ( return (
<Tooltip label="Displays the epoch this block belongs to before the epoch is finalized" maxW="280px" textAlign="center"> <Tooltip label="Displays the epoch this block belongs to before the epoch is finalized" maxW="280px" textAlign="center">
<Tag>Epoch #{ blockQuery.data.celo.epoch_number }</Tag> { content }
</Tooltip> </Tooltip>
); );
} }
......
...@@ -59,6 +59,11 @@ const EXPORT_TYPES: Record<CsvExportParams['type'], ExportTypeEntity> = { ...@@ -59,6 +59,11 @@ const EXPORT_TYPES: Record<CsvExportParams['type'], ExportTypeEntity> = {
resource: 'csv_export_token_holders', resource: 'csv_export_token_holders',
fileNameTemplate: 'holders', fileNameTemplate: 'holders',
}, },
'epoch-rewards': {
text: 'epoch rewards',
resource: 'csv_export_epoch_rewards',
fileNameTemplate: 'epoch_rewards',
},
}; };
const isCorrectExportType = (type: string): type is CsvExportParams['type'] => Object.keys(EXPORT_TYPES).includes(type); const isCorrectExportType = (type: string): type is CsvExportParams['type'] => Object.keys(EXPORT_TYPES).includes(type);
......
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