mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
GasHawk integration (#2232)
* GasHawk integration Fixes #2205 * change link url
This commit is contained in:
@@ -25,6 +25,7 @@ export { default as publicTagsSubmission } from './publicTagsSubmission';
|
||||
export { default as restApiDocs } from './restApiDocs';
|
||||
export { default as rollup } from './rollup';
|
||||
export { default as safe } from './safe';
|
||||
export { default as saveOnGas } from './saveOnGas';
|
||||
export { default as sentry } from './sentry';
|
||||
export { default as sol2uml } from './sol2uml';
|
||||
export { default as stats } from './stats';
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { Feature } from './types';
|
||||
|
||||
import { getEnvValue } from '../utils';
|
||||
import marketplace from './marketplace';
|
||||
|
||||
const title = 'Save on gas with GasHawk';
|
||||
|
||||
const config: Feature<{
|
||||
apiUrlTemplate: string;
|
||||
}> = (() => {
|
||||
if (getEnvValue('NEXT_PUBLIC_SAVE_ON_GAS_ENABLED') === 'true' && marketplace.isEnabled) {
|
||||
return Object.freeze({
|
||||
title,
|
||||
isEnabled: true,
|
||||
apiUrlTemplate: 'https://core.gashawk.io/apiv2/stats/address/<address>/savingsPotential/0x1',
|
||||
});
|
||||
}
|
||||
|
||||
return Object.freeze({
|
||||
title,
|
||||
isEnabled: false,
|
||||
});
|
||||
})();
|
||||
|
||||
export default config;
|
||||
@@ -62,3 +62,4 @@ NEXT_PUBLIC_STATS_API_HOST=https://stats-eth-main.k8s-prod-1.blockscout.com
|
||||
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
|
||||
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
|
||||
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com
|
||||
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true
|
||||
|
||||
@@ -801,6 +801,7 @@ const schema = yup
|
||||
value => value === undefined,
|
||||
),
|
||||
}),
|
||||
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED: yup.boolean(),
|
||||
|
||||
// 6. External services envs
|
||||
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(),
|
||||
|
||||
@@ -84,3 +84,4 @@ NEXT_PUBLIC_VALIDATORS_CHAIN_TYPE=stability
|
||||
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'uniswap'},{'text':'Payment link','icon':'payment_link','url':'https://example.com'}]
|
||||
NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG={'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'}
|
||||
NEXT_PUBLIC_GAS_REFUEL_PROVIDER_CONFIG={'name': 'Need gas?', 'dapp_id': 'smol-refuel', 'url_template': 'https://smolrefuel.com/?outboundChain={chainId}&partner=blockscout&utm_source=blockscout&utm_medium=address&disableBridges=true', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
|
||||
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true
|
||||
|
||||
+12
-1
@@ -59,9 +59,10 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will
|
||||
- [Validators list](ENVS.md#validators-list)
|
||||
- [Sentry error monitoring](ENVS.md#sentry-error-monitoring)
|
||||
- [OpenTelemetry](ENVS.md#opentelemetry)
|
||||
- [Swap button](ENVS.md#defi-dropdown)
|
||||
- [DeFi dropdown](ENVS.md#defi-dropdown)
|
||||
- [Multichain balance button](ENVS.md#multichain-balance-button)
|
||||
- [Get gas button](ENVS.md#get-gas-button)
|
||||
- [Save on gas with GasHawk](ENVS.md#save-on-gas-with-gashawk)
|
||||
- [3rd party services configuration](ENVS.md#external-services-configuration)
|
||||
|
||||
|
||||
@@ -755,6 +756,16 @@ If the feature is enabled, a Get gas button will be displayed in the top bar, wh
|
||||
|
||||
|
||||
|
||||
### Save on gas with GasHawk
|
||||
|
||||
The feature enables a "Save with GasHawk" button next to the "Gas used" value on the address page.
|
||||
|
||||
| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| NEXT_PUBLIC_SAVE_ON_GAS_ENABLED | `boolean` | Set to "true" to enable the feature | - | - | `true` | v1.35.0+ |
|
||||
|
||||
|
||||
|
||||
## External services configuration
|
||||
|
||||
### Google ReCaptcha
|
||||
|
||||
@@ -6,6 +6,7 @@ function generateCspPolicy() {
|
||||
descriptors.app(),
|
||||
descriptors.ad(),
|
||||
descriptors.cloudFlare(),
|
||||
descriptors.gasHawk(),
|
||||
descriptors.googleAnalytics(),
|
||||
descriptors.googleFonts(),
|
||||
descriptors.googleReCaptcha(),
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import type CspDev from 'csp-dev';
|
||||
|
||||
import config from 'configs/app';
|
||||
|
||||
const feature = config.features.saveOnGas;
|
||||
|
||||
export function gasHawk(): CspDev.DirectiveDescriptor {
|
||||
if (!feature.isEnabled) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const apiOrigin = (() => {
|
||||
try {
|
||||
const url = new URL(feature.apiUrlTemplate);
|
||||
return url.origin;
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
})();
|
||||
|
||||
if (!apiOrigin) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
'connect-src': [
|
||||
apiOrigin,
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export { ad } from './ad';
|
||||
export { app } from './app';
|
||||
export { cloudFlare } from './cloudFlare';
|
||||
export { gasHawk } from './gasHawk';
|
||||
export { googleAnalytics } from './googleAnalytics';
|
||||
export { googleFonts } from './googleFonts';
|
||||
export { googleReCaptcha } from './googleReCaptcha';
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<svg viewBox="0 0 15 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.4 12.07c-.164-.152-.302-.312-.456-.454-.703-.668-.168-1.364-.168-1.364s-.844.132-2.113-.128a4.273 4.273 0 0 0-4.44 1.175 2.618 2.618 0 0 0-2.084 2.334 4.722 4.722 0 0 0-.002.5 4.64 4.64 0 0 0 .024.209s.023.132.038.196l.025.095v.003l.028.094.014.04a2.6 2.6 0 0 1 .286-.97.38.38 0 0 1 .346-.196 7 7 0 0 1 1.352.218 4.28 4.28 0 0 1 2.343 2.9h.001a4.683 4.683 0 0 1-.152 2.937A7.22 7.22 0 0 0 14.4 12.07Zm-4.82-.62a.644.644 0 0 1-1.074-.138c.118-.184.239-.278.36-.328.473-.212.807-.22.807-.22.1.221.073.489-.094.686Z" fill="url(#a)"/>
|
||||
<path d="M9.594 16.721a4.743 4.743 0 0 1 .107.5l1.551 1.474.088.021a7.247 7.247 0 0 0 2.415-2.86c-3.78-3.424-7.12-2.33-7.856-2.252.464.029.916.103 1.352.218a4.28 4.28 0 0 1 2.343 2.9Z" fill="url(#b)"/>
|
||||
<path d="M7.605.209A.46.46 0 0 0 7.22 0a.459.459 0 0 0-.387.209L1.381 8.544A7.185 7.185 0 0 0 0 12.782c-.004 3.86 3.064 7.044 6.889 7.218.562-.986.571-2.159.565-2.426a4.866 4.866 0 0 1-.236.006h-.033a4.757 4.757 0 0 1-3.361-1.422 4.755 4.755 0 0 1-1.396-3.374 4.751 4.751 0 0 1 .915-2.81l3.875-5.92L9.53 7.69a6.433 6.433 0 0 1 3.095.254L7.605.209Z" fill="url(#c)"/>
|
||||
<path d="M12.256 17.96c-.686-1.602-1.57-2.637-2.252-3.249-1.07-.958-1.997-1.25-2.875-1.24-.877.01-1.361.15-1.361.15a.408.408 0 0 1 .13-.017c.465.029.917.102 1.352.217a4.28 4.28 0 0 1 2.344 2.9 4.683 4.683 0 0 1-.152 2.937 7.22 7.22 0 0 0 2.814-1.7v.001Z" fill="url(#d)"/>
|
||||
<defs>
|
||||
<linearGradient id="a" x1="7.369" y1="12.845" x2="12.113" y2="14.707" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FDF0C2"/>
|
||||
<stop offset="1" stop-color="#F6C789"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b" x1="9.827" y1="13.377" x2="9.837" y2="18.83" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FDF1C3"/>
|
||||
<stop offset=".625" stop-color="#F5C57D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" x1="2.388" y1="15.63" x2="11.492" y2="4.616" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F5C060"/>
|
||||
<stop offset="1" stop-color="#F0994D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="d" x1="9.012" y1="13.47" x2="9.009" y2="15.153" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F5C780"/>
|
||||
<stop offset="1" stop-color="#FADFA1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -21,6 +21,7 @@ import AddressBalance from './details/AddressBalance';
|
||||
import AddressImplementations from './details/AddressImplementations';
|
||||
import AddressNameInfo from './details/AddressNameInfo';
|
||||
import AddressNetWorth from './details/AddressNetWorth';
|
||||
import AddressSaveOnGas from './details/AddressSaveOnGas';
|
||||
import TokenSelect from './tokenSelect/TokenSelect';
|
||||
import useAddressCountersQuery from './utils/useAddressCountersQuery';
|
||||
import type { AddressQuery } from './utils/useAddressQuery';
|
||||
@@ -211,6 +212,12 @@ const AddressDetails = ({ addressQuery, scrollRef }: Props) => {
|
||||
/>
|
||||
) :
|
||||
0 }
|
||||
{ !countersQuery.isPlaceholderData && countersQuery.data?.gas_usage_count && (
|
||||
<AddressSaveOnGas
|
||||
gasUsed={ countersQuery.data.gas_usage_count }
|
||||
address={ data.hash }
|
||||
/>
|
||||
) }
|
||||
</DetailsInfoItem.Value>
|
||||
</>
|
||||
) }
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import { Image, Skeleton } from '@chakra-ui/react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import React from 'react';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import config from 'configs/app';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
import TextSeparator from 'ui/shared/TextSeparator';
|
||||
|
||||
const feature = config.features.saveOnGas;
|
||||
|
||||
const responseSchema = v.object({
|
||||
percent: v.number(),
|
||||
});
|
||||
|
||||
const ERROR_NAME = 'Invalid response schema';
|
||||
|
||||
interface Props {
|
||||
gasUsed: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
const AddressSaveOnGas = ({ gasUsed, address }: Props) => {
|
||||
|
||||
const gasUsedNumber = Number(gasUsed);
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [ 'gas_hawk_saving_potential', { address } ],
|
||||
queryFn: async() => {
|
||||
if (!feature.isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(feature.apiUrlTemplate.replace('<address>', address));
|
||||
const data = await response.json();
|
||||
return data;
|
||||
},
|
||||
select: (response) => {
|
||||
const parsedResponse = v.safeParse(responseSchema, response);
|
||||
|
||||
if (!parsedResponse.success) {
|
||||
throw Error('Invalid response schema');
|
||||
}
|
||||
|
||||
return parsedResponse.output;
|
||||
},
|
||||
placeholderData: { percent: 42 },
|
||||
enabled: feature.isEnabled && gasUsedNumber > 0,
|
||||
});
|
||||
|
||||
const errorMessage = query.error && 'message' in query.error ? query.error.message : undefined;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (errorMessage === ERROR_NAME) {
|
||||
fetch('/node-api/monitoring/invalid-api-schema', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
resource: 'gas_hawk_saving_potential',
|
||||
url: feature.isEnabled ? feature.apiUrlTemplate.replace('<address>', address) : undefined,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}, [ address, errorMessage ]);
|
||||
|
||||
if (gasUsedNumber <= 0 || !feature.isEnabled || query.isError || !query.data?.percent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const percent = Math.round(query.data.percent);
|
||||
|
||||
if (percent < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextSeparator color="divider"/>
|
||||
<Skeleton isLoaded={ !query.isPlaceholderData } display="flex" alignItems="center" columnGap={ 2 }>
|
||||
<Image src="/static/gas_hawk_logo.svg" w="15px" h="20px" alt="GasHawk logo"/>
|
||||
<LinkExternal href="https://www.gashawk.io" fontSize="sm">
|
||||
Save { percent.toLocaleString(undefined, { maximumFractionDigits: 0 }) }% with GasHawk
|
||||
</LinkExternal>
|
||||
</Skeleton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(AddressSaveOnGas);
|
||||
@@ -24,7 +24,7 @@ interface Params {
|
||||
addressQuery: AddressQuery;
|
||||
}
|
||||
|
||||
export default function useAddressQuery({ hash, addressQuery }: Params): AddressCountersQuery {
|
||||
export default function useAddressCountersQuery({ hash, addressQuery }: Params): AddressCountersQuery {
|
||||
const enabled = Boolean(hash) && !addressQuery.isPlaceholderData;
|
||||
|
||||
const apiQuery = useApiQuery<'address_counters', { status: number }>('address_counters', {
|
||||
|
||||
Reference in New Issue
Block a user