Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pools pages #2468

Merged
merged 3 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configs/app/features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export { default as mixpanel } from './mixpanel';
export { default as mudFramework } from './mudFramework';
export { default as multichainButton } from './multichainButton';
export { default as nameService } from './nameService';
export { default as pools } from './pools';
export { default as publicTagsSubmission } from './publicTagsSubmission';
export { default as restApiDocs } from './restApiDocs';
export { default as rewards } from './rewards';
Expand Down
28 changes: 28 additions & 0 deletions configs/app/features/pools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Feature } from './types';

import { getEnvValue } from '../utils';

const contractInfoApiHost = getEnvValue('NEXT_PUBLIC_CONTRACT_INFO_API_HOST');
const dexPoolsEnabled = getEnvValue('NEXT_PUBLIC_DEX_POOLS_ENABLED') === 'true';

const title = 'DEX Pools';

const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => {
if (contractInfoApiHost && dexPoolsEnabled) {
return Object.freeze({
title,
isEnabled: true,
api: {
endpoint: contractInfoApiHost,
basePath: '',
},
});
}

return Object.freeze({
title,
isEnabled: false,
});
})();

export default config;
3 changes: 2 additions & 1 deletion configs/envs/.env.eth_sepolia
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=eth-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_CONTRACT_CODE_IDES=[{'title':'Remix IDE','url':'https://remix.ethereum.org/?address={hash}&blockscout={domain}','icon_url':'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/ide-icons/remix.png'}]
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info.services.blockscout.com
NEXT_PUBLIC_CONTRACT_INFO_API_HOST=https://contracts-info-test.k8s-dev.blockscout.com
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'cow-swap'},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'}]
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/eth-sepolia.json
Expand Down Expand Up @@ -69,3 +69,4 @@ NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_XSTAR_SCORE_URL=https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address
NEXT_PUBLIC_DEX_POOLS_ENABLED=true
10 changes: 10 additions & 0 deletions deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,16 @@ const schema = yup
value => value === undefined,
),
}),
NEXT_PUBLIC_DEX_POOLS_ENABLED: yup.boolean()
.when('NEXT_PUBLIC_CONTRACT_INFO_API_HOST', {
is: (value: string) => Boolean(value),
then: (schema) => schema,
otherwise: (schema) => schema.test(
'not-exist',
'NEXT_PUBLIC_DEX_POOLS_ENABLED can only be used with NEXT_PUBLIC_CONTRACT_INFO_API_HOST',
value => value === undefined,
),
}),
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED: yup.boolean(),
NEXT_PUBLIC_ADDRESS_USERNAME_TAG: yup
.mixed()
Expand Down
10 changes: 10 additions & 0 deletions docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
- [Get gas button](ENVS.md#get-gas-button)
- [Save on gas with GasHawk](ENVS.md#save-on-gas-with-gashawk)
- [Rewards service API](ENVS.md#rewards-service-api)
- [DEX pools](ENVS.md#dex-pools)
- [3rd party services configuration](ENVS.md#external-services-configuration)

&nbsp;
Expand Down Expand Up @@ -852,6 +853,15 @@ This feature enables Blockscout Merits program. It requires that the [My account
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_REWARDS_SERVICE_API_HOST | `string` | API URL | - | - | `https://example.com` | v1.36.0+ |

&nbsp;

### DEX pools

| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_DEX_POOLS_ENABLED | `boolean` | Set to true to enable the feature | Required | - | `true` | v1.37.0+ |
| NEXT_PUBLIC_CONTRACT_INFO_API_HOST | `string` | Contract Info API endpoint url | Required | - | `https://contracts-info.services.blockscout.com` | v1.0.x+ |

## External services configuration

### Google ReCaptcha
Expand Down
4 changes: 4 additions & 0 deletions icons/dex-tracker.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 21 additions & 1 deletion lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ import type {
OptimismL2BatchTxs,
OptimismL2BatchBlocks,
} from 'types/api/optimisticL2';
import type { Pool, PoolsResponse } from 'types/api/pools';
import type { RawTracesResponse } from 'types/api/rawTrace';
import type {
RewardsConfigResponse,
Expand Down Expand Up @@ -1128,6 +1129,22 @@ export const RESOURCES = {
path: '/api/v2/advanced-filters/csv',
},

// POOLS
pools: {
path: '/api/v1/chains/:chainId/pools',
pathParams: [ 'chainId' as const ],
filterFields: [ 'query' as const ],
endpoint: getFeaturePayload(config.features.pools)?.api.endpoint,
basePath: getFeaturePayload(config.features.pools)?.api.basePath,
},

pool: {
path: '/api/v1/chains/:chainId/pools/:hash',
pathParams: [ 'chainId' as const, 'hash' as const ],
endpoint: getFeaturePayload(config.features.pools)?.api.endpoint,
basePath: getFeaturePayload(config.features.pools)?.api.basePath,
},

// CONFIGS
config_backend_version: {
path: '/api/v2/config/backend-version',
Expand Down Expand Up @@ -1222,7 +1239,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' | 'block_election_reward
'watchlist' | 'private_tags_address' | 'private_tags_tx' |
'domains_lookup' | 'addresses_lookup' | 'user_ops' | 'validators_stability' | 'validators_blackfort' | 'noves_address_history' |
'token_transfers_all' | 'scroll_l2_txn_batches' | 'scroll_l2_txn_batch_txs' | 'scroll_l2_txn_batch_blocks' |
'scroll_l2_deposits' | 'scroll_l2_withdrawals' | 'advanced_filter';
'scroll_l2_deposits' | 'scroll_l2_withdrawals' | 'advanced_filter' | 'pools';

export type PaginatedResponse<Q extends PaginatedResources> = ResourcePayload<Q>;

Expand Down Expand Up @@ -1416,6 +1433,8 @@ Q extends 'scroll_l2_withdrawals' ? ScrollL2MessagesResponse :
Q extends 'scroll_l2_withdrawals_count' ? number :
Q extends 'advanced_filter' ? AdvancedFilterResponse :
Q extends 'advanced_filter_methods' ? AdvancedFilterMethodsResponse :
Q extends 'pools' ? PoolsResponse :
Q extends 'pool' ? Pool :
never;
/* eslint-enable @stylistic/indent */

Expand Down Expand Up @@ -1452,6 +1471,7 @@ Q extends 'address_mud_tables' ? AddressMudTablesFilter :
Q extends 'address_mud_records' ? AddressMudRecordsFilter :
Q extends 'token_transfers_all' ? TokenTransferFilters :
Q extends 'advanced_filter' ? AdvancedFilterParams :
Q extends 'pools' ? { query: string } :
never;
/* eslint-enable @stylistic/indent */

Expand Down
5 changes: 5 additions & 0 deletions lib/getItemIndex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const DEFAULT_PAGE_SIZE = 50;

export default function getItemIndex(index: number, page: number, pageSize: number = DEFAULT_PAGE_SIZE) {
return (page - 1) * pageSize + index + 1;
};
8 changes: 7 additions & 1 deletion lib/hooks/useNavItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,13 @@ export default function useNavItems(): ReturnType {
icon: 'token-transfers',
isActive: pathname === '/token-transfers',
},
];
config.features.pools.isEnabled && {
text: 'DEX tracker',
nextRoute: { pathname: '/pools' as const },
icon: 'dex-tracker',
isActive: pathname === '/pools' || pathname.startsWith('/pool/'),
},
].filter(Boolean);

const apiNavItems: Array<NavItem> = [
config.features.restApiDocs.isEnabled ? {
Expand Down
2 changes: 2 additions & 0 deletions lib/metadata/getPageOgType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
'/mud-worlds': 'Root page',
'/token-transfers': 'Root page',
'/advanced-filter': 'Root page',
'/pools': 'Root page',
'/pools/[hash]': 'Regular page',

// service routes, added only to make typescript happy
'/login': 'Regular page',
Expand Down
2 changes: 2 additions & 0 deletions lib/metadata/templates/description.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/mud-worlds': DEFAULT_TEMPLATE,
'/token-transfers': DEFAULT_TEMPLATE,
'/advanced-filter': DEFAULT_TEMPLATE,
'/pools': DEFAULT_TEMPLATE,
'/pools/[hash]': DEFAULT_TEMPLATE,

// service routes, added only to make typescript happy
'/login': DEFAULT_TEMPLATE,
Expand Down
2 changes: 2 additions & 0 deletions lib/metadata/templates/title.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
'/mud-worlds': '%network_name% MUD worlds list',
'/token-transfers': '%network_name% token transfers',
'/advanced-filter': '%network_name% advanced filter',
'/pools': '%network_name% DEX pools',
'/pools/[hash]': '%network_name% pool details',

// service routes, added only to make typescript happy
'/login': '%network_name% login',
Expand Down
2 changes: 2 additions & 0 deletions lib/mixpanel/getPageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
'/mud-worlds': 'MUD worlds',
'/token-transfers': 'Token transfers',
'/advanced-filter': 'Advanced filter',
'/pools': 'DEX pools',
'/pools/[hash]': 'Pool details',

// service routes, added only to make typescript happy
'/login': 'Login',
Expand Down
21 changes: 21 additions & 0 deletions lib/pools/getPoolLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { Pool } from 'types/api/pools';

type PoolLink = {
url: string;
image: string;
title: string;
};

export default function getPoolLinks(pool?: Pool): Array<PoolLink> {
if (!pool) {
return [];
}

return [
{
url: pool.coin_gecko_terminal_url,
image: '/static/gecko_terminal.png',
title: 'GeckoTerminal',
},
].filter(link => Boolean(link.url));
}
5 changes: 5 additions & 0 deletions lib/pools/getPoolTitle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { Pool } from 'types/api/pools';

export const getPoolTitle = (pool: Pool) => {
return `${ pool.base_token_symbol } / ${ pool.quote_token_symbol } ${ pool.fee ? `(${ pool.fee }%)` : '' }`;
};
24 changes: 24 additions & 0 deletions mocks/pools/pool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Pool } from 'types/api/pools';

export const base: Pool = {
contract_address: '0x06da0fd433c1a5d7a4faa01111c044910a184553',
chain_id: '1',
base_token_address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
base_token_symbol: 'USDT',
base_token_icon_url: 'https://localhost:3000/utia.jpg',
quote_token_address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
quote_token_symbol: 'WETH',
quote_token_icon_url: 'https://localhost:3000/secondary_utia.jpg',
fully_diluted_valuation_usd: '75486579078',
market_cap_usd: '139312819076.195',
liquidity: '2099941.2238',
dex: { id: 'sushiswap', name: 'SushiSwap' },
fee: '0.03',
coin_gecko_terminal_url: 'https://www.geckoterminal.com/eth/pools/0x06da0fd433c1a5d7a4faa01111c044910a184553',
};

export const noIcons: Pool = {
...base,
base_token_icon_url: null,
quote_token_icon_url: null,
};
10 changes: 10 additions & 0 deletions nextjs/getServerSideProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,13 @@ export const mud: GetServerSideProps<Props> = async(context) => {

return base(context);
};

export const pools: GetServerSideProps<Props> = async(context) => {
if (!config.features.pools.isEnabled) {
return {
notFound: true,
};
}

return base(context);
};
2 changes: 2 additions & 0 deletions nextjs/nextjs-routes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ declare module "nextjs-routes" {
| DynamicRoute<"/op/[hash]", { "hash": string }>
| StaticRoute<"/ops">
| StaticRoute<"/output-roots">
| DynamicRoute<"/pools/[hash]", { "hash": string }>
| StaticRoute<"/pools">
| StaticRoute<"/public-tags/submit">
| StaticRoute<"/search-results">
| StaticRoute<"/sprite">
Expand Down
20 changes: 20 additions & 0 deletions pages/pools/[hash].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';

import type { Props } from 'nextjs/getServerSideProps';
import PageNextJs from 'nextjs/PageNextJs';

const Pool = dynamic(() => import('ui/pages/Pool'), { ssr: false });

const Page: NextPage<Props> = (props: Props) => {
return (
<PageNextJs pathname="/pools/[hash]" query={ props.query }>
<Pool/>
</PageNextJs>
);
};

export default Page;

export { pools as getServerSideProps } from 'nextjs/getServerSideProps';
19 changes: 19 additions & 0 deletions pages/pools/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
import React from 'react';

import PageNextJs from 'nextjs/PageNextJs';

const Pools = dynamic(() => import('ui/pages/Pools'), { ssr: false });

const Page: NextPage = () => {
return (
<PageNextJs pathname="/pools">
<Pools/>
</PageNextJs>
);
};

export default Page;

export { pools as getServerSideProps } from 'nextjs/getServerSideProps';
1 change: 1 addition & 0 deletions public/icons/name.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
| "copy"
| "cross"
| "delete"
| "dex-tracker"
| "docs"
| "donate"
| "dots"
Expand Down
Binary file added public/static/gecko_terminal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions stubs/pools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const POOL = {
contract_address: '0x6a1041865b76d1dc33da0257582591227c57832c',
chain_id: '1',
base_token_address: '0xf63e309818e4ea13782678ce6c31c1234fa61809',
base_token_symbol: 'JANET',
base_token_icon_url: null,
quote_token_address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
quote_token_symbol: 'WETH',
quote_token_icon_url: 'https://coin-images.coingecko.com/coins/images/2518/small/weth.png?1696503332',
fully_diluted_valuation_usd: '15211385',
market_cap_usd: '15211385',
liquidity: '394101.2428',
dex: { id: 'uniswap_v2', name: 'Uniswap V2' },
coin_gecko_terminal_url: 'https://www.geckoterminal.com/eth/pools/0x6a1041865b76d1dc33da0257582591227c57832c',
};
27 changes: 27 additions & 0 deletions types/api/pools.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export type PoolsResponse = {
items: Array<Pool>;
next_page_params: {
page_token: string;
page_size: number;
} | null;
};

export type Pool = {
contract_address: string;
chain_id: string;
base_token_address: string;
base_token_symbol: string;
base_token_icon_url: string | null;
quote_token_address: string;
quote_token_symbol: string;
quote_token_icon_url: string | null;
fully_diluted_valuation_usd: string;
market_cap_usd: string;
liquidity: string;
dex: {
id: string;
name: string;
};
fee?: string;
coin_gecko_terminal_url: string;
};
2 changes: 1 addition & 1 deletion ui/marketplace/MarketplaceAppInfo.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ test.describe('mobile', () => {

test('base view', async({ render, page }) => {
await render(<MarketplaceAppInfo data={ appsMock[0] }/>);
await page.getByLabel('Show project info').click();
await page.getByLabel('Show info').click();
await expect(page).toHaveScreenshot();
});
});
Loading
Loading