From e0b89d058830b1adfd8dd1ce01fce44e3a0380c7 Mon Sep 17 00:00:00 2001 From: Igor Stuev <108066576+isstuev@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:53:42 +0100 Subject: [PATCH] Humanity score integration (#2354) * Humanity score integration * isLoading fix --- configs/app/features/index.ts | 1 + configs/app/features/xStarScore.ts | 23 ++++++++++++ configs/envs/.env.eth_sepolia | 1 + deploy/tools/envs-validator/schema.ts | 1 + docs/ENVS.md | 11 ++++++ lib/api/resources.ts | 6 ++++ lib/xStarScore/useFetchXStarScore.ts | 51 +++++++++++++++++++++++++++ types/api/address.ts | 4 +++ ui/pages/Address.tsx | 33 +++++++++++++++-- 9 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 configs/app/features/xStarScore.ts create mode 100644 lib/xStarScore/useFetchXStarScore.ts diff --git a/configs/app/features/index.ts b/configs/app/features/index.ts index 0ccd4f23c8..8fb703f600 100644 --- a/configs/app/features/index.ts +++ b/configs/app/features/index.ts @@ -37,3 +37,4 @@ export { default as addressProfileAPI } from './addressProfileAPI'; export { default as validators } from './validators'; export { default as verifiedTokens } from './verifiedTokens'; export { default as web3Wallet } from './web3Wallet'; +export { default as xStarScore } from './xStarScore'; diff --git a/configs/app/features/xStarScore.ts b/configs/app/features/xStarScore.ts new file mode 100644 index 0000000000..3219584d3e --- /dev/null +++ b/configs/app/features/xStarScore.ts @@ -0,0 +1,23 @@ +import type { Feature } from './types'; + +import { getEnvValue } from '../utils'; + +const title = 'Xname score'; +const url = getEnvValue('NEXT_PUBLIC_XNAME_SCORE_URL'); + +const config: Feature<{ url: string }> = (() => { + if (url) { + return Object.freeze({ + title, + url, + isEnabled: true, + }); + } + + return Object.freeze({ + title, + isEnabled: false, + }); +})(); + +export default config; diff --git a/configs/envs/.env.eth_sepolia b/configs/envs/.env.eth_sepolia index 9299600d29..763512fdb5 100644 --- a/configs/envs/.env.eth_sepolia +++ b/configs/envs/.env.eth_sepolia @@ -62,3 +62,4 @@ NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=noves NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer.services.blockscout.com NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=https://points.k8s-dev.blockscout.com +NEXT_PUBLIC_XSTAR_SCORE_URL='https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm?utm_source=blockscout&utm_medium=address' diff --git a/deploy/tools/envs-validator/schema.ts b/deploy/tools/envs-validator/schema.ts index deda07048a..574901e1a9 100644 --- a/deploy/tools/envs-validator/schema.ts +++ b/deploy/tools/envs-validator/schema.ts @@ -837,6 +837,7 @@ const schema = yup return isUndefined || valueSchema.isValidSync(data); }), NEXT_PUBLIC_REWARDS_SERVICE_API_HOST: yup.string().test(urlTest), + NEXT_PUBLIC_XSTAR_SCORE_URL: yup.string().test(urlTest), // 6. External services envs NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID: yup.string(), diff --git a/docs/ENVS.md b/docs/ENVS.md index 840322fd4e..681d6f76dc 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -55,6 +55,7 @@ Please be aware that all environment variables prefixed with `NEXT_PUBLIC_` will - [Bridged tokens](ENVS.md#bridged-tokens) - [Safe{Core} address tags](ENVS.md#safecore-address-tags) - [Address profile API](ENVS.md#address-profile-api) + - [Address XStar XHS score](ENVS.md#address-xstar-xhs-score) - [SUAVE chain](ENVS.md#suave-chain) - [MetaSuites extension](ENVS.md#metasuites-extension) - [Validators list](ENVS.md#validators-list) @@ -680,6 +681,16 @@ This feature allows the integration of an external API to fetch user info for ad   +### Address XStar XHS score + +This feature allows the integration of an XStar API to fetch XHS score for addresses. When configured, if the API returns a score, a public tag with that score will be displayed in the address page header. + +| Variable | Type| Description | Compulsoriness | Default value | Example value | Version | +| --- | --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_XSTAR_SCORE_URL | `string` | XStar XHS score documentation URL for the address tag. Enables the XStar score feature. | - | - | `https://docs.xname.app/the-solution-adaptive-proof-of-humanity-on-blockchain/xhs-scoring-algorithm` | v1.36.0+ | + +  + ### SUAVE chain For blockchains that implement SUAVE architecture additional fields will be shown on the transaction page ("Allowed peekers", "Kettle"). Users also will be able to see the list of all transactions for a particular Kettle in the separate view. diff --git a/lib/api/resources.ts b/lib/api/resources.ts index 401403210a..b220e4b7d0 100644 --- a/lib/api/resources.ts +++ b/lib/api/resources.ts @@ -38,6 +38,7 @@ import type { AddressMudRecordsSorting, AddressMudRecord, AddressEpochRewardsResponse, + AddressXStarResponse, } from 'types/api/address'; import type { AddressesResponse, AddressesMetadataSearchResult, AddressesMetadataSearchFilters } from 'types/api/addresses'; import type { AddressMetadataInfo, PublicTagTypesResponse } from 'types/api/addressMetadata'; @@ -595,6 +596,10 @@ export const RESOURCES = { pathParams: [ 'hash' as const ], filterFields: [], }, + address_xstar_score: { + path: '/api/v2/proxy/xname/address/:hash', + pathParams: [ 'hash' as const ], + }, // CONTRACT contract: { @@ -1304,6 +1309,7 @@ Q extends 'rewards_user_daily_check' ? RewardsUserDailyCheckResponse : Q extends 'rewards_user_daily_claim' ? RewardsUserDailyClaimResponse : Q extends 'rewards_user_referrals' ? RewardsUserReferralsResponse : Q extends 'token_transfers_all' ? TokenTransferResponse : +Q extends 'address_xstar_score' ? AddressXStarResponse : never; /* eslint-enable @typescript-eslint/indent */ diff --git a/lib/xStarScore/useFetchXStarScore.ts b/lib/xStarScore/useFetchXStarScore.ts new file mode 100644 index 0000000000..dab7deda8e --- /dev/null +++ b/lib/xStarScore/useFetchXStarScore.ts @@ -0,0 +1,51 @@ +import React from 'react'; +import * as v from 'valibot'; + +import config from 'configs/app'; +import buildUrl from 'lib/api/buildUrl'; +import useApiQuery from 'lib/api/useApiQuery'; + +interface Params { + hash: string; +} + +const RESOURCE_NAME = 'address_xstar_score'; +const ERROR_NAME = 'Invalid response schema'; + +export default function useFetchXStarScore({ hash }: Params) { + const query = useApiQuery(RESOURCE_NAME, { + pathParams: { hash }, + queryOptions: { + select: (response) => { + const parsedResponse = v.safeParse(v.object({ data: v.string() }), response); + + if (!parsedResponse.success) { + throw Error(ERROR_NAME); + } + + return parsedResponse.output; + }, + enabled: Boolean(hash) && config.features.xStarScore.isEnabled, + placeholderData: { + data: 'Base' as const, + }, + retry: 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: RESOURCE_NAME, + url: buildUrl(RESOURCE_NAME, { hash }, undefined, true), + }), + }); + } + }, [ errorMessage, hash ]); + + return query; +} diff --git a/types/api/address.ts b/types/api/address.ts index c20b3eb034..270db50007 100644 --- a/types/api/address.ts +++ b/types/api/address.ts @@ -270,3 +270,7 @@ export type AddressEpochRewardsItem = { epoch_number: number; associated_account: AddressParam; } + +export type AddressXStarResponse = { + data: string | null; +} diff --git a/ui/pages/Address.tsx b/ui/pages/Address.tsx index 6c87ecfd2b..dc9f52090d 100644 --- a/ui/pages/Address.tsx +++ b/ui/pages/Address.tsx @@ -16,6 +16,7 @@ import getNetworkValidationActionText from 'lib/networks/getNetworkValidationAct import getQueryParamString from 'lib/router/getQueryParamString'; import useSocketChannel from 'lib/socket/useSocketChannel'; import useSocketMessage from 'lib/socket/useSocketMessage'; +import useFetchXStarScore from 'lib/xStarScore/useFetchXStarScore'; import { ADDRESS_TABS_COUNTERS } from 'stubs/address'; import { USER_OPS_ACCOUNT } from 'stubs/userOps'; import AddressAccountHistory from 'ui/address/AddressAccountHistory'; @@ -58,6 +59,7 @@ const TOKEN_TABS = [ 'tokens_erc20', 'tokens_nfts', 'tokens_nfts_collection', 't const txInterpretation = config.features.txInterpretation; const addressProfileAPIFeature = config.features.addressProfileAPI; +const xScoreFeature = config.features.xStarScore; const AddressPageContent = () => { const router = useRouter(); @@ -139,6 +141,8 @@ const AddressPageContent = () => { const isSafeAddress = useIsSafeAddress(!addressQuery.isPlaceholderData && addressQuery.data?.is_contract ? hash : undefined); const safeIconColor = useColorModeValue('black', 'white'); + const xStarQuery = useFetchXStarScore({ hash }); + const contractTabs = useContractTabs( addressQuery.data, config.features.mudFramework.isEnabled ? (mudTablesCountQuery.isPlaceholderData || addressQuery.isPlaceholderData) : addressQuery.isPlaceholderData, @@ -295,8 +299,32 @@ const AddressPageContent = () => { undefined, ...formatUserTags(addressQuery.data), ...(addressMetadataQuery.data?.addresses?.[hash.toLowerCase()]?.tags.filter(tag => tag.tagType !== 'note') || []), + !addressQuery.data?.is_contract && xScoreFeature.isEnabled && xStarQuery.data?.data ? + { + slug: 'xstar', + name: `XHS ${ xStarQuery.data.data } level`, + tagType: 'custom' as const, + ordinal: 12, + meta: { + tagUrl: xScoreFeature.url, + tooltipTitle: 'XStar humanity levels', + tooltipDescription: + 'XStar looks for off-chain information about an address and interpret it as a XHS score. Different score means different humanity levels.', + tooltipUrl: xScoreFeature.url, + }, + } : + undefined, ].filter(Boolean).sort(sortEntityTags); - }, [ addressMetadataQuery.data, addressQuery.data, hash, isSafeAddress, userOpsAccountQuery.data, mudTablesCountQuery.data, usernameApiTag ]); + }, [ + addressMetadataQuery.data, + addressQuery.data, + hash, + isSafeAddress, + userOpsAccountQuery.data, + mudTablesCountQuery.data, + usernameApiTag, + xStarQuery.data?.data, + ]); const titleContentAfter = ( { isLoading || (config.features.userOps.isEnabled && userOpsAccountQuery.isPlaceholderData) || (config.features.addressMetadata.isEnabled && addressMetadataQuery.isPending) || - (addressProfileAPIFeature.isEnabled && userPropfileApiQuery.isPending) + (addressProfileAPIFeature.isEnabled && userPropfileApiQuery.isPending) || + (xScoreFeature.isEnabled && xStarQuery.isPlaceholderData) } /> );