"Add chain" button in the header (#3084)

* redesign "add chain" button

* fix wallet detection

* [skip ci] update default value for ENV

* fix tests

* return button to the footer and add source to mixpanel event

* regcred from helmfile for review

* [skip ci] remove wallets env from values for demo stand

* use react-query to cache detection result

* add cookie to hide button in the topbar

* add prefix to svg ids to fix rabby icon

* update screenshot

* remove unnecessary comment

---------

Co-authored-by: Nick Zenchik <n.zenchik@gmail.com>
This commit is contained in:
tom goriunov
2025-11-17 18:15:21 +01:00
committed by GitHub
co-authored by Nick Zenchik
parent 670259eab9
commit 0c5a297834
60 changed files with 424 additions and 142 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ echo 🧿 Running svg formatter...
for file in `git diff --diff-filter=ACMRT --cached --name-only | grep ".svg\$"`
do
echo "Formatting $file"
./node_modules/.bin/svgo -q $file
./node_modules/.bin/svgo -q $file --config svgo.config.format.js
git add $file
done
+1 -1
View File
@@ -13,7 +13,7 @@ const wallets = ((): Array<WalletType> | undefined => {
const wallets = parseEnvJson<Array<WalletType>>(envValue)?.filter((type) => SUPPORTED_WALLETS.includes(type));
if (!wallets || wallets.length === 0) {
return [ 'metamask' ];
return [ 'metamask', 'rabby', 'coinbase', 'trust', 'okx', 'token_pocket' ];
}
return wallets;
-1
View File
@@ -63,7 +63,6 @@ frontend:
NEXT_PUBLIC_HOMEPAGE_CHARTS: "['daily_txs','coin_price','market_cap']"
NEXT_PUBLIC_NETWORK_RPC_URL: https://eth-sepolia.public.blastapi.io
NEXT_PUBLIC_NETWORK_EXPLORERS: "[{'title':'Bitquery','baseUrl':'https://explorer.bitquery.io/','paths':{'tx':'/goerli/tx','address':'/goerli/address','token':'/goerli/token','block':'/goerli/block'}},{'title':'Etherscan','logo':'https://github.com/blockscout/frontend-configs/blob/main/configs/explorer-logos/etherscan.png?raw=true','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address','token':'/token','block':'/block'}}]"
NEXT_PUBLIC_WEB3_WALLETS: "['token_pocket','coinbase','metamask']"
NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE: gradient_avatar
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED: true
NEXT_PUBLIC_USE_NEXT_JS_PROXY: true
+1 -1
View File
@@ -649,7 +649,7 @@ This feature is **enabled by default** with the `['metamask']` value. To switch
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_WEB3_WALLETS | `Array<'metamask' \| 'coinbase' \| 'token_pocket'>` | Array of Web3 wallets which will be used to add tokens or chain to. The first wallet which is enabled in user's browser will be shown. | - | `[ 'metamask' ]` | `[ 'coinbase' ]` | v1.10.0+ |
| NEXT_PUBLIC_WEB3_WALLETS | `Array<'metamask' \| 'coinbase' \| 'token_pocket' \| 'rabby' \| 'trust' \| 'okx'>` | Array of Web3 wallets which will be used to add tokens or chain to. The first wallet which is enabled in user's browser will be shown. | - | `[ 'metamask', 'rabby', 'coinbase', 'trust', 'okx', 'token_pocket' ]` | `[ 'coinbase' ]` | v1.10.0+ |
| NEXT_PUBLIC_WEB3_DISABLE_ADD_TOKEN_TO_WALLET | `boolean`| Set to `true` to hide icon "Add to your wallet" next to token addresses | - | - | `true` | v1.0.x+ |
&nbsp;
+2 -2
View File
@@ -1,12 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none">
<g clip-path="url(#a)" fill="currentColor">
<g clip-path="url(#ABI_slim_svg__a)" fill="currentColor">
<path d="M5.176 5.291a1.253 1.253 0 0 0-1.76.159L.29 9.2a1.247 1.247 0 0 0 0 1.6l3.125 3.75a1.25 1.25 0 1 0 1.919-1.6L2.876 10l2.459-2.949a1.25 1.25 0 0 0-.159-1.76Z"/>
<path d="M5.176 5.291a1.253 1.253 0 0 0-1.76.159L.29 9.2a1.247 1.247 0 0 0 0 1.6l3.125 3.75a1.25 1.25 0 1 0 1.919-1.6L2.876 10l2.459-2.949a1.25 1.25 0 0 0-.159-1.76Zm6.318-2.766a1.261 1.261 0 0 0-1.47.981l-2.5 12.5a1.247 1.247 0 0 0 1.23 1.494 1.249 1.249 0 0 0 1.223-1.006l2.5-12.5a1.246 1.246 0 0 0-.982-1.469Z"/>
<path d="M11.494 2.525a1.261 1.261 0 0 0-1.47.981l-2.5 12.5a1.247 1.247 0 0 0 1.23 1.494 1.249 1.249 0 0 0 1.223-1.006l2.5-12.5a1.246 1.246 0 0 0-.982-1.469ZM19.708 9.2l-3.124-3.75a1.249 1.249 0 1 0-1.92 1.601l2.46 2.95-2.46 2.948a1.25 1.25 0 1 0 1.92 1.602l3.124-3.75a1.246 1.246 0 0 0 0-1.601Z"/>
<path d="m19.708 9.2-3.124-3.75a1.249 1.249 0 1 0-1.92 1.601l2.46 2.95-2.46 2.948a1.25 1.25 0 1 0 1.92 1.602l3.124-3.75a1.246 1.246 0 0 0 0-1.601Z"/>
</g>
<defs>
<clipPath id="a">
<clipPath id="ABI_slim_svg__a">
<path fill="#fff" d="M0 0h20v20H0z"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

+2 -2
View File
@@ -1,10 +1,10 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#a)" fill="currentColor">
<g clip-path="url(#image_svg__a)" fill="currentColor">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.647 1h12.705A2.65 2.65 0 0 1 19 3.647v12.706A2.65 2.65 0 0 1 16.353 19H3.647A2.65 2.65 0 0 1 1 16.353V3.647A2.65 2.65 0 0 1 3.647 1Zm12.705 16.94a1.59 1.59 0 0 0 1.588-1.588l.001-1.87-2.647-2.206-3.192 2.66-5.77-5.246-4.273 4.747v1.915a1.59 1.59 0 0 0 1.588 1.588h12.705Zm-1.059-7.04 2.647 2.205.001-9.457a1.59 1.59 0 0 0-1.588-1.588H3.647A1.59 1.59 0 0 0 2.06 3.647v9.208L6.257 8.19l5.874 5.342 3.162-2.634Zm1.06-4.605a2.65 2.65 0 0 1-2.647 2.647 2.65 2.65 0 0 1-2.647-2.648 2.65 2.65 0 0 1 2.647-2.647 2.65 2.65 0 0 1 2.647 2.647Zm-1.059 0a1.59 1.59 0 1 0-1.588 1.588 1.59 1.59 0 0 0 1.588-1.588Z"/>
<path d="M19 3.647h.2-.2Zm-1.06 12.705h-.2.2Zm.001-1.87h.2v-.094l-.072-.06-.128.154Zm-2.647-2.206.128-.153-.128-.107-.128.107.128.153Zm-3.192 2.66-.134.148.129.117.133-.112-.128-.153ZM6.332 9.69l.134-.148-.149-.136-.134.15.148.134Zm-4.273 4.747-.149-.134-.051.057v.077h.2Zm15.881-1.333-.128.154.328.273v-.427h-.2ZM15.293 10.9l.128-.154-.128-.107-.128.107.128.154Zm2.648-7.252h.2-.2ZM2.06 12.855h-.2v.521l.349-.387-.15-.134ZM6.257 8.19l.134-.148-.149-.135-.134.15.149.133Zm5.874 5.342-.134.148.129.117.133-.112-.128-.153ZM16.351.8H3.648v.4h12.705V.8ZM19.2 3.647A2.85 2.85 0 0 0 16.352.8v.4A2.45 2.45 0 0 1 18.8 3.647h.4Zm0 12.706V3.647h-.4v12.706h.4ZM16.353 19.2a2.85 2.85 0 0 0 2.847-2.847h-.4a2.45 2.45 0 0 1-2.447 2.447v.4Zm-12.706 0h12.706v-.4H3.647v.4ZM.8 16.353A2.85 2.85 0 0 0 3.647 19.2v-.4A2.45 2.45 0 0 1 1.2 16.353H.8Zm0-12.706v12.706h.4V3.647H.8ZM3.647.8A2.85 2.85 0 0 0 .8 3.647h.4A2.45 2.45 0 0 1 3.647 1.2V.8ZM17.74 16.352a1.39 1.39 0 0 1-1.388 1.388v.4a1.79 1.79 0 0 0 1.788-1.788h-.4Zm.001-1.87v1.87h.4v-1.87h-.4Zm-2.575-2.052 2.647 2.205.256-.307-2.647-2.205-.256.307Zm-2.936 2.66 3.192-2.66-.256-.307-3.192 2.66.256.306ZM6.197 9.837l5.77 5.246.27-.296-5.771-5.246-.27.296Zm-3.99 4.733L6.48 9.823l-.297-.267-4.273 4.747.298.268Zm.052 1.78v-1.914h-.4v1.915h.4Zm1.388 1.39a1.39 1.39 0 0 1-1.388-1.39h-.4a1.79 1.79 0 0 0 1.788 1.79v-.4Zm12.705 0H3.647v.4h12.705v-.4Zm1.716-4.79-2.647-2.206-.256.307 2.647 2.206.256-.307Zm-.327-9.304v9.457h.4V3.647h-.4ZM16.353 2.26a1.39 1.39 0 0 1 1.388 1.388h.4a1.79 1.79 0 0 0-1.788-1.788v.4Zm-12.706 0h12.706v-.4H3.647v.4ZM2.26 3.647A1.39 1.39 0 0 1 3.647 2.26v-.4A1.79 1.79 0 0 0 1.86 3.647h.4Zm0 9.208V3.647h-.4v9.208h.4Zm3.849-4.798L1.91 12.721l.298.268 4.197-4.664-.297-.268Zm6.158 5.328L6.39 8.043l-.269.296 5.875 5.342.269-.296Zm2.899-2.64-3.162 2.634.256.307 3.162-2.634-.256-.307Zm-1.46-1.604a2.85 2.85 0 0 0 2.848-2.848h-.4a2.45 2.45 0 0 1-2.447 2.448v.4Zm-2.847-2.848a2.85 2.85 0 0 0 2.848 2.848v-.4a2.45 2.45 0 0 1-2.447-2.447h-.4Zm2.848-2.847a2.85 2.85 0 0 0-2.848 2.847h.4a2.45 2.45 0 0 1 2.448-2.447v-.4Zm2.847 2.847a2.85 2.85 0 0 0-2.847-2.847v.4a2.45 2.45 0 0 1 2.447 2.447h.4Zm-2.847-1.387a1.39 1.39 0 0 1 1.388 1.388h.4a1.79 1.79 0 0 0-1.788-1.788v.4Zm-1.389 1.388a1.39 1.39 0 0 1 1.389-1.388v-.4a1.79 1.79 0 0 0-1.789 1.788h.4Zm1.389 1.389a1.39 1.39 0 0 1-1.389-1.389h-.4a1.79 1.79 0 0 0 1.789 1.789v-.4Zm1.388-1.389a1.39 1.39 0 0 1-1.388 1.389v.4a1.79 1.79 0 0 0 1.788-1.789h-.4Z"/>
</g>
<defs>
<clipPath id="a">
<clipPath id="image_svg__a">
<path fill="#fff" d="M0 0h20v20H0z"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

+2 -2
View File
@@ -1,10 +1,10 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#a)" fill="currentColor" stroke="currentColor" stroke-width=".4">
<g clip-path="url(#raw_svg__a)" fill="currentColor" stroke="currentColor" stroke-width=".4">
<path d="M15.063 19H4.937A3.937 3.937 0 0 1 1 15.062V4.938A3.937 3.937 0 0 1 4.938 1h5.625v1.125H4.936a2.813 2.813 0 0 0-2.812 2.813v10.125a2.812 2.812 0 0 0 2.813 2.812h10.125a2.812 2.812 0 0 0 2.812-2.813V9.439H19v5.624A3.937 3.937 0 0 1 15.062 19Z"/>
<path d="M15.063 8.875a3.938 3.938 0 1 1 0-7.875 3.938 3.938 0 0 1 0 7.875Zm0-6.75a2.812 2.812 0 1 0 0 5.624 2.812 2.812 0 0 0 0-5.624Z"/>
</g>
<defs>
<clipPath id="a">
<clipPath id="raw_svg__a">
<path fill="#fff" d="M0 0h20v20H0z"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 693 B

+4 -4
View File
@@ -1,15 +1,15 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="10" cy="10" r="10" fill="url(#a)"/>
<g clip-path="url(#b)" fill="#fff" fill-opacity=".95">
<circle cx="10" cy="10" r="10" fill="url(#celenium_svg__a)"/>
<g clip-path="url(#celenium_svg__b)" fill="#fff" fill-opacity=".95">
<path d="M15.763 12.826c.119-.243.178-.365.158-.462a.292.292 0 0 0-.148-.199c-.088-.047-.244-.02-.554.033a6.409 6.409 0 0 1-5.632-1.786 6.409 6.409 0 0 1-1.785-5.631c.053-.31.08-.466.033-.554a.292.292 0 0 0-.199-.149c-.098-.02-.219.04-.462.159a6.417 6.417 0 1 0 8.589 8.589Z"/>
<path d="M15.9 10.817c.152-.054.229-.082.31-.152a.686.686 0 0 0 .163-.228c.04-.1.04-.183.043-.35a6.398 6.398 0 0 0-1.879-4.624 6.398 6.398 0 0 0-4.624-1.88c-.167.003-.25.004-.35.044a.685.685 0 0 0-.229.163c-.07.081-.097.158-.151.31a5.25 5.25 0 0 0 6.717 6.717Z"/>
</g>
<defs>
<linearGradient id="a" x1="17.5" y1="2" x2="0" y2="20" gradientUnits="userSpaceOnUse">
<linearGradient id="celenium_svg__a" x1="17.5" y1="2" x2="0" y2="20" gradientUnits="userSpaceOnUse">
<stop stop-color="#196E41"/>
<stop offset="1" stop-color="#092E1B"/>
</linearGradient>
<clipPath id="b">
<clipPath id="celenium_svg__b">
<rect x="3" y="3" width="14" height="14" rx="7" fill="#fff"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+2 -2
View File
@@ -1,7 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 23 20">
<path fill="url(#a)" d="M1.12 1.111v5.556h2.241V2.222h4.482V0H2.241a1.13 1.13 0 0 0-.793.325c-.21.209-.327.491-.327.786Zm20.168 5.556V1.11c0-.295-.118-.577-.328-.786A1.125 1.125 0 0 0 20.168 0h-5.603v2.222h4.482v4.445h2.24Zm-2.24 11.11h-4.483V20h5.603c.297 0 .582-.117.792-.326.21-.208.328-.49.328-.785v-5.556h-2.24v4.445ZM7.842 20v-2.222H3.361v-4.445h-2.24v5.556c0 .295.117.577.327.785.21.209.496.326.793.326h5.602ZM0 8.889h22.408v2.222H0V8.89Z"/>
<path fill="url(#solidity_scan_svg__a)" d="M1.12 1.111v5.556h2.241V2.222h4.482V0H2.241a1.13 1.13 0 0 0-.793.325c-.21.209-.327.491-.327.786Zm20.168 5.556V1.11c0-.295-.118-.577-.328-.786A1.125 1.125 0 0 0 20.168 0h-5.603v2.222h4.482v4.445h2.24Zm-2.24 11.11h-4.483V20h5.603c.297 0 .582-.117.792-.326.21-.208.328-.49.328-.785v-5.556h-2.24v4.445ZM7.842 20v-2.222H3.361v-4.445h-2.24v5.556c0 .295.117.577.327.785.21.209.496.326.793.326h5.602ZM0 8.889h22.408v2.222H0V8.89Z"/>
<defs>
<linearGradient id="a" x1="2.161" x2="21.104" y1="1.429" y2="18.729" gradientUnits="userSpaceOnUse">
<linearGradient id="solidity_scan_svg__a" x1="2.161" x2="21.104" y1="1.429" y2="18.729" gradientUnits="userSpaceOnUse">
<stop stop-color="#52FF00"/>
<stop offset="1" stop-color="#00EEFD"/>
</linearGradient>

Before

Width:  |  Height:  |  Size: 758 B

After

Width:  |  Height:  |  Size: 796 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

+2 -2
View File
@@ -1,9 +1,9 @@
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#a)">
<g clip-path="url(#gas_svg__a)">
<path d="M12 17h1v2H0v-2h1V2a1 1 0 0 1 1-1h9a1 1 0 0 1 1 1v8h2a2 2 0 0 1 2 2v4a1 1 0 0 0 2 0V9h-2a1 1 0 0 1-1-1V4.414l-1.657-1.657 1.414-1.414 4.95 4.95A.996.996 0 0 1 20 7v9a3 3 0 0 1-6 0v-4h-2v5Zm-9 0h7v-6H3v6ZM3 3v6h7V3H3Z" fill="currentColor"/>
</g>
<defs>
<clipPath id="a">
<clipPath id="gas_svg__a">
<path fill="#fff" d="M0 0h20v20H0z"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 485 B

+4 -4
View File
@@ -1,9 +1,9 @@
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="m5.29 9.053 3.723-3.828a.972.972 0 0 1 1.336.06 1.051 1.051 0 0 1 .058 1.394L8.4 8.751h14.37c.262 0 .514.107.7.3.188.191.294.454.294.729 0 .275-.106.537-.293.73a.976.976 0 0 1-.7.298H5.987a.969.969 0 0 1-.55-.17 1.02 1.02 0 0 1-.367-.46 1.062 1.062 0 0 1 .22-1.125Zm18.724 6a.97.97 0 0 1 .55.17c.163.111.291.271.367.46a1.062 1.062 0 0 1-.22 1.125l-3.737 3.841-.006.008a.997.997 0 0 1-.322.255.966.966 0 0 1-1.13-.197 1.06 1.06 0 0 1 .056-1.513l.008-.006 2.021-2.087H15.72c.103-.684.183-1.403 0-2.072l8.294.016Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="m5.29 9.053 3.723-3.828a.972.972 0 0 1 1.336.06 1.051 1.051 0 0 1 .058 1.394L8.4 8.751h14.37c.262 0 .514.107.7.3.188.191.294.454.294.729 0 .275-.106.537-.293.73a.976.976 0 0 1-.7.298H5.987a.969.969 0 0 1-.55-.17 1.02 1.02 0 0 1-.367-.46 1.062 1.062 0 0 1 .22-1.125Zm18.724 6a.97.97 0 0 1 .55.17c.163.111.291.271.367.46a1.062 1.062 0 0 1-.22 1.125l-3.737 3.841-.006.008a.997.997 0 0 1-.322.255.966.966 0 0 1-1.13-.197 1.06 1.06 0 0 1 .056-1.513l.008-.006 2.021-2.087H15.72c.103-.684.183-1.403 0-2.072l8.294.016ZM6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865c.067.077.104.181.104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865c.067.077.104.181.104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" fill="currentColor"/>
<path clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865c.067.077.104.181.104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865c.067.077.104.181.104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" stroke="currentColor" stroke-opacity=".2" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="m5.29 9.053 3.723-3.828a.972.972 0 0 1 1.336.06 1.051 1.051 0 0 1 .058 1.394L8.4 8.751h14.37c.262 0 .514.107.7.3.188.191.294.454.294.729 0 .275-.106.537-.293.73a.976.976 0 0 1-.7.298H5.987a.969.969 0 0 1-.55-.17 1.02 1.02 0 0 1-.367-.46 1.062 1.062 0 0 1 .22-1.125Zm18.724 6a.97.97 0 0 1 .55.17c.163.111.291.271.367.46a1.062 1.062 0 0 1-.22 1.125l-3.737 3.841-.006.008a.997.997 0 0 1-.322.255.966.966 0 0 1-1.13-.197 1.06 1.06 0 0 1 .056-1.513l.008-.006 2.021-2.087H15.72c.103-.684.183-1.403 0-2.072l8.294.016ZM6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865a.442.442 0 0 1 .104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865a.442.442 0 0 1 .104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" fill="currentColor"/>
<path clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865a.442.442 0 0 1 .104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path clip-rule="evenodd" d="M6.209 13.24a.67.67 0 0 1 .504-.24h4.277c.095 0 .186.043.252.12l2.496 2.865a.442.442 0 0 1 .104.29v6.55a.884.884 0 0 1-.209.578.67.67 0 0 1-.504.24H6.713a.67.67 0 0 1-.504-.24.884.884 0 0 1-.209-.579V13.82c0-.217.075-.426.209-.58Zm4.634.579h-4.13v9.005h6.416v-6.38l-2.286-2.625Z" stroke="currentColor" stroke-opacity=".2" stroke-linecap="round" stroke-linejoin="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.743 13.224c.206 0 .373.167.373.373v2.24h2.24a.373.373 0 1 1 0 .748h-2.613a.373.373 0 0 1-.374-.374v-2.614c0-.206.167-.373.374-.373Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.743 13.224c.206 0 .373.167.373.373v2.24h2.24a.373.373 0 1 1 0 .748h-2.613a.373.373 0 0 1-.374-.374v-2.614c0-.206.167-.373.374-.373Z" fill="currentColor"/>
<path clip-rule="evenodd" d="M10.743 13.224c.206 0 .373.167.373.373v2.24h2.24a.373.373 0 1 1 0 .748h-2.613a.373.373 0 0 1-.374-.374v-2.614c0-.206.167-.373.374-.373Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

+4 -4
View File
@@ -1,13 +1,13 @@
<svg viewBox="0 0 40 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.787 1.923a3.574 3.574 0 0 0-3.574 0l-14.706 8.49a3.574 3.574 0 0 0-1.788 3.096v16.982c0 1.277.682 2.457 1.788 3.095l14.706 8.49a3.574 3.574 0 0 0 3.574 0l14.706-8.49a3.574 3.574 0 0 0 1.788-3.095V13.509a3.574 3.574 0 0 0-1.788-3.095l-14.706-8.49Z" fill="url(#a)" stroke="#fff" stroke-width="1.92"/>
<path d="M18.693 2.755a2.614 2.614 0 0 1 2.614 0l14.706 8.49a2.614 2.614 0 0 1 1.308 2.264v16.982c0 .934-.499 1.797-1.308 2.264l-14.706 8.49a2.614 2.614 0 0 1-2.614 0l-14.706-8.49a2.614 2.614 0 0 1-1.308-2.265V13.51c0-.935.499-1.798 1.308-2.265l14.706-8.49Z" fill="url(#b)"/>
<path d="M21.787 1.923a3.574 3.574 0 0 0-3.574 0l-14.706 8.49a3.574 3.574 0 0 0-1.788 3.096v16.982c0 1.277.682 2.457 1.788 3.095l14.706 8.49a3.574 3.574 0 0 0 3.574 0l14.706-8.49a3.574 3.574 0 0 0 1.788-3.095V13.509a3.574 3.574 0 0 0-1.788-3.095l-14.706-8.49Z" fill="url(#merits_colored_svg__a)" stroke="#fff" stroke-width="1.92"/>
<path d="M18.693 2.755a2.614 2.614 0 0 1 2.614 0l14.706 8.49a2.614 2.614 0 0 1 1.308 2.264v16.982c0 .934-.499 1.797-1.308 2.264l-14.706 8.49a2.614 2.614 0 0 1-2.614 0l-14.706-8.49a2.614 2.614 0 0 1-1.308-2.265V13.51c0-.935.499-1.798 1.308-2.265l14.706-8.49Z" fill="url(#merits_colored_svg__b)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.33 15.164c0-.643-.522-1.164-1.164-1.164h-1.111c-.643 0-1.164.521-1.164 1.164v1.006c0 .642-.521 1.163-1.164 1.163h-.563c-.643 0-1.164.521-1.164 1.164v10.34c0 .642.521 1.163 1.164 1.163h1.11c.643 0 1.164-.521 1.164-1.164V18.497c0-.643.521-1.164 1.164-1.164h.564c.642 0 1.163-.52 1.163-1.163v-1.006Zm6.899 0c0-.643-.521-1.164-1.164-1.164h-1.11c-.643 0-1.164.521-1.164 1.164v1.006c0 .642.52 1.163 1.163 1.163h.444c.643 0 1.164.521 1.164 1.164v10.34c0 .642.52 1.163 1.163 1.163h1.111C27.48 30 28 29.479 28 28.836v-10.34c0-.642-.521-1.163-1.164-1.163h-.443a1.164 1.164 0 0 1-1.164-1.163v-1.006Zm-3.486 6.38c0-.642-.521-1.163-1.164-1.163h-1.11c-.643 0-1.164.521-1.164 1.164v4.149c0 .642.52 1.163 1.163 1.163h1.111c.643 0 1.164-.52 1.164-1.163v-4.15Z" fill="#fff"/>
<defs>
<linearGradient id="a" x1="20" y1="2" x2="20" y2="42" gradientUnits="userSpaceOnUse">
<linearGradient id="merits_colored_svg__a" x1="20" y1="2" x2="20" y2="42" gradientUnits="userSpaceOnUse">
<stop stop-color="#2C5282"/>
<stop offset="1" stop-color="#153967"/>
</linearGradient>
<linearGradient id="b" x1="20" y1="2" x2="20" y2="42" gradientUnits="userSpaceOnUse">
<linearGradient id="merits_colored_svg__b" x1="20" y1="2" x2="20" y2="42" gradientUnits="userSpaceOnUse">
<stop stop-color="#008BE4"/>
<stop offset="1" stop-color="#81C5F1"/>
</linearGradient>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+2 -2
View File
@@ -1,9 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<g clip-path="url(#a)">
<g clip-path="url(#minus_svg__a)">
<path fill="currentColor" d="M8.857 9H17a1 1 0 0 1 1 1v.286a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V10a1 1 0 0 1 1-1h5.857Z"/>
</g>
<defs>
<clipPath id="a">
<clipPath id="minus_svg__a">
<path fill="currentColor" d="M0 0h20v20H0z"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 343 B

After

Width:  |  Height:  |  Size: 365 B

+1 -1
View File
@@ -1,3 +1,3 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.24.061a.502.502 0 0 0-.48 0L1.238 5.835A.449.449 0 0 0 1 6.226v11.548c0 .161.09.31.238.391L11.76 23.94a.502.502 0 0 0 .48 0l10.522-5.774a.45.45 0 0 0 .238-.391V6.226a.45.45 0 0 0-.238-.391L12.24.06ZM1.957 17.513V6.487L12 .977l10.044 5.51v11.026L12 23.023l-10.043-5.51ZM8.174 6.566a.466.466 0 0 0-.478-.453.466.466 0 0 0-.479.453v7.625L5.28 8.687a.481.481 0 0 0-.53-.304.461.461 0 0 0-.402.447v6.793c0 .25.214.453.478.453a.466.466 0 0 0 .478-.453V11.62l1.938 5.503c.074.211.299.34.53.304a.461.461 0 0 0 .402-.447V6.566Zm3.578-2.938a.501.501 0 0 1 .49-.003l2.866 1.585a.44.44 0 0 1 .172.619.493.493 0 0 1-.654.163l-2.622-1.45L9.61 5.916v5.631h2.87c.263 0 .478.203.478.453s-.215.453-.479.453h-2.87v5.887c0 .25-.213.452-.478.452a.466.466 0 0 1-.478-.452V5.66c0-.158.088-.305.23-.387l2.87-1.645Zm4.05 2.472a.493.493 0 0 1 .652-.168l2.957 1.6c.23.123.31.4.178.617a.493.493 0 0 1-.652.169l-.72-.39v9.053c0 .25-.214.453-.478.453a.466.466 0 0 1-.478-.453v-9.57l-1.281-.693a.44.44 0 0 1-.178-.618Z" fill="currentColor"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.24.061a.502.502 0 0 0-.48 0L1.238 5.835A.449.449 0 0 0 1 6.226v11.548c0 .161.09.31.238.391L11.76 23.94a.502.502 0 0 0 .48 0l10.522-5.774a.45.45 0 0 0 .238-.391V6.226a.45.45 0 0 0-.238-.391L12.24.06ZM1.957 17.513V6.487L12 .977l10.044 5.51v11.026L12 23.023l-10.043-5.51ZM8.174 6.566a.466.466 0 0 0-.478-.453.466.466 0 0 0-.479.453v7.625L5.28 8.687a.481.481 0 0 0-.53-.304.461.461 0 0 0-.402.447v6.793c0 .25.214.453.478.453a.466.466 0 0 0 .478-.453V11.62l1.938 5.503a.48.48 0 0 0 .53.304.461.461 0 0 0 .402-.447V6.566Zm3.578-2.938a.501.501 0 0 1 .49-.003l2.866 1.585a.44.44 0 0 1 .172.619.493.493 0 0 1-.654.163l-2.622-1.45L9.61 5.916v5.631h2.87c.263 0 .478.203.478.453s-.215.453-.479.453h-2.87v5.887c0 .25-.213.452-.478.452a.466.466 0 0 1-.478-.452V5.66a.45.45 0 0 1 .23-.387l2.87-1.645Zm4.05 2.472a.493.493 0 0 1 .652-.168l2.957 1.6c.23.123.31.4.178.617a.493.493 0 0 1-.652.169l-.72-.39v9.053c0 .25-.214.453-.478.453a.466.466 0 0 1-.478-.453v-9.57l-1.281-.693a.44.44 0 0 1-.178-.618Z" fill="currentColor"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+2 -2
View File
@@ -1,9 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<g clip-path="url(#a)">
<g clip-path="url(#plus_svg__a)">
<path fill="currentColor" d="M7.857 9.052a1 1 0 0 0 1-1V3.195a1 1 0 0 1 1-1h.286a1 1 0 0 1 1 1v4.857a1 1 0 0 0 1 1H17a1 1 0 0 1 1 1v.286a1 1 0 0 1-1 1h-4.857a1 1 0 0 0-1 1v4.857a1 1 0 0 1-1 1h-.286a1 1 0 0 1-1-1v-4.857a1 1 0 0 0-1-1H3a1 1 0 0 1-1-1v-.286a1 1 0 0 1 1-1h4.857Z"/>
</g>
<defs>
<clipPath id="a">
<clipPath id="plus_svg__a">
<path fill="currentColor" d="M0 0h20v20H0z"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 505 B

After

Width:  |  Height:  |  Size: 525 B

+3 -5
View File
@@ -1,6 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2500 2500">
<rect fill="none"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#0052FF" d="M520.7 0h1458.5C2266.9 0 2500 250.8 2500 560.2v1379.6c0 309.4-233.1 560.2-520.7 560.2H520.7C233.1 2500 0 2249.2 0 1939.8V560.2C0 250.8 233.1 0 520.7 0z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFF" d="M1250 362.1c490.4 0 887.9 397.5 887.9 887.9s-397.5 887.9-887.9 887.9-887.9-397.5-887.9-887.9S759.6 362.1 1250 362.1z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#0052FF" d="M1031.3 966.2h437.3c36 0 65.1 31.4 65.1 70v427.5c0 38.7-29.2 70-65.1 70h-437.3c-36 0-65.1-31.4-65.1-70v-427.5c0-38.6 29.2-70 65.1-70z"/>
<svg viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.6 0H2.4A2.4 2.4 0 0 0 0 2.4v7.2A2.4 2.4 0 0 0 2.4 12h7.2A2.4 2.4 0 0 0 12 9.6V2.4A2.4 2.4 0 0 0 9.6 0Z" fill="#0052FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.781 6a4.219 4.219 0 1 0 8.438 0A4.219 4.219 0 0 0 1.78 6Zm3.14-1.36a.281.281 0 0 0-.28.282v2.156c0 .155.126.281.28.281h2.157a.281.281 0 0 0 .281-.28V4.921a.281.281 0 0 0-.28-.281H4.921Z" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 701 B

After

Width:  |  Height:  |  Size: 470 B

+2 -2
View File
@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<g clip-path="url(#a)">
<g clip-path="url(#metamask_svg__a)">
<path fill="#FF5C16" d="m17.763 17.7-3.864-1.15-2.914 1.742H8.952L6.037 16.55 2.175 17.7 1 13.736l1.175-4.402L1 5.613 2.175 1l6.034 3.605h3.519L17.763 1l1.175 4.612-1.175 3.721 1.175 4.402-1.175 3.966Z"/>
<path fill="#FF5C16" d="M2.176 1 8.21 4.608l-.24 2.476L2.176 1Zm3.862 12.737 2.655 2.022-2.655.791v-2.813Zm2.443-3.344-.51-3.307-3.267 2.249-.002-.001v.001l.01 2.315 1.325-1.257H8.48ZM17.763 1l-6.035 3.608.24 2.476L17.762 1ZM13.9 13.737l-2.656 2.022 2.656.791v-2.813Zm1.335-4.402v-.002l-.001.001-3.267-2.248-.51 3.307H13.9l1.325 1.257.01-2.315Z"/>
<path fill="#E34807" d="M6.037 16.55 2.175 17.7 1 13.737h5.037v2.814Zm2.443-6.159.738 4.781-1.023-2.658-3.485-.865 1.326-1.257H8.48Zm5.42 6.159 3.863 1.15 1.175-3.964H13.9v2.814Zm-2.442-6.159-.738 4.781 1.022-2.658 3.485-.865-1.326-1.257h-2.443Z"/>
@@ -9,7 +9,7 @@
<path fill="#E7EBF6" d="m6.038 16.548 2.916 1.744v-1.579l-.261-.956-2.655.791Zm7.863 0-2.917 1.744v-1.579l.262-.956 2.655.791Z"/>
</g>
<defs>
<clipPath id="a">
<clipPath id="metamask_svg__a">
<path fill="#fff" d="M1 1h18v17.366H1z"/>
</clipPath>
</defs>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

+4
View File
@@ -0,0 +1,4 @@
<svg viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="12" height="12" rx="2" ry="2" fill="#9AED2C"/>
<path d="M4.642 7.187c.089 0 .16.072.161.16V9.43a.162.162 0 0 1-.16.16H2.56a.162.162 0 0 1-.161-.16V7.348c0-.089.073-.161.161-.161h2.081Zm4.797 0c.088 0 .161.072.161.16V9.43a.162.162 0 0 1-.16.16H7.357a.162.162 0 0 1-.161-.16V7.348c0-.089.073-.161.161-.161h2.081ZM7.045 4.792c.088 0 .16.073.16.161v2.081a.162.162 0 0 1-.16.161H4.963a.162.162 0 0 1-.16-.16V4.952c0-.088.072-.161.16-.161h2.082ZM4.642 2.397c.089 0 .16.073.161.162v2.08a.162.162 0 0 1-.16.162H2.56a.162.162 0 0 1-.16-.161V2.559c0-.089.073-.162.161-.162h2.081Zm4.797 0c.088 0 .161.073.161.162v2.08a.162.162 0 0 1-.16.162H7.357a.162.162 0 0 1-.161-.161V2.559c0-.089.073-.162.161-.162h2.081Z" fill="#000"/>
</svg>

After

Width:  |  Height:  |  Size: 811 B

+24
View File
@@ -0,0 +1,24 @@
<svg viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.939 6.559C12.41 5.502 10.08 2.55 7.854 1.32 6.451.368 4.989.5 4.693.918c-.65.918 2.152 1.695 4.025 2.603a2.14 2.14 0 0 0-1.005.893C7.014 3.65 5.48 2.99 3.682 3.521c-1.212.358-2.22 1.2-2.61 2.475a.764.764 0 0 0-1.073.7.765.765 0 0 0 .764.765c.078 0 .323-.052.323-.052l3.903.028c-1.561 2.485-2.795 2.848-2.795 3.279 0 .43 1.18.313 1.624.153 2.121-.768 4.4-3.162 4.791-3.851 1.642.205 3.022.23 3.33-.46Z" fill="url(#rabby_svg__a)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.718 3.521c.087-.034.073-.163.05-.264-.056-.232-1.001-1.17-1.89-1.589-1.21-.572-2.1-.542-2.232-.279.246.507 1.389.983 2.583 1.48.509.212 1.027.428 1.49.652h-.001Z" fill="url(#rabby_svg__b)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.182 8.623a7.155 7.155 0 0 0-.835-.258c.335-.602.405-1.492.089-2.056-.444-.79-1.002-1.21-2.298-1.21-.712 0-2.63.24-2.665 1.847-.003.169 0 .323.012.466l3.504.025c-.473.752-.915 1.31-1.302 1.733.465.12.849.22 1.2.313l.961.249c.483-.353.938-.738 1.334-1.109Z" fill="url(#rabby_svg__c)"/>
<path d="M1.026 7.247c.143 1.22.835 1.699 2.247 1.84 1.413.142 2.224.047 3.303.146.901.082 1.706.543 2.004.383.269-.143.119-.66-.24-.993-.467-.43-1.112-.73-2.246-.836.226-.621.162-1.492-.189-1.966-.508-.686-1.445-.996-2.632-.86-1.239.141-2.427.754-2.247 2.286Z" fill="url(#rabby_svg__d)"/>
<defs>
<linearGradient id="rabby_svg__a" x1="3.541" y1="5.623" x2="11.841" y2="7.969" gradientUnits="userSpaceOnUse">
<stop stop-color="#8697FF"/>
<stop offset="1" stop-color="#ABB7FF"/>
</linearGradient>
<linearGradient id="rabby_svg__b" x1="10.44" y1="5.465" x2="4.434" y2="-.536" gradientUnits="userSpaceOnUse">
<stop stop-color="#8697FF"/>
<stop offset="1" stop-color="#5156D8" stop-opacity="0"/>
</linearGradient>
<linearGradient id="rabby_svg__c" x1="7.349" y1="8.833" x2="1.589" y2="5.532" gradientUnits="userSpaceOnUse">
<stop stop-color="#465EED"/>
<stop offset="1" stop-color="#8697FF" stop-opacity="0"/>
</linearGradient>
<linearGradient id="rabby_svg__d" x1="4.036" y1="5.561" x2="7.939" y2="10.504" gradientUnits="userSpaceOnUse">
<stop stop-color="#8898FF"/>
<stop offset=".984" stop-color="#6277F1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+14
View File
@@ -0,0 +1,14 @@
<svg viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M.6 1.733 5.91 0v12C2.116 10.4.6 7.333.6 5.6V1.733Z" fill="#0500FF"/>
<path d="M11.219 1.733 5.909 0v12c3.793-1.6 5.31-4.667 5.31-6.4V1.733Z" fill="url(#trust_svg__a)"/>
<defs>
<linearGradient id="trust_svg__a" x1="9.82" y1="-.836" x2="5.776" y2="11.829" gradientUnits="userSpaceOnUse">
<stop offset=".02" stop-color="#00F"/>
<stop offset=".08" stop-color="#0094FF"/>
<stop offset=".16" stop-color="#48FF91"/>
<stop offset=".42" stop-color="#0094FF"/>
<stop offset=".68" stop-color="#0038FF"/>
<stop offset=".9" stop-color="#0500FF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 702 B

+1
View File
@@ -18,6 +18,7 @@ export enum NAMES {
ADBLOCK_DETECTED = 'adblock_detected',
MIXPANEL_DEBUG = '_mixpanel_debug',
ADDRESS_NFT_DISPLAY_TYPE = 'address_nft_display_type',
HIDE_ADD_TO_WALLET_BUTTON = 'hide_add_to_wallet_button',
UUID = 'uuid',
SHOW_SCAM_TOKENS = 'show_scam_tokens',
}
+1
View File
@@ -51,6 +51,7 @@ Type extends EventTypes.ADD_TO_WALLET ? (
{
Wallet: WalletType;
Target: 'network';
Source: 'Footer' | 'Top bar' | 'Chain widget';
} | {
Wallet: WalletType;
Target: 'token';
+78
View File
@@ -0,0 +1,78 @@
import type { WalletType } from 'types/client/wallets';
import type { WalletProvider } from 'types/web3';
const isWalletProvider = (wallet: WalletType) => (provider: WalletProvider): boolean | undefined => {
switch (wallet) {
case 'rabby': {
return provider.isRabby;
}
case 'coinbase': {
return provider.isCoinbaseWallet;
}
case 'token_pocket': {
return provider.isTokenPocket;
}
case 'okx': {
return provider.isOkxWallet;
}
case 'trust': {
return provider.isTrustWallet;
}
case 'metamask': {
// some wallets (e.g TokenPocket, Liquality, etc) try to look like MetaMask but they are not (not even close)
// found this hack in wagmi repo
// https://github.com/wevm/wagmi/blob/8c35b7adccb4f92c4e0e36a76b970e9126053772/packages/core/src/connectors/injected.ts#L553
if (!provider.isMetaMask) {
return false;
}
if (!provider._events || !provider._state) {
return false;
}
// Other wallets that try to look like MetaMask
const flags = [
'isApexWallet',
'isAvalanche',
'isBitKeep',
'isBlockWallet',
'isKuCoinWallet',
'isMathWallet',
'isOkxWallet',
'isOKExWallet',
'isOneInchIOSWallet',
'isOneInchAndroidWallet',
'isOpera',
'isPhantom',
'isPortal',
'isRabby',
'isRainbow',
'isTokenPocket',
'isTokenary',
'isUniswapWallet',
'isZerion',
] ;
for (const flag of flags) {
if (provider[flag as keyof WalletProvider]) {
return false;
}
}
return true;
}
default:
return false;
}
};
export default function detectWallet(wallet: WalletType): { wallet: WalletType; provider: WalletProvider } | undefined {
if (!('ethereum' in window && window.ethereum)) {
return;
}
// if user has multiple wallets installed, they all are injected in the window.ethereum.providers array
// if user has only one wallet, the provider is injected in the window.ethereum directly
const providers = Array.isArray(window.ethereum.providers) ? window.ethereum.providers : [ window.ethereum ];
const provider = providers.find(isWalletProvider(wallet));
if (provider) {
return { wallet, provider };
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ function getParams(chainConfig: typeof config): AddEthereumChainParameter {
}
export default function useAddChain() {
const { wallet, provider } = useProvider();
const { data: { wallet, provider } = {} } = useProvider();
const { trackUsage } = useRewardsActivity();
const multichainContext = useMultichainContext();
+10 -3
View File
@@ -7,8 +7,13 @@ import useAddChain from './useAddChain';
import useProvider from './useProvider';
import useSwitchChain from './useSwitchChain';
export default function useAddChainClick() {
const { provider, wallet } = useProvider();
interface Props {
source: 'Footer' | 'Top bar' | 'Chain widget';
onSuccess?: () => void;
}
export default function useAddChainClick({ source, onSuccess }: Props) {
const { data: { wallet, provider } = {} } = useProvider();
const addChain = useAddChain();
const switchChain = useSwitchChain();
@@ -29,13 +34,15 @@ export default function useAddChainClick() {
mixpanel.logEvent(mixpanel.EventTypes.ADD_TO_WALLET, {
Target: 'network',
Wallet: wallet,
Source: source,
});
onSuccess?.();
} catch (error) {
toaster.error({
title: 'Error',
description: (error as Error)?.message || 'Something went wrong',
});
}
}, [ addChain, provider, wallet, switchChain ]);
}, [ addChain, provider, wallet, switchChain, source, onSuccess ]);
}
+71
View File
@@ -0,0 +1,71 @@
// https://eips.ethereum.org/EIPS/eip-6963
import React from 'react';
import type { WalletType } from 'types/client/wallets';
import type { WalletProvider } from 'types/web3';
interface EIP6963ProviderInfo {
uuid: string;
name: string;
icon: string;
rdns: string;
}
interface EIP6963ProviderDetail {
info: EIP6963ProviderInfo;
provider: WalletProvider;
}
const WALLET_RDNS_MAP: Record<WalletType, string> = {
metamask: 'io.metamask',
coinbase: 'com.coinbase.wallet',
token_pocket: 'io.tokenpocket',
rabby: 'io.rabby',
okx: 'com.okex.wallet',
trust: 'com.trustwallet.app',
} as const;
const DETECTED_PROVIDERS: Partial<Record<WalletType, WalletProvider | false>> = {};
export default function useDetectWalletEip6963() {
const detectionTimeoutRef = React.useRef<number | null>(null);
const handleAnnounceProviderEvent = React.useCallback((event: CustomEvent<EIP6963ProviderDetail>) => {
const wallet = Object.entries(WALLET_RDNS_MAP)
.find(([ , rdns ]) => rdns === event.detail.info.rdns)?.[0] as WalletType | undefined;
if (wallet && !DETECTED_PROVIDERS[wallet]) {
DETECTED_PROVIDERS[wallet] = event.detail.provider;
}
}, []);
const detect = React.useCallback((wallet: WalletType) => {
return new Promise<{ wallet: WalletType; provider: WalletProvider } | undefined>((resolve) => {
const provider = DETECTED_PROVIDERS[wallet];
if (provider) {
resolve({ wallet, provider });
return;
}
window.addEventListener('eip6963:announceProvider', handleAnnounceProviderEvent as EventListener);
window.dispatchEvent(new Event('eip6963:requestProvider'));
detectionTimeoutRef.current = window.setTimeout(() => {
const provider = DETECTED_PROVIDERS[wallet];
resolve(provider ? { wallet, provider } : undefined);
}, 200);
});
}, [ handleAnnounceProviderEvent ]);
React.useEffect(() => {
return () => {
window.removeEventListener('eip6963:announceProvider', handleAnnounceProviderEvent as EventListener);
};
}, [ handleAnnounceProviderEvent ]);
return React.useMemo(() => ({
detect,
}), [ detect ]);
}
+52 -64
View File
@@ -1,85 +1,73 @@
import React from 'react';
import { useQuery } from '@tanstack/react-query';
import { getFeaturePayload } from 'configs/app/features/types';
import type { WalletType } from 'types/client/wallets';
import type { WalletProvider } from 'types/web3';
import config from 'configs/app';
import { useMultichainContext } from 'lib/contexts/multichain';
import detectWallet from './detectWallet';
import useDetectWalletEip6963 from './useDetectWalletEip6963';
export default function useProvider() {
const [ provider, setProvider ] = React.useState<WalletProvider>();
const [ wallet, setWallet ] = React.useState<WalletType>();
const multichainContext = useMultichainContext();
const feature = (multichainContext?.chain.config ?? config).features.web3Wallet;
const wallets = getFeaturePayload(feature)?.wallets;
const initializeProvider = React.useMemo(() => async() => {
if (!feature.isEnabled || !wallets) {
return;
}
const { detect: detectWalletEip6963 } = useDetectWalletEip6963();
if (!('ethereum' in window && window.ethereum)) {
if (wallets.includes('metamask') && window.navigator.userAgent.includes('Firefox')) {
const { WindowPostMessageStream } = (await import('@metamask/post-message-stream'));
const { initializeProvider } = (await import('@metamask/providers'));
// workaround for MetaMask in Firefox
// Firefox blocks MetaMask injection script because of our CSP for 'script-src'
// so we have to inject it manually while the issue is not fixed
// https://github.com/MetaMask/metamask-extension/issues/3133#issuecomment-1025641185
const metamaskStream = new WindowPostMessageStream({
name: 'metamask-inpage',
target: 'metamask-contentscript',
});
// this will initialize the provider and set it as window.ethereum
initializeProvider({
connectionStream: metamaskStream as never,
shouldShimWeb3: true,
});
} else {
return useQuery({
queryKey: [ 'web3-wallet' ],
queryFn: async() => {
if (!feature.isEnabled || !wallets) {
return;
}
}
// have to check again in case provider was not set as window.ethereum in the previous step for MM in FF
// and also it makes typescript happy
if (!('ethereum' in window && window.ethereum)) {
return;
}
if (!('ethereum' in window && window.ethereum)) {
if (wallets.includes('metamask') && window.navigator.userAgent.includes('Firefox')) {
const { WindowPostMessageStream } = (await import('@metamask/post-message-stream'));
const { initializeProvider } = (await import('@metamask/providers'));
// if user has multiple wallets installed, they all are injected in the window.ethereum.providers array
// if user has only one wallet, the provider is injected in the window.ethereum directly
const providers = Array.isArray(window.ethereum.providers) ? window.ethereum.providers : [ window.ethereum ];
// workaround for MetaMask in Firefox
// Firefox blocks MetaMask injection script because of our CSP for 'script-src'
// so we have to inject it manually while the issue is not fixed
// https://github.com/MetaMask/metamask-extension/issues/3133#issuecomment-1025641185
const metamaskStream = new WindowPostMessageStream({
name: 'metamask-inpage',
target: 'metamask-contentscript',
});
for (const wallet of wallets) {
const provider = providers.find((provider) => {
return (
// some wallets (e.g TokenPocket, Liquality, etc) try to look like MetaMask but they are not (not even close)
// so we have to check in addition the presence of the provider._events property
// found this hack in wagmi repo
// https://github.com/wagmi-dev/wagmi/blob/399b9eab8cfd698b49bfaa8456598dad9b597e0e/packages/connectors/src/types.ts#L65
// for now it's the only way to distinguish them
(wallet === 'metamask' && provider.isMetaMask && Boolean(provider._events)) ||
(wallet === 'coinbase' && provider.isCoinbaseWallet) ||
(wallet === 'token_pocket' && provider.isTokenPocket)
);
});
if (provider) {
setProvider(provider);
setWallet(wallet);
break;
// this will initialize the provider and set it as window.ethereum
initializeProvider({
connectionStream: metamaskStream as never,
shouldShimWeb3: true,
});
} else {
return;
}
}
}
}, [ feature.isEnabled, wallets ]);
React.useEffect(() => {
initializeProvider();
}, [ initializeProvider ]);
// have to check again in case provider was not set as window.ethereum in the previous step for MM in FF
// and also it makes typescript happy
if (!('ethereum' in window && window.ethereum)) {
return;
}
return { provider, wallet };
for (const wallet of wallets) {
const detectedWalletEip6963 = await detectWalletEip6963(wallet);
if (detectedWalletEip6963) {
return detectedWalletEip6963;
}
const detectedWallet = detectWallet(wallet);
if (detectedWallet) {
return detectedWallet;
}
}
},
enabled: Boolean(feature.isEnabled || !wallets),
refetchOnMount: false,
staleTime: Infinity,
});
}
+1 -1
View File
@@ -15,7 +15,7 @@ function getParams(chainConfig: typeof config): { chainId: string } {
}
export default function useSwitchChain() {
const { wallet, provider } = useProvider();
const { data: { wallet, provider } = {} } = useProvider();
const multichainContext = useMultichainContext();
const chainConfig = multichainContext?.chain.config ?? config;
+1 -1
View File
@@ -8,7 +8,7 @@ import useProvider from './useProvider';
import useSwitchChain from './useSwitchChain';
export default function useSwitchOrAddChain() {
const { wallet, provider } = useProvider();
const { data: { wallet, provider } = {} } = useProvider();
const addChain = useAddChain();
const switchChain = useSwitchChain();
+18
View File
@@ -4,13 +4,31 @@ export const WALLETS_INFO: Record<Exclude<WalletType, 'none'>, WalletInfo> = {
metamask: {
name: 'MetaMask',
icon: 'wallets/metamask',
color: '#FF8D5D',
},
coinbase: {
name: 'Coinbase Wallet',
icon: 'wallets/coinbase',
color: '#0052FF',
},
token_pocket: {
name: 'TokenPocket',
icon: 'wallets/token-pocket',
color: '#2980FE',
},
rabby: {
name: 'Rabby',
icon: 'wallets/rabby',
color: '#8798FF',
},
okx: {
name: 'OKX Wallet',
icon: 'wallets/okx',
color: '#6DB809',
},
trust: {
name: 'Trust Wallet',
icon: 'wallets/trust',
color: '#2D77FF',
},
};
+1 -1
View File
@@ -24,7 +24,7 @@
"lint:tsc": "tsc -p ./tsconfig.json",
"lint:envs-validator:test": "cd ./deploy/tools/envs-validator && ./test.sh",
"prepare": "husky install",
"svg:format": "svgo -r ./icons",
"svg:format": "svgo -r --config svgo.config.format.js ./icons",
"svg:build-sprite": "./deploy/scripts/build_sprite.sh",
"test:pw": "./tools/scripts/pw.sh",
"test:pw:local": "export NODE_PATH=$(pwd)/node_modules && yarn test:pw",
@@ -10,6 +10,7 @@ const fixture: TestFixture<InjectMetaMaskProvider, { page: Page }> = async({ pag
window.ethereum = {
isMetaMask: true,
_events: {},
_state: {},
} as WalletProvider;
});
});
+3
View File
@@ -203,6 +203,9 @@
| "wallet"
| "wallets/coinbase"
| "wallets/metamask"
| "wallets/okx"
| "wallets/rabby"
| "wallets/token-pocket"
| "wallets/trust"
| "watchlist";
+22
View File
@@ -0,0 +1,22 @@
module.exports = {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
removeHiddenElems: false,
},
},
},
'removeDimensions',
// during formatting we need to prefix ids to avoid conflicts with other icons
// this is not the case for building the sprite
// so we use a separate config only with one additional plugin
'prefixIds',
],
js2svg: {
indent: 2,
pretty: true,
},
};
+4
View File
@@ -6,6 +6,9 @@ export const SUPPORTED_WALLETS = [
'metamask',
'coinbase',
'token_pocket',
'rabby',
'okx',
'trust',
] as const;
export type WalletType = ArrayElement<typeof SUPPORTED_WALLETS>;
@@ -13,4 +16,5 @@ export type WalletType = ArrayElement<typeof SUPPORTED_WALLETS>;
export interface WalletInfo {
name: string;
icon: IconName;
color: string;
}
+1 -1
View File
@@ -30,7 +30,7 @@ const ChainWidget = ({ data }: Props) => {
},
});
const handleAddToWalletClick = useAddChainClick();
const handleAddToWalletClick = useAddChainClick({ source: 'Chain widget' });
return (
<Box
+25 -7
View File
@@ -7,19 +7,37 @@ import { WALLETS_INFO } from 'lib/web3/wallets';
import { Button } from 'toolkit/chakra/button';
import IconSvg from 'ui/shared/IconSvg';
const feature = config.features.web3Wallet;
interface Props {
source: 'Footer' | 'Top bar';
onAddSuccess?: () => void;
}
const NetworkAddToWallet = () => {
const { provider, wallet } = useProvider();
const handleClick = useAddChainClick();
const NetworkAddToWallet = ({ source, onAddSuccess }: Props) => {
const { data: { wallet } = {} } = useProvider();
if (!provider || !wallet || !config.chain.rpcUrls.length || !feature.isEnabled) {
const handleClick = useAddChainClick({ source, onSuccess: onAddSuccess });
if (!wallet) {
return null;
}
const walletInfo = WALLETS_INFO[wallet];
return (
<Button variant="outline" size="sm" onClick={ handleClick }>
<IconSvg name={ WALLETS_INFO[wallet].icon } boxSize={ 5 }/>
<Button
variant="outline"
size="2xs"
borderWidth="1px"
fontWeight="500"
color={ walletInfo.color }
borderColor={ walletInfo.color }
onClick={ handleClick }
_hover={{
color: 'link.primary.hover',
borderColor: 'link.primary.hover',
}}
>
<IconSvg name={ walletInfo.icon } boxSize={ 3 }/>
Add { config.chain.name }
</Button>
);
+1 -1
View File
@@ -60,7 +60,7 @@ interface Props {
}
const AddressAddToWallet = ({ className, token, tokenId, isLoading, variant = 'icon', iconSize = 6 }: Props) => {
const { provider, wallet } = useProvider();
const { data: { wallet, provider } = {} } = useProvider();
const switchOrAddChain = useSwitchOrAddChain();
const isMobile = useIsMobile();
const { trackUsage } = useRewardsActivity();
+5 -3
View File
@@ -103,15 +103,17 @@ const Footer = () => {
const renderNetworkInfo = React.useCallback((gridArea?: GridProps['gridArea']) => {
return (
<Flex
alignItems="center"
gridArea={ gridArea }
flexWrap="wrap"
columnGap={ 8 }
rowGap={ 6 }
justifyContent="flex-start"
columnGap={ 3 }
rowGap={ 2 }
mb={{ base: 5, lg: 10 }}
_empty={{ display: 'none' }}
>
{ !config.UI.indexingAlert.intTxs.isHidden && <IntTxsIndexingStatus/> }
<NetworkAddToWallet/>
<NetworkAddToWallet source="Footer"/>
</Flex>
);
}, []);
+4 -8
View File
@@ -21,8 +21,6 @@ const IntTxsIndexingStatus = () => {
},
});
const bgColor = { base: 'blackAlpha.100', _dark: 'whiteAlpha.100' };
const queryClient = useQueryClient();
const handleInternalTxsIndexStatus: SocketMessage.InternalTxsIndexStatus['handler'] = React.useCallback((payload) => {
@@ -65,15 +63,13 @@ const IntTxsIndexingStatus = () => {
const trigger = (
<Flex
px={ 2 }
py={ 1 }
bg={ bgColor }
borderRadius="base"
px={ 1 }
bg={{ base: 'blackAlpha.50', _dark: 'whiteAlpha.100' }}
borderRadius="sm"
alignItems="center"
justifyContent="center"
columnGap={ 1 }
color="green.400"
_hover={{ color: 'blue.400' }}
_hover={{ color: 'hover' }}
>
<IconSvg name="info" boxSize={ 5 }/>
{ data.indexed_internal_transactions_ratio && (
Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 38 KiB

+6 -2
View File
@@ -13,10 +13,13 @@ test.beforeEach(async({ mockEnvs }) => {
]);
});
test('default view +@dark-mode', async({ render, mockApiResponse, page }) => {
test('default view +@dark-mode', async({ render, mockApiResponse, page, injectMetaMaskProvider }) => {
await injectMetaMaskProvider();
await mockApiResponse('general:stats', statsMock.base);
const component = await render(<TopBar/>);
await expect(page.getByText(/add blockscout/i)).toBeVisible();
await component.getByText(/\$1\.39/).click();
await expect(page.getByText(/last update/i)).toBeVisible();
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 220 } });
@@ -25,8 +28,9 @@ test('default view +@dark-mode', async({ render, mockApiResponse, page }) => {
await expect(page).toHaveScreenshot({ clip: { x: 0, y: 0, width: 1500, height: 400 } });
});
test('default view +@mobile -@default', async({ render, mockApiResponse, page }) => {
test('default view +@mobile -@default', async({ render, mockApiResponse, page, injectMetaMaskProvider }) => {
await mockApiResponse('general:stats', statsMock.base);
await injectMetaMaskProvider();
const component = await render(<TopBar/>);
await component.getByLabel('User settings').click();
+36 -7
View File
@@ -2,7 +2,12 @@ import { Flex, Separator, Box, HStack } from '@chakra-ui/react';
import React from 'react';
import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
import useIsMobile from 'lib/hooks/useIsMobile';
import useProvider from 'lib/web3/useProvider';
import { CONTENT_MAX_WIDTH } from 'ui/shared/layout/utils';
import NetworkAddToWallet from 'ui/shared/NetworkAddToWallet';
import DeFiDropdown from './DeFiDropdown';
import NetworkMenu from './NetworkMenu';
@@ -10,6 +15,27 @@ import Settings from './settings/Settings';
import TopBarStats from './TopBarStats';
const TopBar = () => {
const hideAddToWalletButtonCookie = cookies.get(cookies.NAMES.HIDE_ADD_TO_WALLET_BUTTON, useAppContext().cookies);
const [ isAddChainButtonVisible, setIsAddChainButtonVisible ] = React.useState(hideAddToWalletButtonCookie !== 'topbar');
const web3 = useProvider();
const isMobile = useIsMobile();
const hasAddChainButton = Boolean(
isAddChainButtonVisible &&
web3.data?.provider &&
web3.data?.wallet &&
config.chain.rpcUrls.length &&
config.features.web3Wallet.isEnabled &&
!isMobile,
);
const hasDeFiDropdown = Boolean(config.features.deFiDropdown.isEnabled);
const handleAddSuccess = React.useCallback(() => {
cookies.set(cookies.NAMES.HIDE_ADD_TO_WALLET_BUTTON, 'topbar', { expires: 3 * 365 });
setIsAddChainButtonVisible(false);
}, [ ]);
return (
// not ideal if scrollbar is visible, but better than having a horizontal scroll
<Box bgColor={{ _light: 'theme.topbar.bg._light', _dark: 'theme.topbar.bg._dark' }} position="sticky" left={ 0 } width="100%" maxWidth="100vw">
@@ -25,15 +51,18 @@ const TopBar = () => {
{ Boolean(config.UI.featuredNetworks.items) && <NetworkMenu/> }
{ !config.features.opSuperchain.isEnabled ? <TopBarStats/> : <div/> }
</HStack>
<Flex alignItems="center">
{ config.features.deFiDropdown.isEnabled && (
<>
<DeFiDropdown/>
<Separator mr={ 3 } ml={{ base: 2, sm: 3 }} height={ 4 } orientation="vertical" borderColor="border.divider"/>
</>
<HStack
alignItems="center"
separator={ <Separator mx={{ base: 2, lg: 3 }} height={ 4 } orientation="vertical" borderColor="border.divider"/> }
>
{ (hasAddChainButton || hasDeFiDropdown) && (
<HStack>
{ hasAddChainButton && <NetworkAddToWallet source="Top bar" onAddSuccess={ handleAddSuccess }/> }
{ hasDeFiDropdown && <DeFiDropdown/> }
</HStack>
) }
<Settings/>
</Flex>
</HStack>
</Flex>
</Box>
);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB