Skip to content

Commit

Permalink
Bridged tokens (#1249)
Browse files Browse the repository at this point in the history
* tag "Bridged"

* base implementation of bridged tokens table

* - add tags
- reset search term and sort when switching between tabs
- fix skeleton
- add descriptioin

* - save search and filter in query
- add link to original token on token page

* add envs

* fix ts

* pin host for demo

* adjust envs schema

* fix schema

* fix review env configs

* tests

* rollback api config

* refactor query

* [skip ci] refactor filters

* update description text and disable reset link if there are no filters active

* fix poppover filter and tabs resize issues

* fix tests
  • Loading branch information
tom2drum authored Oct 9, 2023
1 parent 2ffc1e5 commit 87036c3
Show file tree
Hide file tree
Showing 61 changed files with 1,008 additions and 306 deletions.
1 change: 1 addition & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@
"eth",
"rootstock",
"polygon",
"gnosis",
"localhost",
],
"default": "main"
Expand Down
27 changes: 27 additions & 0 deletions configs/app/features/bridgedTokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Feature } from './types';
import type { BridgedTokenChain, TokenBridge } from 'types/client/token';

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

const title = 'Bridged tokens';

const config: Feature<{ chains: Array<BridgedTokenChain>; bridges: Array<TokenBridge> }> = (() => {
const chains = parseEnvJson<Array<BridgedTokenChain>>(getEnvValue('NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS'));
const bridges = parseEnvJson<Array<TokenBridge>>(getEnvValue('NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES'));

if (chains && chains.length > 0 && bridges && bridges.length > 0) {
return Object.freeze({
title,
isEnabled: true,
chains,
bridges,
});
}

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

export default config;
1 change: 1 addition & 0 deletions configs/app/features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export { default as addressVerification } from './addressVerification';
export { default as adsBanner } from './adsBanner';
export { default as adsText } from './adsText';
export { default as beaconChain } from './beaconChain';
export { default as bridgedTokens } from './bridgedTokens';
export { default as blockchainInteraction } from './blockchainInteraction';
export { default as csvExport } from './csvExport';
export { default as googleAnalytics } from './googleAnalytics';
Expand Down
53 changes: 53 additions & 0 deletions configs/envs/.env.gnosis
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Set of ENVs for Gnosis network explorer
# https://gnosis.blockscout.com/

# app configuration
NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000

# blockchain parameters
NEXT_PUBLIC_NETWORK_NAME=Gnosis
NEXT_PUBLIC_NETWORK_SHORT_NAME=Gnosis
NEXT_PUBLIC_NETWORK_ID=100
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=xDAI
NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=xDAI
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE=validation
NEXT_PUBLIC_NETWORK_RPC_URL=https://rpc.gnosischain.com

# api configuration
NEXT_PUBLIC_API_HOST=gnosis.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/

# ui config
## homepage
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND="rgb(46, 74, 60)"
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR="rgb(255, 255, 255)"
## sidebar
NEXT_PUBLIC_FEATURED_NETWORKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/featured-networks/gnosis-chain-mainnet.json
NEXT_PUBLIC_NETWORK_LOGO=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-logos/gnosis.svg
NEXT_PUBLIC_NETWORK_ICON=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/network-icons/gnosis.svg
## footer
NEXT_PUBLIC_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/gnosis.json
## views
## misc

# app features
NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x082762f95047d39d612daafec832f88163f3815fde4ddd8944f2a5198a396e0f
# NEXT_PUBLIC_BEACON_CHAIN_CURRENCY_SYMBOL=GNO
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_LOGOUT_URL=https://blockscoutcom.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CONFIG_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace/gnosis-chain.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/shrmiO9mDGJoPNmJe
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
NEXT_PUBLIC_WEB3_WALLETS=['token_pocket','metamask']
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_BRIDGED_TOKENS_CHAINS=[{'id':'1','title':'Ethereum','short_title':'ETH','base_url':'https://eth.blockscout.com/token/'},{'id':'56','title':'Binance Smart Chain','short_title':'BSC','base_url':'https://bscscan.com/token/'},{'id':'99','title':'POA','short_title':'POA','base_url':'https://blockscout.com/poa/core/token/'}]
NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES=[{'type':'omni','title':'OmniBridge','short_title':'OMNI'},{'type':'amb','title':'Arbitrary Message Bridge','short_title':'AMB'}]

#meta
NEXT_PUBLIC_OG_IMAGE_URL=https://github.com/blockscout/frontend-configs/blob/main/configs/og-images/polygon-mainnet.png?raw=true
37 changes: 37 additions & 0 deletions deploy/tools/envs-validator/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS } from '../.
import type { AdTextProviders, AdBannerProviders } from '../../../types/client/adProviders';
import type { MarketplaceAppOverview } from '../../../types/client/marketplace';
import type { NavItemExternal } from '../../../types/client/navigation-items';
import type { BridgedTokenChain, TokenBridge } from '../../../types/client/token';
import type { WalletType } from '../../../types/client/wallets';
import { SUPPORTED_WALLETS } from '../../../types/client/wallets';
import type { CustomLink, CustomLinksGroup } from '../../../types/footerLinks';
Expand Down Expand Up @@ -247,6 +248,41 @@ const networkExplorerSchema: yup.ObjectSchema<NetworkExplorer> = yup
}),
});

const bridgedTokenChainSchema: yup.ObjectSchema<BridgedTokenChain> = yup
.object({
id: yup.string().required(),
title: yup.string().required(),
short_title: yup.string().required(),
base_url: yup.string().test(urlTest).required(),
});

const tokenBridgeSchema: yup.ObjectSchema<TokenBridge> = yup
.object({
type: yup.string().required(),
title: yup.string().required(),
short_title: yup.string().required(),
});

const bridgedTokensSchema = yup
.object()
.shape({
NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS: yup
.array()
.transform(replaceQuotes)
.json()
.of(bridgedTokenChainSchema),
NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES: yup
.array()
.transform(replaceQuotes)
.json()
.of(tokenBridgeSchema)
.when('NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS', {
is: (value: Array<unknown>) => value && value.length > 0,
then: (schema) => schema.required(),
otherwise: (schema) => schema.max(-1, 'NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES cannot not be used without NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS'),
}),
});

const schema = yup
.object()
.noUnknown(true, (params) => {
Expand Down Expand Up @@ -374,6 +410,7 @@ const schema = yup
.concat(marketplaceSchema)
.concat(rollupSchema)
.concat(beaconChainSchema)
.concat(bridgedTokensSchema)
.concat(sentrySchema);

export default schema;
2 changes: 2 additions & 0 deletions deploy/values/review-l2/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ frontend:
exact:
# - "/(apps|auth/profile|account)"
- "/"
- "/envs.js"
prefix:
# - "/(apps|auth/profile|account)"
- "/_next"
Expand All @@ -25,6 +26,7 @@ frontend:
- "/apps"
- "/static"
- "/favicon"
- "/assets"
- "/auth/profile"
- "/auth/unverified-email"
- "/txs"
Expand Down
3 changes: 3 additions & 0 deletions deploy/values/review/values.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ frontend:
- "/static"
- "/assets"
- "/favicon"
- "/assets"
- "/auth/profile"
- "/auth/unverified-email"
- "/txs"
Expand Down Expand Up @@ -134,3 +135,5 @@ frontend:
_default: "['token_pocket','coinbase','metamask']"
NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE:
_default: gradient_avatar
NEXT_PUBLIC_USE_NEXT_JS_PROXY:
_default: true
32 changes: 32 additions & 0 deletions docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ The app instance could be customized by passing following variables to NodeJS en
- [Blockchain statistics](ENVS.md#blockchain-statistics)
- [Web3 wallet integration](ENVS.md#web3-wallet-integration-add-token-or-network-to-the-wallet) (add token or network to the wallet)
- [Verified tokens info](ENVS.md#verified-tokens-info)
- [Bridged tokens](ENVS.md#bridged-tokens)
- [Safe{Core} address tags](ENVS.md#safecore-address-tags)
- [SUAVE chain](ENVS.md#suave-chain)
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
- [3rd party services configuration](ENVS.md#external-services-configuration)
Expand Down Expand Up @@ -406,6 +408,36 @@ This feature is **enabled by default** with the `['metamask']` value. To switch

&nbsp;

### Bridged tokens

This feature allows users to view tokens that have been bridged from other EVM chains. Additional tab "Bridged" will be added to the tokens page and the link to original token will be displayed on the token page.

| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS | `Array<BridgedTokenChain>` where `BridgedTokenChain` can have following [properties](#bridged-token-chain-configuration-properties) | Used for displaying filter by the chain from which token where bridged. Also, used for creating links to original tokens in other explorers. | Required | - | `[{'id':'1','title':'Ethereum','short_title':'ETH','base_url':'https://eth.blockscout.com/token'}]` |
| NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES | `Array<TokenBridge>` where `TokenBridge` can have following [properties](#token-bridge-configuration-properties) | Used for displaying text about bridges types on the tokens page. | Required | - | `[{'type':'omni','title':'OmniBridge','short_title':'OMNI'}]` |

#### Bridged token chain configuration properties

| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| id | `string` | Base chain id, see [https://chainlist.org](https://chainlist.org) for the reference | Required | - | `1` |
| title | `string` | Displayed name of the chain | Required | - | `Ethereum` |
| short_title | `string` | Used for displaying chain name in the list view as tag | Required | - | `ETH` |
| base_url | `string` | Base url to original token in base chain explorer | Required | - | `https://eth.blockscout.com/token` |

*Note* The url to original token will be constructed as `<base_url>/<token_hash>`, e.g `https://eth.blockscout.com/token/<token_hash>`

#### Token bridge configuration properties

| Variable | Type| Description | Compulsoriness | Default value | Example value |
| --- | --- | --- | --- | --- | --- |
| type | `string` | Bridge type; should be matched to `bridge_type` field in API response | Required | - | `omni` |
| title | `string` | Bridge title | Required | - | `OmniBridge` |
| short_title | `string` | Bridge short title for displaying in the tags | Required | - | `OMNI` |

&nbsp;

### Safe{Core} address tags

For the smart contract addresses which are [Safe{Core} accounts](https://safe.global/) public tag "Multisig: Safe" will be displayed in the address page header along side to Safe logo. The Safe service is available only for certain networks, see full list [here](https://docs.safe.global/safe-core-api/available-services). Based on provided value of `NEXT_PUBLIC_NETWORK_ID`, the feature will be enabled or disabled.
Expand Down
11 changes: 9 additions & 2 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import type {
TokenInstanceTransfersCount,
TokenVerifiedInfo,
} from 'types/api/token';
import type { TokensResponse, TokensFilters, TokensSorting, TokenInstanceTransferResponse } from 'types/api/tokens';
import type { TokensResponse, TokensFilters, TokensSorting, TokenInstanceTransferResponse, TokensBridgedFilters } from 'types/api/tokens';
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
import type { TransactionsResponseValidated, TransactionsResponsePending, Transaction, TransactionsResponseWatchlist } from 'types/api/transaction';
import type { TTxsFilters } from 'types/api/txsFilters';
Expand Down Expand Up @@ -382,6 +382,10 @@ export const RESOURCES = {
path: '/api/v2/tokens',
filterFields: [ 'q' as const, 'type' as const ],
},
tokens_bridged: {
path: '/api/v2/tokens/bridged',
filterFields: [ 'q' as const, 'chain_ids' as const ],
},

// TOKEN INSTANCE
token_instance: {
Expand Down Expand Up @@ -544,7 +548,7 @@ export type PaginatedResources = 'blocks' | 'block_txs' |
'address_txs' | 'address_internal_txs' | 'address_token_transfers' | 'address_blocks_validated' | 'address_coin_balance' |
'search' |
'address_logs' | 'address_tokens' |
'token_transfers' | 'token_holders' | 'token_inventory' | 'tokens' |
'token_transfers' | 'token_holders' | 'token_inventory' | 'tokens' | 'tokens_bridged' |
'token_instance_transfers' | 'token_instance_holders' |
'verified_contracts' |
'l2_output_roots' | 'l2_withdrawals' | 'l2_txn_batches' | 'l2_deposits' |
Expand Down Expand Up @@ -613,6 +617,7 @@ Q extends 'token_instance_transfers' ? TokenInstanceTransferResponse :
Q extends 'token_instance_holders' ? TokenHolders :
Q extends 'token_inventory' ? TokenInventoryResponse :
Q extends 'tokens' ? TokensResponse :
Q extends 'tokens_bridged' ? TokensResponse :
Q extends 'quick_search' ? Array<SearchResultItem> :
Q extends 'search' ? SearchResult :
Q extends 'search_check_redirect' ? SearchRedirectResult :
Expand Down Expand Up @@ -650,12 +655,14 @@ Q extends 'address_token_transfers' ? AddressTokenTransferFilters :
Q extends 'address_tokens' ? AddressTokensFilter :
Q extends 'search' ? SearchResultFilters :
Q extends 'tokens' ? TokensFilters :
Q extends 'tokens_bridged' ? TokensBridgedFilters :
Q extends 'verified_contracts' ? VerifiedContractsFilters :
never;
/* eslint-enable @typescript-eslint/indent */

/* eslint-disable @typescript-eslint/indent */
export type PaginationSorting<Q extends PaginatedResources> =
Q extends 'tokens' ? TokensSorting :
Q extends 'tokens_bridged' ? TokensSorting :
never;
/* eslint-enable @typescript-eslint/indent */
4 changes: 2 additions & 2 deletions lib/token/tokenTypes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { TokenType } from 'types/api/token';

const TOKEN_TYPE: Array<{ title: string; id: TokenType }> = [
export const TOKEN_TYPES: Array<{ title: string; id: TokenType }> = [
{ title: 'ERC-20', id: 'ERC-20' },
{ title: 'ERC-721', id: 'ERC-721' },
{ title: 'ERC-1155', id: 'ERC-1155' },
];

export default TOKEN_TYPE;
export const TOKEN_TYPE_IDS = TOKEN_TYPES.map(i => i.id);
34 changes: 29 additions & 5 deletions mocks/tokens/tokenInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const tokenInfoERC20b: TokenInfo<'ERC-20'> = {
};

export const tokenInfoERC20c: TokenInfo<'ERC-20'> = {
address: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A7',
address: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A8',
circulating_market_cap: null,
decimals: '18',
exchange_rate: '1328.89',
Expand All @@ -58,7 +58,7 @@ export const tokenInfoERC20c: TokenInfo<'ERC-20'> = {
};

export const tokenInfoERC20d: TokenInfo<'ERC-20'> = {
address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195',
address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9196',
circulating_market_cap: null,
decimals: '18',
exchange_rate: null,
Expand All @@ -71,7 +71,7 @@ export const tokenInfoERC20d: TokenInfo<'ERC-20'> = {
};

export const tokenInfoERC20LongSymbol: TokenInfo<'ERC-20'> = {
address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195',
address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9197',
circulating_market_cap: '112855875.75888918',
decimals: '18',
exchange_rate: '1328.89',
Expand Down Expand Up @@ -123,7 +123,7 @@ export const tokenInfoERC721c: TokenInfo<'ERC-721'> = {
};

export const tokenInfoERC721LongSymbol: TokenInfo<'ERC-721'> = {
address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4992',
address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4993',
circulating_market_cap: null,
decimals: null,
exchange_rate: null,
Expand Down Expand Up @@ -162,7 +162,7 @@ export const tokenInfoERC1155b: TokenInfo<'ERC-1155'> = {
};

export const tokenInfoERC1155WithoutName: TokenInfo<'ERC-1155'> = {
address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e',
address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8a',
circulating_market_cap: null,
decimals: null,
exchange_rate: null,
Expand All @@ -173,3 +173,27 @@ export const tokenInfoERC1155WithoutName: TokenInfo<'ERC-1155'> = {
type: 'ERC-1155',
icon_url: null,
};

export const bridgedTokenA: TokenInfo<'ERC-20'> = {
...tokenInfoERC20a,
is_bridged: true,
origin_chain_id: '1',
bridge_type: 'omni',
foreign_address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8b',
};

export const bridgedTokenB: TokenInfo<'ERC-20'> = {
...tokenInfoERC20b,
is_bridged: true,
origin_chain_id: '56',
bridge_type: 'omni',
foreign_address: '0xf4b71b179132ad457f6bcae2a55efa9e4b26eefd',
};

export const bridgedTokenC: TokenInfo<'ERC-20'> = {
...tokenInfoERC20d,
is_bridged: true,
origin_chain_id: '99',
bridge_type: 'amb',
foreign_address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4994',
};
11 changes: 11 additions & 0 deletions playwright/utils/configs.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-len */
import { devices } from '@playwright/test';

export const viewport = {
Expand All @@ -18,6 +19,16 @@ export const featureEnvs = {
{ name: 'NEXT_PUBLIC_L1_BASE_URL', value: 'https://localhost:3101' },
{ name: 'NEXT_PUBLIC_L2_WITHDRAWAL_URL', value: 'https://localhost:3102' },
],
bridgedTokens: [
{
name: 'NEXT_PUBLIC_BRIDGED_TOKENS_CHAINS',
value: '[{"id":"1","title":"Ethereum","short_title":"ETH","base_url":"https://eth.blockscout.com/token/"},{"id":"56","title":"Binance Smart Chain","short_title":"BSC","base_url":"https://bscscan.com/token/"},{"id":"99","title":"POA","short_title":"POA","base_url":"https://blockscout.com/poa/core/token/"}]',
},
{
name: 'NEXT_PUBLIC_BRIDGED_TOKENS_BRIDGES',
value: '[{"type":"omni","title":"OmniBridge","short_title":"OMNI"},{"type":"amb","title":"Arbitrary Message Bridge","short_title":"AMB"}]',
},
],
};

export const viewsEnvs = {
Expand Down
Loading

0 comments on commit 87036c3

Please sign in to comment.