Commit c77b692a authored by Max Alekseenko's avatar Max Alekseenko

rework defi dropdown env value

parent 1a268894
import type { Feature } from './types';
import { getEnvValue } from '../utils';
import marketplace from './marketplace';
const swapButtonUrl = getEnvValue('NEXT_PUBLIC_SWAP_BUTTON_URL');
const paymentLinkUrl = getEnvValue('NEXT_PUBLIC_PAYMENT_LINK_URL');
const title = 'Blockscout DeFi';
function isValidUrl(string: string) {
try {
new URL(string);
return true;
} catch (error) {
return false;
}
}
function createFeatureDetails(value: string | undefined) {
if (value) {
if (isValidUrl(value)) {
return { url: value };
} else if (marketplace.isEnabled) {
return { dappId: value };
}
}
return undefined;
}
const swapButton = createFeatureDetails(swapButtonUrl);
const paymentLink = createFeatureDetails(paymentLinkUrl);
const isEnabled = Boolean(swapButton) || Boolean(paymentLink);
const config: Feature<{
swapButton?: { dappId: string } | { url: string };
paymentLink?: { dappId: string } | { url: string };
}> = isEnabled ?
Object.freeze({
title,
isEnabled: true,
swapButton,
paymentLink,
}) :
Object.freeze({
title,
isEnabled: false,
});
export default config;
import type { Feature } from './types';
import type { DeFiDropdownItem } from 'types/client/deFiDropdown';
import { getEnvValue, parseEnvJson } from '../utils';
const items = parseEnvJson<Array<DeFiDropdownItem>>(getEnvValue('NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS')) || [];
const title = 'Blockscout DeFi dropdown';
const config: Feature<{ items: Array<DeFiDropdownItem> }> = items.length > 0 ?
Object.freeze({
title,
isEnabled: true,
items,
}) :
Object.freeze({
title,
isEnabled: false,
});
export default config;
......@@ -23,7 +23,7 @@ export { default as sentry } from './sentry';
export { default as sol2uml } from './sol2uml';
export { default as stats } from './stats';
export { default as suave } from './suave';
export { default as deFi } from './deFi';
export { default as deFiDropdown } from './deFiDropdown';
export { default as txInterpretation } from './txInterpretation';
export { default as userOps } from './userOps';
export { default as validators } from './validators';
......
......@@ -56,7 +56,6 @@ NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={'id':'728301','width':'728','height':'90
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={'id':'728301','width':'320','height':'100'}
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens.services.blockscout.com
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata.services.blockscout.com
NEXT_PUBLIC_SWAP_BUTTON_URL=aerodrome
NEXT_PUBLIC_MARKETPLACE_ENABLED=true
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
......
......@@ -53,8 +53,7 @@ NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blocksc
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_SWAP_BUTTON_URL=uniswap
NEXT_PUBLIC_PAYMENT_LINK_URL=peanut-protocol
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'uniswap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'}]
#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/eth.jpg?raw=true
......
......@@ -57,4 +57,3 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-goerli.k8s-dev.blockscout.com
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.k8s-dev.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info-test.k8s-dev.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs-test.k8s-dev.blockscout.com
NEXT_PUBLIC_SWAP_BUTTON_URL=uniswap
......@@ -57,10 +57,9 @@ NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blocksc
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-security-reports/default.json
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_SWAP_BUTTON_URL=uniswap
NEXT_PUBLIC_HAS_USER_OPS=true
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
# rollup
NEXT_PUBLIC_ROLLUP_TYPE=optimistic
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com/
\ No newline at end of file
NEXT_PUBLIC_ROLLUP_L1_BASE_URL=https://eth.blockscout.com/
......@@ -53,7 +53,6 @@ NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs.services.blockscout.com
NEXT_PUBLIC_MARKETPLACE_ENABLED=false
NEXT_PUBLIC_SWAP_BUTTON_URL=uniswap
NEXT_PUBLIC_HAS_USER_OPS=true
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
......
......@@ -12,6 +12,7 @@ import type { AdButlerConfig } from '../../../types/client/adButlerConfig';
import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS, SUPPORTED_AD_BANNER_ADDITIONAL_PROVIDERS } from '../../../types/client/adProviders';
import type { AdTextProviders, AdBannerProviders, AdBannerAdditionalProviders } from '../../../types/client/adProviders';
import type { ContractCodeIde } from '../../../types/client/contract';
import type { DeFiDropdownItem } from '../../../types/client/deFiDropdown';
import { GAS_UNITS } from '../../../types/client/gasTracker';
import type { GasUnit } from '../../../types/client/gasTracker';
import type { MarketplaceAppOverview, MarketplaceAppSecurityReportRaw, MarketplaceAppSecurityReport } from '../../../types/client/marketplace';
......@@ -39,6 +40,7 @@ import { TX_ADDITIONAL_FIELDS_IDS, TX_FIELDS_IDS } from '../../../types/views/tx
import { replaceQuotes } from '../../../configs/app/utils';
import * as regexp from '../../../lib/regexp';
import type { IconName } from '../../../ui/shared/IconSvg';
const protocols = [ 'http', 'https' ];
......@@ -464,6 +466,14 @@ const bridgedTokensSchema = yup
}),
});
const deFiDropdownItemSchema: yup.ObjectSchema<DeFiDropdownItem> = yup
.object({
text: yup.string().required(),
icon: yup.string<IconName>().required(),
dappId: yup.string(),
url: yup.string().test(urlTest),
});
const schema = yup
.object()
.noUnknown(true, (params) => {
......@@ -619,12 +629,15 @@ const schema = yup
NEXT_PUBLIC_IS_SUAVE_CHAIN: yup.boolean(),
NEXT_PUBLIC_HAS_USER_OPS: yup.boolean(),
NEXT_PUBLIC_METASUITES_ENABLED: yup.boolean(),
NEXT_PUBLIC_SWAP_BUTTON_URL: yup.string(),
NEXT_PUBLIC_PAYMENT_LINK_URL: yup.string(),
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE: yup.string<ValidatorsChainType>().oneOf(VALIDATORS_CHAIN_TYPE),
NEXT_PUBLIC_GAS_TRACKER_ENABLED: yup.boolean(),
NEXT_PUBLIC_GAS_TRACKER_UNITS: yup.array().transform(replaceQuotes).json().of(yup.string<GasUnit>().oneOf(GAS_UNITS)),
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: yup.boolean(),
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS: yup
.array()
.transform(replaceQuotes)
.json()
.of(deFiDropdownItemSchema),
// 6. External services envs
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(),
......
......@@ -72,6 +72,5 @@ NEXT_PUBLIC_VISUALIZE_API_HOST=https://example.com
NEXT_PUBLIC_VISUALIZE_API_BASE_PATH=https://example.com
NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=false
NEXT_PUBLIC_WEB3_WALLETS=['coinbase','metamask','token_pocket']
NEXT_PUBLIC_SWAP_BUTTON_URL=uniswap
NEXT_PUBLIC_PAYMENT_LINK_URL=peanut-protocol
NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=stability
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'uniswap'},{'text':'Payment link','icon':'payment_link','url':'https://example.com'}]
......@@ -197,7 +197,6 @@ frontend:
NEXT_PUBLIC_ROLLUP_L1_BASE_URL: https://eth-goerli.blockscout.com/
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
NEXT_PUBLIC_SWAP_BUTTON_URL: sushiswap
envFromSecret:
NEXT_PUBLIC_AUTH0_CLIENT_ID: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_AUTH0_CLIENT_ID
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID
......
......@@ -165,7 +165,6 @@ frontend:
NEXT_PUBLIC_WEB3_WALLETS: "['token_pocket','coinbase','metamask']"
#NEXT_PUBLIC_VIEWS_NFT_MARKETPLACES: "[{'name':'LooksRare','collection_url':'https://goerli.looksrare.org/collections/{hash}','instance_url':'https://goerli.looksrare.org/collections/{hash}/{id}','logo_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/nft-marketplace-logos/looks-rare.png'}]"
NEXT_PUBLIC_CONTRACT_CODE_IDES: "[{'title':'Remix IDE','url':'https://remix.blockscout.com/?address={hash}&blockscout=eth-sepolia.blockscout.com','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]"
NEXT_PUBLIC_SWAP_BUTTON_URL: uniswap
envFromSecret:
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/front-main?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
......
......@@ -74,7 +74,6 @@ frontend:
NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL=https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
NEXT_PUBLIC_USE_NEXT_JS_PROXY: true
NEXT_PUBLIC_SWAP_BUTTON_URL: sushiswap
envFromSecret:
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review-l2?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
......
......@@ -86,7 +86,6 @@ frontend:
NEXT_PUBLIC_HAS_USER_OPS: true
NEXT_PUBLIC_CONTRACT_CODE_IDES: "[{'title':'Remix IDE','url':'https://remix.blockscout.com/?address={hash}&blockscout=eth-goerli.blockscout.com','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]"
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER: blockscout
NEXT_PUBLIC_SWAP_BUTTON_URL: uniswap
NEXT_PUBLIC_HAS_CONTRACT_AUDIT_REPORTS: true
NEXT_PUBLIC_AD_BANNER_PROVIDER: getit
NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER: adbutler
......
......@@ -7,4 +7,5 @@
| NEXT_PUBLIC_OPTIMISTIC_L2_WITHDRAWAL_URL | `string` | URL for optimistic L2 -> L1 withdrawals | Required | - | `https://app.optimism.io/bridge/withdraw` | v1.24.0 | Renamed to NEXT_PUBLIC_ROLLUP_L2_WITHDRAWAL_URL |
| NEXT_PUBLIC_L1_BASE_URL | `string` | Blockscout base URL for L1 network | Required | - | `'http://eth-goerli.blockscout.com'` | v1.24.0 | Renamed to NEXT_PUBLIC_ROLLUP_L1_BASE_URL |
| NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER | `boolean` | Set to false if network doesn't have gas tracker | - | `true` | `false` | v1.25.0 | Replaced by NEXT_PUBLIC_GAS_TRACKER_ENABLED |
| NEXT_PUBLIC_NETWORK_GOVERNANCE_TOKEN_SYMBOL | `string` | Network governance token symbol | - | - | `GNO` | v1.29.0 | Replaced by NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL |
\ No newline at end of file
| NEXT_PUBLIC_NETWORK_GOVERNANCE_TOKEN_SYMBOL | `string` | Network governance token symbol | - | - | `GNO` | v1.29.0 | Replaced by NEXT_PUBLIC_NETWORK_SECONDARY_COIN_SYMBOL |
| NEXT_PUBLIC_SWAP_BUTTON_URL | `string` | Application ID in the marketplace or website URL | - | - | `uniswap` | v1.31.0 | Replaced by NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS |
......@@ -669,14 +669,13 @@ OpenTelemetry SDK for Node.js app could be enabled by passing `OTEL_SDK_ENABLED=
&nbsp;
### Blockscout DeFi
### Blockscout DeFi dropdown
If the feature is enabled, a single button or a dropdown (if more than 1 variable is provided) will be displayed at the top of the explorer page, which will take you to the specified application in the marketplace or to an external site.
If the feature is enabled, a single button or a dropdown (if more than 1 item is provided) will be displayed at the top of the explorer page, which will take you to the specified application in the marketplace or to an external site.
| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_SWAP_BUTTON_URL | `string` | Application ID in the marketplace or website URL | - | - | `uniswap` |
| NEXT_PUBLIC_PAYMENT_LINK_URL | `string` | Application ID in the marketplace or website URL | - | - | `peanut-protocol` |
| NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS | `[{ text: string; icon: string; dappId?: string, url?: string }]` | An array of dropdown items containing the button text, icon name and dappId in DAppscout or an external url | - | - | `[{'text':'Swap','icon':'swap','dappId':'uniswap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'}]` |
&nbsp;
......
......@@ -128,7 +128,7 @@ Type extends EventTypes.FILTERS ? {
'Filter name': string;
} :
Type extends EventTypes.BUTTON_CLICK ? {
'Content': 'Swap button' | 'Payment link';
'Content': string;
'Source': string;
} :
Type extends EventTypes.PROMO_BANNER ? {
......
import type { IconName } from 'ui/shared/IconSvg';
export type DeFiDropdownItem = {
text: string;
icon: IconName;
dappId?: string;
url?: string;
};
......@@ -2,7 +2,6 @@ import { Button, Box, Flex, Popover, PopoverTrigger, PopoverContent, PopoverBody
import { useRouter } from 'next/router';
import React from 'react';
import type { Route } from 'nextjs-routes';
import { route } from 'nextjs-routes';
import config from 'configs/app';
......@@ -13,14 +12,7 @@ import IconSvg from 'ui/shared/IconSvg';
import DeFiDropdownItem from './DeFiDropdownItem';
const feature = config.features.deFi;
function getUrl(feature: { url: string } | { dappId: string }): { url?: string; nextRoute?: Route } {
return {
url: 'url' in feature ? feature.url : undefined,
nextRoute: 'dappId' in feature ? { pathname: '/apps/[id]', query: { id: feature.dappId, action: 'connect' } } : undefined,
};
}
const feature = config.features.deFiDropdown;
const DeFiDropdown = () => {
const router = useRouter();
......@@ -28,39 +20,11 @@ const DeFiDropdown = () => {
const isMobile = useIsMobile();
const { isOpen, onToggle, onClose } = useDisclosure();
const handleClick = React.useCallback((content: 'Swap button' | 'Payment link') => {
const handleClick = React.useCallback((content: string) => {
mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: content, Source: source });
}, [ source ]);
const items = React.useMemo(() => {
if (!feature.isEnabled) {
return [];
}
const items = [];
if (feature.swapButton) {
items.push({
icon: 'swap' as const,
text: 'Swap',
onClick: () => handleClick('Swap button'),
...getUrl(feature.swapButton),
});
}
if (feature.paymentLink) {
items.push({
icon: 'payment_link' as const,
text: 'Payment link',
onClick: () => handleClick('Payment link'),
...getUrl(feature.paymentLink),
});
}
return items;
}, [ handleClick ]);
if (items.length === 0) {
if (!feature.isEnabled) {
return null;
}
......@@ -73,6 +37,11 @@ const DeFiDropdown = () => {
fontWeight: '500',
};
const items = feature.items.map((item) => ({
...item,
onClick: () => handleClick(item.text),
}));
return items.length > 1 ? (
<Popover isOpen={ isOpen } onClose={ onClose } placement="bottom-start" isLazy>
<PopoverTrigger>
......@@ -99,8 +68,8 @@ const DeFiDropdown = () => {
) : (
<Button
as="a"
href={ items[0].nextRoute ? route(items[0].nextRoute) : items[0].url }
target={ items[0].nextRoute ? '_self' : '_blank' }
href={ items[0].dappId ? route({ pathname: '/apps/[id]', query: { id: items[0].dappId, action: 'connect' } }) : items[0].url }
target={ items[0].dappId ? '_self' : '_blank' }
onClick={ items[0].onClick }
{ ...buttonStyles }
>
......
......@@ -2,29 +2,25 @@ import { Link, Text, HStack, chakra, shouldForwardProp, useColorModeValue } from
import NextLink from 'next/link';
import React from 'react';
import type { DeFiDropdownItem as TDeFiDropdownItem } from 'types/client/deFiDropdown';
import type { Route } from 'nextjs-routes';
import { route } from 'nextjs-routes';
import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg';
type Props = {
item: {
icon: IconName;
text: string;
nextRoute?: Route;
url?: string;
onClick: () => void;
};
item: TDeFiDropdownItem & { onClick: () => void };
}
const DeFiDropdownItem = ({ item }: Props) => {
const href = item.nextRoute ? route(item.nextRoute) : item.url;
const nextRoute: Route = { pathname: '/apps/[id]', query: { id: item.dappId || '', action: 'connect' } };
const href = item.dappId ? route(nextRoute) : item.url;
const content = (
<Link
href={ href }
target={ item.nextRoute ? '_self' : '_blank' }
target={ item.dappId ? '_self' : '_blank' }
w="100%"
h="34px"
display="flex"
......@@ -42,14 +38,14 @@ const DeFiDropdownItem = ({ item }: Props) => {
<IconSvg name={ item.icon } boxSize={ 5 }/>
<Text as="span" fontSize="sm">
<span>{ item.text }</span>
{ !item.nextRoute && <IconSvg name="arrows/north-east" boxSize={ 4 } color="text_secondary" verticalAlign="middle"/> }
{ !item.dappId && <IconSvg name="arrows/north-east" boxSize={ 4 } color="text_secondary" verticalAlign="middle"/> }
</Text>
</HStack>
</Link>
);
return item.nextRoute ? (
<NextLink href={ item.nextRoute } passHref legacyBehavior>
return item.dappId ? (
<NextLink href={ nextRoute } passHref legacyBehavior>
{ content }
</NextLink>
) : content;
......
......@@ -7,7 +7,7 @@ import DeFiDropdown from './DeFiDropdown';
import Settings from './settings/Settings';
import TopBarStats from './TopBarStats';
const feature = config.features.deFi;
const feature = config.features.deFiDropdown;
const TopBar = () => {
const bgColor = useColorModeValue('gray.50', 'whiteAlpha.100');
......
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