diff --git a/.env.template b/.env.template index dd17c4db4f..50ee492d44 100644 --- a/.env.template +++ b/.env.template @@ -45,6 +45,9 @@ NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=__PLACEHOLDER_FOR_NEXT_PUBLIC_HOMEPAGE_SHO NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=__PLACEHOLDER_FOR_NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME__ NEXT_PUBLIC_AD_DOMAIN_WITH_AD=__PLACEHOLDER_FOR_NEXT_PUBLIC_AD_DOMAIN_WITH_AD__ NEXT_PUBLIC_AD_ADBUTLER_ON=__PLACEHOLDER_FOR_NEXT_PUBLIC_AD_ADBUTLER_ON__ +NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP=__PLACEHOLDER_FOR_NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP__ +NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE=__PLACEHOLDER_FOR_NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE__ +NEXT_PUBLIC_AD_SLISE_ON=__PLACEHOLDER_FOR_NEXT_PUBLIC_AD_SLISE_ON__ NEXT_PUBLIC_GRAPHIQL_TRANSACTION=__PLACEHOLDER_FOR_NEXT_PUBLIC_GRAPHIQL_TRANSACTION__ NEXT_PUBLIC_WEB3_DEFAULT_WALLET=__PLACEHOLDER_FOR_NEXT_PUBLIC_WEB3_DEFAULT_WALLET__ NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET=__PLACEHOLDER_FOR_NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET__ diff --git a/configs/app/config.ts b/configs/app/config.ts index fc1ceadf0d..e86e64abf3 100644 --- a/configs/app/config.ts +++ b/configs/app/config.ts @@ -1,4 +1,5 @@ /* eslint-disable no-restricted-properties */ +import type { AdButlerConfig } from 'types/client/adButlerConfig'; import type { NavItemExternal } from 'types/client/navigation-items'; import type { WalletType } from 'types/client/wallets'; import type { NetworkExplorer } from 'types/networks'; @@ -114,6 +115,9 @@ const config = Object.freeze({ ad: { domainWithAd: getEnvValue(process.env.NEXT_PUBLIC_AD_DOMAIN_WITH_AD) || 'blockscout.com', adButlerOn: getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_ON) === 'true', + adButlerConfigDesktop: parseEnvJson(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP)), + adButlerConfigMobile: parseEnvJson(getEnvValue(process.env.NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE)), + sliseOn: getEnvValue(process.env.NEXT_PUBLIC_AD_SLISE_ON) === 'true', }, web3: { defaultWallet: getWeb3DefaultWallet(), diff --git a/deploy/values/review/values.yaml.gotmpl b/deploy/values/review/values.yaml.gotmpl index 3014a20934..2039329a87 100644 --- a/deploy/values/review/values.yaml.gotmpl +++ b/deploy/values/review/values.yaml.gotmpl @@ -133,3 +133,5 @@ frontend: _default: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: _default: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID + NEXT_PUBLIC_AD_SLISE_ON: + _default: 'true' diff --git a/docs/ENVS.md b/docs/ENVS.md index 5974a64eeb..5610c1b691 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -46,6 +46,9 @@ The app instance could be customized by passing following variables to NodeJS en | NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME | `boolean` | Set to false if average block time is useless for the network | - | `true` | `false` | | NEXT_PUBLIC_AD_DOMAIN_WITH_AD | `string` | The domain on which we display ads | - | - | `blockscout.com` | | NEXT_PUBLIC_AD_ADBUTLER_ON | `boolean` | Set to true to show Adbutler banner instead of Coinzilla banner | - | `false` | `true` | +| NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP | `{ id: string; width: string; height: string }` | Placement config for desktop Adbutler banner | - | - | `{'id':'123456','width':'728','height':'90'}` | +| NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE | `{ id: string; width: number; height: number }` | Placement config for mobile Adbutler banner | - | - | `{'id':'654321','width':'300','height':'100'}` | +| NEXT_PUBLIC_AD_SLISE_ON | `boolean` | Set to true to show Slise banner instead of Coinzilla banner | - | `false` | `true` | | NEXT_PUBLIC_API_SPEC_URL | `string` | Spec to be displayed on api-docs page | - | - | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` | | NEXT_PUBLIC_GRAPHIQL_TRANSACTION | `string` | Txn hash for default query at GraphQl playground page | - | - | `0x69e3923eef50eada197c3336d546936d0c994211492c9f947a24c02827568f9f` | | NEXT_PUBLIC_WEB3_DEFAULT_WALLET | `metamask` \| `coinbase`| Type of Web3 wallet which will be used by default to add tokens or chains to | - | `metamask` | `coinbase` | diff --git a/lib/api/buildUrl.ts b/lib/api/buildUrl.ts index bee6f8faaa..137652efd4 100644 --- a/lib/api/buildUrl.ts +++ b/lib/api/buildUrl.ts @@ -18,8 +18,8 @@ export default function buildUrl( const url = new URL(compile(path)(pathParams), baseUrl); queryParams && Object.entries(queryParams).forEach(([ key, value ]) => { - // there are some pagination params that can be null for the next page - (value || value === null) && url.searchParams.append(key, String(value)); + // there are some pagination params that can be null or false for the next page + value !== undefined && value !== '' && url.searchParams.append(key, String(value)); }); return url.toString(); diff --git a/lib/csp/policies/ad.ts b/lib/csp/policies/ad.ts index 9f75dc825b..90158f0bf2 100644 --- a/lib/csp/policies/ad.ts +++ b/lib/csp/policies/ad.ts @@ -1,6 +1,9 @@ +import Base64 from 'crypto-js/enc-base64'; +import sha256 from 'crypto-js/sha256'; import type CspDev from 'csp-dev'; import isSelfHosted from 'lib/isSelfHosted'; +import { connectAdbutler, placeAd } from 'ui/shared/ad/adbutlerScript'; export function ad(): CspDev.DirectiveDescriptor { if (!isSelfHosted()) { @@ -12,6 +15,7 @@ export function ad(): CspDev.DirectiveDescriptor { 'coinzilla.com', '*.coinzilla.com', 'request-global.czilladx.com', + '*.slise.xyz', ], 'frame-src': [ 'request-global.czilladx.com', @@ -19,10 +23,9 @@ export function ad(): CspDev.DirectiveDescriptor { 'script-src': [ 'coinzillatag.com', 'servedbyadbutler.com', - // what hash is this? - '\'sha256-wMOeDjJaOTjCfNjluteV+tSqHW547T89sgxd8W6tQJM=\'', - // what hash is this? - '\'sha256-FcyIn1h7zra8TVnnRhYrwrplxJW7dpD5TV7kP2AG/kI=\'', + `'sha256-${ Base64.stringify(sha256(connectAdbutler)) }'`, + `'sha256-${ Base64.stringify(sha256(placeAd)) }'`, + '*.slise.xyz', ], 'img-src': [ 'servedbyadbutler.com', diff --git a/mocks/tokens/tokenInfo.ts b/mocks/tokens/tokenInfo.ts index 9c5881f03a..c2e5e520aa 100644 --- a/mocks/tokens/tokenInfo.ts +++ b/mocks/tokens/tokenInfo.ts @@ -2,6 +2,7 @@ import type { TokenCounters, TokenInfo } from 'types/api/token'; export const tokenInfo: TokenInfo = { address: '0x55d536e4d6c1993d8ef2e2a4ef77f02088419420', + circulating_market_cap: '117629601.61913824', decimals: '18', exchange_rate: '2.0101', holders: '46554', @@ -19,6 +20,7 @@ export const tokenCounters: TokenCounters = { export const tokenInfoERC20a: TokenInfo = { address: '0xb2a90505dc6680a7a695f7975d0d32EeF610f456', + circulating_market_cap: '117268489.23970924', decimals: '18', exchange_rate: null, holders: '23', @@ -31,6 +33,7 @@ export const tokenInfoERC20a: TokenInfo = { export const tokenInfoERC20b: TokenInfo = { address: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A7', + circulating_market_cap: '115060192.36105014', decimals: '6', exchange_rate: '0.982', holders: '17', @@ -43,6 +46,7 @@ export const tokenInfoERC20b: TokenInfo = { export const tokenInfoERC20c: TokenInfo = { address: '0xc1116c98ba622a6218433fF90a2E40DEa482d7A7', + circulating_market_cap: null, decimals: '18', exchange_rate: '1328.89', holders: '17', @@ -55,6 +59,7 @@ export const tokenInfoERC20c: TokenInfo = { export const tokenInfoERC20d: TokenInfo = { address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195', + circulating_market_cap: null, decimals: '18', exchange_rate: null, holders: '102625', @@ -67,6 +72,7 @@ export const tokenInfoERC20d: TokenInfo = { export const tokenInfoERC20LongSymbol: TokenInfo = { address: '0xCc7bb2D219A0FC08033E130629C2B854b7bA9195', + circulating_market_cap: '112855875.75888918', decimals: '18', exchange_rate: '1328.89', holders: '102625', @@ -79,6 +85,7 @@ export const tokenInfoERC20LongSymbol: TokenInfo = { export const tokenInfoERC721a: TokenInfo = { address: '0xDe7cAc71E072FCBd4453E5FB3558C2684d1F88A0', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '7', @@ -91,6 +98,7 @@ export const tokenInfoERC721a: TokenInfo = { export const tokenInfoERC721b: TokenInfo = { address: '0xA8d5C7beEA8C9bB57f5fBa35fB638BF45550b11F', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '2', @@ -103,6 +111,7 @@ export const tokenInfoERC721b: TokenInfo = { export const tokenInfoERC721c: TokenInfo = { address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4992', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '12', @@ -115,6 +124,7 @@ export const tokenInfoERC721c: TokenInfo = { export const tokenInfoERC721LongSymbol: TokenInfo = { address: '0x47646F1d7dc4Dd2Db5a41D092e2Cf966e27A4992', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '12', @@ -127,6 +137,7 @@ export const tokenInfoERC721LongSymbol: TokenInfo = { export const tokenInfoERC1155a: TokenInfo = { address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '22', @@ -139,6 +150,7 @@ export const tokenInfoERC1155a: TokenInfo = { export const tokenInfoERC1155b: TokenInfo = { address: '0xf4b71b179132ad457f6bcae2a55efa9e4b26eefc', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '100', @@ -151,6 +163,7 @@ export const tokenInfoERC1155b: TokenInfo = { export const tokenInfoERC1155WithoutName: TokenInfo = { address: '0x4b333DEd10c7ca855EA2C8D4D90A0a8b73788c8e', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '22', diff --git a/mocks/tokens/tokenTransfer.ts b/mocks/tokens/tokenTransfer.ts index 07056de289..bab120adb0 100644 --- a/mocks/tokens/tokenTransfer.ts +++ b/mocks/tokens/tokenTransfer.ts @@ -23,6 +23,7 @@ export const erc20: TokenTransfer = { }, token: { address: '0x55d536e4d6c1993d8ef2e2a4ef77f02088419420', + circulating_market_cap: '117629601.61913824', decimals: '18', exchange_rate: null, holders: '46554', @@ -67,6 +68,7 @@ export const erc721: TokenTransfer = { }, token: { address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '63090', @@ -110,6 +112,7 @@ export const erc1155A: TokenTransfer = { }, token: { address: '0xF56b7693E4212C584de4a83117f805B8E89224CB', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '1', diff --git a/mocks/txs/state.ts b/mocks/txs/state.ts index c49b2c548c..ff47b1f972 100644 --- a/mocks/txs/state.ts +++ b/mocks/txs/state.ts @@ -24,6 +24,7 @@ export const mintToken: TxStateChange = { is_miner: false, token: { address: '0x8977EA6C55e878125d1bF3433EBf72138B7a4543', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '9191', @@ -60,6 +61,7 @@ export const receiveMintedToken: TxStateChange = { is_miner: false, token: { address: '0x8977EA6C55e878125d1bF3433EBf72138B7a4543', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '9191', @@ -89,6 +91,7 @@ export const transfer1155Token: TxStateChange = { is_miner: false, token: { address: '0x56Cc277717106E528A9FcC2CD342Ff98db758041', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '50413', diff --git a/package.json b/package.json index e8187be53e..719667fc88 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@sentry/nextjs": "^7.12.1", "@sentry/react": "^7.24.0", "@sentry/tracing": "^7.24.0", + "@slise/embed-react": "^2.2.0", "@tanstack/react-query": "^4.0.10", "@tanstack/react-query-devtools": "^4.0.10", "@types/papaparse": "^5.3.5", @@ -49,6 +50,7 @@ "@web3modal/react": "^2.4.1", "bignumber.js": "^9.1.0", "chakra-react-select": "^4.4.3", + "crypto-js": "^4.1.1", "d3": "^7.6.1", "dayjs": "^1.11.5", "dom-to-image": "^2.6.0", @@ -87,6 +89,7 @@ "@svgr/webpack": "^6.5.1", "@testing-library/react": "^13.4.0", "@total-typescript/ts-reset": "^0.3.7", + "@types/crypto-js": "^4.1.1", "@types/csp-dev": "^1.0.0", "@types/d3": "^7.4.0", "@types/dom-to-image": "^2.6.4", diff --git a/stubs/token.ts b/stubs/token.ts index 6502cef11f..9ae80fc903 100644 --- a/stubs/token.ts +++ b/stubs/token.ts @@ -8,6 +8,7 @@ import { generateListStub } from './utils'; export const TOKEN_INFO_ERC_20: TokenInfo<'ERC-20'> = { address: ADDRESS_HASH, + circulating_market_cap: '117629601.61913824', decimals: '18', exchange_rate: '0.999997', holders: '16026', @@ -20,11 +21,13 @@ export const TOKEN_INFO_ERC_20: TokenInfo<'ERC-20'> = { export const TOKEN_INFO_ERC_721: TokenInfo<'ERC-721'> = { ...TOKEN_INFO_ERC_20, + circulating_market_cap: null, type: 'ERC-721', }; export const TOKEN_INFO_ERC_1155: TokenInfo<'ERC-1155'> = { ...TOKEN_INFO_ERC_20, + circulating_market_cap: null, type: 'ERC-1155', }; diff --git a/types/api/token.ts b/types/api/token.ts index 0f07197d37..6617ab59db 100644 --- a/types/api/token.ts +++ b/types/api/token.ts @@ -13,6 +13,7 @@ export interface TokenInfo { exchange_rate: string | null; total_supply: string | null; icon_url: string | null; + circulating_market_cap: string | null; } export interface TokenCounters { diff --git a/types/api/txStateChanges.ts b/types/api/txStateChanges.ts index 9bc9db5be6..459f196aff 100644 --- a/types/api/txStateChanges.ts +++ b/types/api/txStateChanges.ts @@ -45,7 +45,6 @@ export type TxStateChanges = { items: Array; next_page_params: { items_count: number; - // ??? state_changes: null; }; }; diff --git a/types/client/adButlerConfig.ts b/types/client/adButlerConfig.ts new file mode 100644 index 0000000000..39dcb385b7 --- /dev/null +++ b/types/client/adButlerConfig.ts @@ -0,0 +1,5 @@ +export type AdButlerConfig = { + id: string; + width: string; + height: string; +} diff --git a/ui/address/AddressContract.tsx b/ui/address/AddressContract.tsx index 4820becc98..32bd6e8baf 100644 --- a/ui/address/AddressContract.tsx +++ b/ui/address/AddressContract.tsx @@ -2,7 +2,6 @@ import React from 'react'; import type { RoutedSubTab } from 'ui/shared/Tabs/types'; -import { ContractContextProvider } from 'ui/address/contract/context'; import RoutedTabs from 'ui/shared/Tabs/RoutedTabs'; import Web3ModalProvider from 'ui/shared/Web3ModalProvider'; @@ -15,17 +14,17 @@ const TAB_LIST_PROPS = { columnGap: 3, }; -const AddressContract = ({ addressHash, tabs }: Props) => { +const AddressContract = ({ tabs }: Props) => { const fallback = React.useCallback(() => { const noProviderTabs = tabs.filter(({ id }) => id === 'contact_code'); - return ; + return ( + + ); }, [ tabs ]); return ( - - - + ); }; diff --git a/ui/address/contract/ContractCode.tsx b/ui/address/contract/ContractCode.tsx index 02ad6c20a7..2604d082d5 100644 --- a/ui/address/contract/ContractCode.tsx +++ b/ui/address/contract/ContractCode.tsx @@ -212,16 +212,10 @@ const ContractCode = ({ addressHash, noSocket }: Props) => { isLoading={ isPlaceholderData } /> ) } - { data?.source_code && ( + { data?.is_verified && ( ) } { data?.compiler_settings ? ( diff --git a/ui/address/contract/ContractSourceCode.tsx b/ui/address/contract/ContractSourceCode.tsx index 4ae5bd8016..ec39abff01 100644 --- a/ui/address/contract/ContractSourceCode.tsx +++ b/ui/address/contract/ContractSourceCode.tsx @@ -1,37 +1,94 @@ -import { Flex, Skeleton, Text, Tooltip } from '@chakra-ui/react'; +import { Box, Flex, Select, Skeleton, Text, Tooltip } from '@chakra-ui/react'; import { route } from 'nextjs-routes'; import React from 'react'; import type { SmartContract } from 'types/api/contract'; +import type { ArrayElement } from 'types/utils'; +import useApiQuery from 'lib/api/useApiQuery'; +import * as stubs from 'stubs/contract'; import CopyToClipboard from 'ui/shared/CopyToClipboard'; import LinkInternal from 'ui/shared/LinkInternal'; import CodeEditor from 'ui/shared/monaco/CodeEditor'; import formatFilePath from 'ui/shared/monaco/utils/formatFilePath'; +const SOURCE_CODE_OPTIONS = [ + { id: 'primary', label: 'Proxy' } as const, + { id: 'secondary', label: 'Implementation' } as const, +]; +type SourceCodeType = ArrayElement['id']; + +function getEditorData(contractInfo: SmartContract | undefined) { + if (!contractInfo || !contractInfo.source_code) { + return undefined; + } + + const defaultName = contractInfo.is_vyper_contract ? '/index.vy' : '/index.sol'; + return [ + { file_path: formatFilePath(contractInfo.file_path || defaultName), source_code: contractInfo.source_code }, + ...(contractInfo.additional_sources || []).map((source) => ({ ...source, file_path: formatFilePath(source.file_path) })), + ]; +} + interface Props { - data: string; - hasSol2Yml: boolean; address?: string; - isViper: boolean; - filePath?: string; - additionalSource?: SmartContract['additional_sources']; - remappings?: Array; - isLoading?: boolean; + implementationAddress?: string; } -const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, additionalSource, remappings, isLoading }: Props) => { +const ContractSourceCode = ({ address, implementationAddress }: Props) => { + const [ sourceType, setSourceType ] = React.useState('primary'); + + const primaryContractQuery = useApiQuery('contract', { + pathParams: { hash: address }, + queryOptions: { + enabled: Boolean(address), + refetchOnMount: false, + placeholderData: stubs.CONTRACT_CODE_VERIFIED, + }, + }); + + const secondaryContractQuery = useApiQuery('contract', { + pathParams: { hash: implementationAddress }, + queryOptions: { + enabled: Boolean(implementationAddress), + refetchOnMount: false, + placeholderData: stubs.CONTRACT_CODE_VERIFIED, + }, + }); + + const isLoading = implementationAddress ? + primaryContractQuery.isPlaceholderData || secondaryContractQuery.isPlaceholderData : + primaryContractQuery.isPlaceholderData; + + const primaryEditorData = React.useMemo(() => { + return getEditorData(primaryContractQuery.data); + }, [ primaryContractQuery.data ]); + + const secondaryEditorData = React.useMemo(() => { + return getEditorData(secondaryContractQuery.data); + }, [ secondaryContractQuery.data ]); + + const activeContract = sourceType === 'secondary' ? secondaryContractQuery.data : primaryContractQuery.data; + const activeContractData = sourceType === 'secondary' ? secondaryEditorData : primaryEditorData; + const heading = ( Contract source code - ({ isViper ? 'Vyper' : 'Solidity' }) + ({ activeContract?.is_vyper_contract ? 'Vyper' : 'Solidity' }) ); - const diagramLink = hasSol2Yml && address ? ( + const diagramLinkAddress = (() => { + if (!activeContract?.can_be_visualized_via_sol2uml) { + return; + } + return sourceType === 'secondary' ? implementationAddress : address; + })(); + + const diagramLink = diagramLinkAddress ? ( @@ -39,27 +96,66 @@ const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, addi + ) : ; + + const copyToClipboard = activeContractData?.length === 1 ? + : + null; + + const handleSelectChange = React.useCallback((event: React.ChangeEvent) => { + setSourceType(event.target.value as SourceCodeType); + }, []); + + const editorSourceTypeSelector = !secondaryContractQuery.isPlaceholderData && secondaryContractQuery.data?.source_code ? ( + ) : null; - const editorData = React.useMemo(() => { - const defaultName = isViper ? '/index.vy' : '/index.sol'; - return [ - { file_path: formatFilePath(filePath || defaultName), source_code: data }, - ...(additionalSource || []).map((source) => ({ ...source, file_path: formatFilePath(source.file_path) })) ]; - }, [ additionalSource, data, filePath, isViper ]); + const content = (() => { + if (isLoading) { + return ; + } - const copyToClipboard = editorData.length === 1 ? - : - null; + if (!primaryEditorData) { + return null; + } + + return ( + <> + + + + { secondaryEditorData && ( + + + + ) } + + ); + })(); + + if (!primaryEditorData) { + return null; + } return (
{ heading } + { editorSourceTypeSelector } { diagramLink } { copyToClipboard } - { isLoading ? : } + { content }
); }; diff --git a/ui/address/contract/ContractWrite.tsx b/ui/address/contract/ContractWrite.tsx index ddbe20a58e..529c9d21a0 100644 --- a/ui/address/contract/ContractWrite.tsx +++ b/ui/address/contract/ContractWrite.tsx @@ -9,12 +9,12 @@ import ContractMethodsAccordion from 'ui/address/contract/ContractMethodsAccordi import ContentLoader from 'ui/shared/ContentLoader'; import DataFetchAlert from 'ui/shared/DataFetchAlert'; -import { useContractContext } from './context'; import ContractConnectWallet from './ContractConnectWallet'; import ContractCustomAbiAlert from './ContractCustomAbiAlert'; import ContractImplementationAddress from './ContractImplementationAddress'; import ContractMethodCallable from './ContractMethodCallable'; import ContractWriteResult from './ContractWriteResult'; +import useContractAbi from './useContractAbi'; import { getNativeCoinValue } from './utils'; interface Props { @@ -39,18 +39,7 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => { }, }); - const { contractInfo, customInfo, proxyInfo } = useContractContext(); - const abi = (() => { - if (isProxy) { - return proxyInfo?.abi; - } - - if (isCustomAbi) { - return customInfo?.abi; - } - - return contractInfo?.abi; - })(); + const contractAbi = useContractAbi({ addressHash, isProxy, isCustomAbi }); const handleMethodFormSubmit = React.useCallback(async(item: SmartContractWriteMethod, args: Array>) => { if (!isConnected) { @@ -61,7 +50,7 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => { await switchNetworkAsync?.(Number(config.network.id)); } - if (!abi) { + if (!contractAbi) { throw new Error('Something went wrong. Try again later.'); } @@ -84,14 +73,14 @@ const ContractWrite = ({ addressHash, isProxy, isCustomAbi }: Props) => { const hash = await walletClient?.writeContract({ args: _args, - abi: abi, + abi: contractAbi, functionName: methodName, address: addressHash as `0x${ string }`, value: value as undefined, }); return { hash }; - }, [ isConnected, chain, abi, walletClient, addressHash, switchNetworkAsync ]); + }, [ isConnected, chain, contractAbi, walletClient, addressHash, switchNetworkAsync ]); const renderContent = React.useCallback((item: SmartContractWriteMethod, index: number, id: number) => { return ( diff --git a/ui/address/contract/context.tsx b/ui/address/contract/useContractAbi.tsx similarity index 53% rename from ui/address/contract/context.tsx rename to ui/address/contract/useContractAbi.tsx index 226b0bbb9d..7f85bb4d8e 100644 --- a/ui/address/contract/context.tsx +++ b/ui/address/contract/useContractAbi.tsx @@ -1,29 +1,18 @@ import { useQueryClient } from '@tanstack/react-query'; +import type { Abi } from 'abitype'; import React from 'react'; import type { Address } from 'types/api/address'; -import type { SmartContract } from 'types/api/contract'; import useApiQuery, { getResourceKey } from 'lib/api/useApiQuery'; -type ProviderProps = { +interface Params { addressHash?: string; - children: React.ReactNode; + isProxy?: boolean; + isCustomAbi?: boolean; } -type TContractContext = { - contractInfo: SmartContract | undefined; - proxyInfo: SmartContract | undefined; - customInfo: SmartContract | undefined; -}; - -const ContractContext = React.createContext({ - proxyInfo: undefined, - contractInfo: undefined, - customInfo: undefined, -}); - -export function ContractContextProvider({ addressHash, children }: ProviderProps) { +export default function useContractAbi({ addressHash, isProxy, isCustomAbi }: Params): Abi | undefined { const queryClient = useQueryClient(); const { data: contractInfo } = useApiQuery('contract', { @@ -55,23 +44,15 @@ export function ContractContextProvider({ addressHash, children }: ProviderProps }, }); - const value = React.useMemo(() => ({ - proxyInfo, - contractInfo, - customInfo, - } as TContractContext), [ proxyInfo, contractInfo, customInfo ]); + return React.useMemo(() => { + if (isProxy) { + return proxyInfo?.abi ?? undefined; + } - return ( - - { children } - - ); -} + if (isCustomAbi) { + return customInfo; + } -export function useContractContext() { - const context = React.useContext(ContractContext); - if (context === undefined) { - throw new Error('useContractContext must be used within a ContractContextProvider'); - } - return context; + return contractInfo?.abi ?? undefined; + }, [ contractInfo?.abi, customInfo, isCustomAbi, isProxy, proxyInfo?.abi ]); } diff --git a/ui/addressVerification/steps/AddressVerificationStepSignature.tsx b/ui/addressVerification/steps/AddressVerificationStepSignature.tsx index 03599cd912..f8bfbe9707 100644 --- a/ui/addressVerification/steps/AddressVerificationStepSignature.tsx +++ b/ui/addressVerification/steps/AddressVerificationStepSignature.tsx @@ -94,20 +94,25 @@ const AddressVerificationStepSignature = ({ address, signingMessage, contractCre }, [ clearErrors ]); const handleOpenWeb3Modal = React.useCallback(() => { + clearErrors('root'); openWeb3Modal(); - }, [ openWeb3Modal ]); + }, [ clearErrors, openWeb3Modal ]); const handleWeb3SignClick = React.useCallback(() => { + clearErrors('root'); + if (!isConnected) { return setError('root', { type: 'manual', message: 'Please connect to your Web3 wallet first' }); } + const message = getValues('message'); signMessage({ message }); - }, [ getValues, signMessage, isConnected, setError ]); + }, [ clearErrors, isConnected, getValues, signMessage, setError ]); const handleManualSignClick = React.useCallback(() => { + clearErrors('root'); onSubmit(); - }, [ onSubmit ]); + }, [ clearErrors, onSubmit ]); const button = (() => { if (signMethod === 'manually') { diff --git a/ui/pages/Address.tsx b/ui/pages/Address.tsx index 5961a93c16..5245924a47 100644 --- a/ui/pages/Address.tsx +++ b/ui/pages/Address.tsx @@ -86,11 +86,11 @@ const AddressPageContent = () => { return 'Contract'; }, - component: , + component: , subTabs: contractTabs.map(tab => tab.id), } : undefined, ].filter(Boolean); - }, [ addressQuery.data, contractTabs, hash ]); + }, [ addressQuery.data, contractTabs ]); const tags = ( { return 'Contract'; }, - component: , + component: , subTabs: contractTabs.map(tab => tab.id), } : undefined, ].filter(Boolean); diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png index 200bd59e18..475bd2a4f8 100644 Binary files a/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png and b/ui/pages/__screenshots__/Token.pw.tsx_default_base-view-1.png differ diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png index 1f75b5ca1e..ac8a14e58b 100644 Binary files a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png and b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-base-view-1.png differ diff --git a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png index ce08a6b98e..5a94c5942d 100644 Binary files a/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png and b/ui/pages/__screenshots__/Token.pw.tsx_default_mobile-with-verified-info-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png index 45e005bc3e..dc0e82b0b7 100644 Binary files a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png and b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_default_base-view-mobile-1.png differ diff --git a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png index b96c58425b..4ec9742be8 100644 Binary files a/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png and b/ui/pages/__screenshots__/VerifiedContracts.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/ui/shared/Page/specs/DefaultView.tsx b/ui/shared/Page/specs/DefaultView.tsx index 55798373d5..79356eb261 100644 --- a/ui/shared/Page/specs/DefaultView.tsx +++ b/ui/shared/Page/specs/DefaultView.tsx @@ -16,6 +16,7 @@ const DefaultView = () => { const tokenData: TokenInfo = { address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29', + circulating_market_cap: '117629601.61913824', type: 'ERC-20', symbol: 'SHAAAAAAAAAAAAA', name: null, diff --git a/ui/shared/Page/specs/LongNameAndManyTags.tsx b/ui/shared/Page/specs/LongNameAndManyTags.tsx index 1eb9d6a50b..1db4d47a98 100644 --- a/ui/shared/Page/specs/LongNameAndManyTags.tsx +++ b/ui/shared/Page/specs/LongNameAndManyTags.tsx @@ -19,6 +19,7 @@ const LongNameAndManyTags = () => { const tokenData: TokenInfo = { address: '0xa77A39CC9680B10C00af5D4ABFc92e1F07406c64', + circulating_market_cap: null, decimals: null, exchange_rate: null, holders: '294', diff --git a/ui/shared/TokenSnippet/TokenSnippet.pw.tsx b/ui/shared/TokenSnippet/TokenSnippet.pw.tsx index b60300500e..9d43f80f51 100644 --- a/ui/shared/TokenSnippet/TokenSnippet.pw.tsx +++ b/ui/shared/TokenSnippet/TokenSnippet.pw.tsx @@ -12,6 +12,7 @@ test.use(devices['iPhone 13 Pro']); test('unnamed', async({ mount }) => { const data: TokenInfo = { address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29', + circulating_market_cap: '117629601.61913824', type: 'ERC-20', symbol: 'xDAI', name: null, @@ -33,6 +34,7 @@ test('unnamed', async({ mount }) => { test('named', async({ mount }) => { const data: TokenInfo = { address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29', + circulating_market_cap: '117629601.61913824', type: 'ERC-20', symbol: 'SHA', name: 'Shavuha token', @@ -55,6 +57,7 @@ test('with logo and long symbol', async({ mount, page }) => { const API_URL = 'https://example.com/logo.png'; const data: TokenInfo = { address: '0x363574E6C5C71c343d7348093D84320c76d5Dd29', + circulating_market_cap: '117629601.61913824', type: 'ERC-20', symbol: 'SHAAAAAAAAAAAAA', name: null, diff --git a/ui/shared/ad/AdBanner.tsx b/ui/shared/ad/AdBanner.tsx index 45a75601eb..798d4a9b07 100644 --- a/ui/shared/ad/AdBanner.tsx +++ b/ui/shared/ad/AdBanner.tsx @@ -8,6 +8,7 @@ import isSelfHosted from 'lib/isSelfHosted'; import AdbutlerBanner from './AdbutlerBanner'; import CoinzillaBanner from './CoinzillaBanner'; +import SliseBanner from './SliseBanner'; const AdBanner = ({ className, isLoading }: { className?: string; isLoading?: boolean }) => { const hasAdblockCookie = cookies.get(cookies.NAMES.ADBLOCK_DETECTED, useAppContext().cookies); @@ -16,14 +17,24 @@ const AdBanner = ({ className, isLoading }: { className?: string; isLoading?: bo return null; } - const content = appConfig.ad.adButlerOn ? : ; + const content = (() => { + if (appConfig.ad.adButlerOn) { + return ; + } + + if (appConfig.ad.sliseOn) { + return ; + } + + return ; + })(); return ( { content } diff --git a/ui/shared/ad/AdbutlerBanner.tsx b/ui/shared/ad/AdbutlerBanner.tsx index 0d13a95798..9491eaf652 100644 --- a/ui/shared/ad/AdbutlerBanner.tsx +++ b/ui/shared/ad/AdbutlerBanner.tsx @@ -3,29 +3,14 @@ import { Flex, chakra } from '@chakra-ui/react'; import Script from 'next/script'; import React from 'react'; -const scriptText1 = `if (!window.AdButler){(function(){var s = document.createElement("script"); s.async = true; s.type = "text/javascript";s.src = 'https://servedbyadbutler.com/app.js';var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(s, n);}());}`; -const scriptText2 = ` -var AdButler = AdButler || {}; AdButler.ads = AdButler.ads || []; - var abkw = window.abkw || ''; - const isMobile = window.matchMedia("only screen and (max-width: 760px)").matches; - if (isMobile) { - var plc539876 = window.plc539876 || 0; - document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_539876_'+plc539876+'">'; - document.getElementById("ad-banner").className = "ad-container mb-3"; - AdButler.ads.push({handler: function(opt){ AdButler.register(182226, 539876, [320,100], 'placement_539876_'+opt.place, opt); }, opt: { place: plc539876++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }}); - } else { - var plc523705 = window.plc523705 || 0; - document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_523705_'+plc523705+'">'; - AdButler.ads.push({handler: function(opt){ AdButler.register(182226, 523705, [728,90], 'placement_523705_'+opt.place, opt); }, opt: { place: plc523705++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }}); - } -`; +import { connectAdbutler, placeAd } from 'ui/shared/ad/adbutlerScript'; const AdbutlerBanner = ({ className }: { className?: string }) => { return (
- - + +
); }; diff --git a/ui/shared/ad/SliseBanner.tsx b/ui/shared/ad/SliseBanner.tsx new file mode 100644 index 0000000000..ba1e9061cf --- /dev/null +++ b/ui/shared/ad/SliseBanner.tsx @@ -0,0 +1,27 @@ +import { Flex, chakra } from '@chakra-ui/react'; +import { SliseAd } from '@slise/embed-react'; +import React from 'react'; + +const SliseBanner = ({ className }: { className?: string }) => { + + return ( + <> + + + + + + + + ); +}; + +export default chakra(SliseBanner); diff --git a/ui/shared/ad/adbutlerScript.ts b/ui/shared/ad/adbutlerScript.ts new file mode 100644 index 0000000000..86d5970e0f --- /dev/null +++ b/ui/shared/ad/adbutlerScript.ts @@ -0,0 +1,20 @@ +/* eslint-disable max-len */ +import appConfig from 'configs/app/config'; + +export const connectAdbutler = `if (!window.AdButler){(function(){var s = document.createElement("script"); s.async = true; s.type = "text/javascript";s.src = 'https://servedbyadbutler.com/app.js';var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(s, n);}());}`; + +export const placeAd = ` +var AdButler = AdButler || {}; AdButler.ads = AdButler.ads || []; + var abkw = window.abkw || ''; + const isMobile = window.matchMedia("only screen and (max-width: 760px)").matches; + if (isMobile) { + var plc${ appConfig.ad.adButlerConfigMobile?.id } = window.plc${ appConfig.ad.adButlerConfigMobile?.id } || 0; + document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+plc${ appConfig.ad.adButlerConfigMobile?.id }+'">'; + document.getElementById("ad-banner").className = "ad-container mb-3"; + AdButler.ads.push({handler: function(opt){ AdButler.register(182226, ${ appConfig.ad.adButlerConfigMobile?.id }, [${ appConfig.ad.adButlerConfigMobile?.width },${ appConfig.ad.adButlerConfigMobile?.height }], 'placement_${ appConfig.ad.adButlerConfigMobile?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigMobile?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }}); + } else { + var plc${ appConfig.ad.adButlerConfigDesktop?.id } = window.plc${ appConfig.ad.adButlerConfigDesktop?.id } || 0; + document.getElementById('ad-banner').innerHTML += '<'+'div id="placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+plc${ appConfig.ad.adButlerConfigDesktop?.id }+'">'; + AdButler.ads.push({handler: function(opt){ AdButler.register(182226, ${ appConfig.ad.adButlerConfigDesktop?.id }, [${ appConfig.ad.adButlerConfigDesktop?.width },${ appConfig.ad.adButlerConfigDesktop?.height }], 'placement_${ appConfig.ad.adButlerConfigDesktop?.id }_'+opt.place, opt); }, opt: { place: plc${ appConfig.ad.adButlerConfigDesktop?.id }++, keywords: abkw, domain: 'servedbyadbutler.com', click:'CLICK_MACRO_PLACEHOLDER' }}); + } +`; diff --git a/ui/token/TokenDetails.tsx b/ui/token/TokenDetails.tsx index 48d199b417..c50fe2c236 100644 --- a/ui/token/TokenDetails.tsx +++ b/ui/token/TokenDetails.tsx @@ -1,5 +1,6 @@ import { Box, Flex, Grid, Link, Skeleton } from '@chakra-ui/react'; import type { UseQueryResult } from '@tanstack/react-query'; +import BigNumber from 'bignumber.js'; import { useRouter } from 'next/router'; import React, { useCallback } from 'react'; import { scroller } from 'react-scroll'; @@ -65,17 +66,16 @@ const TokenDetails = ({ tokenQuery }: Props) => { const { exchange_rate: exchangeRate, total_supply: totalSupply, + circulating_market_cap: marketCap, decimals, symbol, type, } = tokenQuery.data || {}; - let marketcap; let totalSupplyValue; if (type === 'ERC-20') { const totalValue = totalSupply ? getCurrencyValue({ value: totalSupply, accuracy: 3, accuracyUsd: 2, exchangeRate, decimals }) : undefined; - marketcap = totalValue?.usd; totalSupplyValue = totalValue?.valueStr; } else { totalSupplyValue = Number(totalSupply).toLocaleString(); @@ -100,7 +100,7 @@ const TokenDetails = ({ tokenQuery }: Props) => {
) } - { marketcap && ( + { marketCap && ( { isLoading={ tokenQuery.isPlaceholderData } > - { `$${ marketcap }` } + { `$${ BigNumber(marketCap).toFormat() }` } ) } diff --git a/ui/tokens/TokensListItem.tsx b/ui/tokens/TokensListItem.tsx index 03c5f10c7e..b22384e27f 100644 --- a/ui/tokens/TokensListItem.tsx +++ b/ui/tokens/TokensListItem.tsx @@ -1,9 +1,9 @@ import { Flex, HStack, Grid, GridItem, Skeleton } from '@chakra-ui/react'; +import BigNumber from 'bignumber.js'; import React from 'react'; import type { TokenInfo } from 'types/api/token'; -import getCurrencyValue from 'lib/getCurrencyValue'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressLink from 'ui/shared/address/AddressLink'; import Tag from 'ui/shared/chakra/Tag'; @@ -29,17 +29,14 @@ const TokensTableItem = ({ const { address, - total_supply: totalSupply, exchange_rate: exchangeRate, type, name, symbol, - decimals, holders, + circulating_market_cap: marketCap, } = token; - const totalValue = totalSupply !== null ? getCurrencyValue({ value: totalSupply, accuracy: 3, accuracyUsd: 2, exchangeRate, decimals }) : undefined; - const tokenString = [ name, symbol && `(${ symbol })` ].filter(Boolean).join(' '); return ( @@ -74,10 +71,10 @@ const TokensTableItem = ({ { exchangeRate } ) } - { totalValue?.usd && ( + { marketCap && ( On-chain market cap - { totalValue.usd } + { BigNumber(marketCap).toFormat() } ) } diff --git a/ui/tokens/TokensTableItem.tsx b/ui/tokens/TokensTableItem.tsx index 3724c73e93..4a86e76e01 100644 --- a/ui/tokens/TokensTableItem.tsx +++ b/ui/tokens/TokensTableItem.tsx @@ -1,9 +1,9 @@ import { Box, Flex, Td, Tr, Skeleton } from '@chakra-ui/react'; +import BigNumber from 'bignumber.js'; import React from 'react'; import type { TokenInfo } from 'types/api/token'; -import getCurrencyValue from 'lib/getCurrencyValue'; import Address from 'ui/shared/address/Address'; import AddressAddToWallet from 'ui/shared/address/AddressAddToWallet'; import AddressLink from 'ui/shared/address/AddressLink'; @@ -29,17 +29,14 @@ const TokensTableItem = ({ const { address, - total_supply: totalSupply, exchange_rate: exchangeRate, type, name, symbol, - decimals, holders, + circulating_market_cap: marketCap, } = token; - const totalValue = totalSupply !== null ? getCurrencyValue({ value: totalSupply, accuracy: 3, accuracyUsd: 2, exchangeRate, decimals }) : undefined; - const tokenString = [ name, symbol && `(${ symbol })` ].filter(Boolean).join(' '); return ( @@ -81,7 +78,7 @@ const TokensTableItem = ({ - { totalValue?.usd && `$${ totalValue.usd }` } + { marketCap && `$${ BigNumber(marketCap).toFormat() }` } diff --git a/ui/tokens/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/ui/tokens/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png index 5fee8effc0..62d20a82b8 100644 Binary files a/ui/tokens/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png and b/ui/tokens/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png differ diff --git a/ui/tokens/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-1.png b/ui/tokens/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-1.png index 6dcc0f4bf3..bad339b75c 100644 Binary files a/ui/tokens/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-1.png and b/ui/tokens/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-1.png differ diff --git a/ui/tokens/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/ui/tokens/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-1.png index 2967ed48e3..f3a71a6599 100644 Binary files a/ui/tokens/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-1.png and b/ui/tokens/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-1.png differ diff --git a/ui/verifiedContracts/VerifiedContractsListItem.tsx b/ui/verifiedContracts/VerifiedContractsListItem.tsx index 7eb32a26c2..4f8619cf7a 100644 --- a/ui/verifiedContracts/VerifiedContractsListItem.tsx +++ b/ui/verifiedContracts/VerifiedContractsListItem.tsx @@ -13,6 +13,7 @@ import Address from 'ui/shared/address/Address'; import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressLink from 'ui/shared/address/AddressLink'; import Icon from 'ui/shared/chakra/Icon'; +import CopyToClipboard from 'ui/shared/CopyToClipboard'; import HashStringShorten from 'ui/shared/HashStringShorten'; import ListItemMobile from 'ui/shared/ListItemMobile/ListItemMobile'; @@ -34,6 +35,7 @@ const VerifiedContractsListItem = ({ data, isLoading }: Props) => { + Balance { appConfig.network.currency.symbol } diff --git a/ui/verifiedContracts/VerifiedContractsTableItem.tsx b/ui/verifiedContracts/VerifiedContractsTableItem.tsx index f328518373..fb548b6fe9 100644 --- a/ui/verifiedContracts/VerifiedContractsTableItem.tsx +++ b/ui/verifiedContracts/VerifiedContractsTableItem.tsx @@ -12,6 +12,7 @@ import dayjs from 'lib/date/dayjs'; import AddressIcon from 'ui/shared/address/AddressIcon'; import AddressLink from 'ui/shared/address/AddressLink'; import Icon from 'ui/shared/chakra/Icon'; +import CopyToClipboard from 'ui/shared/CopyToClipboard'; import HashStringShorten from 'ui/shared/HashStringShorten'; interface Props { @@ -31,9 +32,12 @@ const VerifiedContractsTableItem = ({ data, isLoading }: Props) => { - - - + + + + + + diff --git a/yarn.lock b/yarn.lock index f905cff819..6731e9f7b4 100755 --- a/yarn.lock +++ b/yarn.lock @@ -3928,6 +3928,13 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@slise/embed-react@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@slise/embed-react/-/embed-react-2.2.0.tgz#346bf34d375144a7f5173354c1672d3687fa8b47" + integrity sha512-btboJc24ABEg5ncbVnab+asKarp3kTSTdMHHcndrnkCDlXNQNSw3vL0Lv8tanqgE3Ogt51AF8QGEhWDNOZAcxQ== + dependencies: + react-script-hook "^1.7.2" + "@solana/buffer-layout@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" @@ -4671,6 +4678,11 @@ dependencies: "@types/node" "*" +"@types/crypto-js@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.1.1.tgz#602859584cecc91894eb23a4892f38cfa927890d" + integrity sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA== + "@types/csp-dev@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/csp-dev/-/csp-dev-1.0.0.tgz#59e2fd69f276988b349765c2f6a39ea0a4a1a161" @@ -6859,6 +6871,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-js@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" + integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== + css-box-model@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1" @@ -11550,6 +11567,11 @@ react-remove-scroll@^2.4.3, react-remove-scroll@^2.5.5: use-callback-ref "^1.3.0" use-sidecar "^1.1.2" +react-script-hook@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/react-script-hook/-/react-script-hook-1.7.2.tgz#ec130d67f9a25fcde57fbfd1faa87e5b97521948" + integrity sha512-fhyCEfXb94fag34UPRF0zry1XGwmVY+79iibWwTqAoOiCzYJQOYTiWJ7CnqglA9tMSV8g45cQpHCMcBwr7dwhA== + react-scroll@^1.8.7: version "1.8.7" resolved "https://registry.yarnpkg.com/react-scroll/-/react-scroll-1.8.7.tgz#8020035329efad00f03964e18aff6822137de3aa"