Skip to content

Commit

Permalink
Initial implementation for lsp7 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Wolmin committed Dec 28, 2023
1 parent 5058f32 commit 67ad55a
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 51 deletions.
23 changes: 23 additions & 0 deletions lib/api/algolia.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { SearchIndex } from 'algoliasearch';
import algoliasearch from 'algoliasearch';

import { getEnvValue } from '../../configs/app/utils';

interface AlgoliaSearcher {
profile: SearchIndex;
asset: SearchIndex;
}

const algoliaEnvs = {
appId: getEnvValue('NEXT_PUBLIC_ALGOLIA_APP_ID') || '',
apiKey: getEnvValue('NEXT_PUBLIC_ALGOLIA_API_KEY') || '',
profileIndex: getEnvValue('NEXT_PUBLIC_ALGOLIA_PROFILE_INDEX_NAME') || '',
assetIndex: getEnvValue('NEXT_PUBLIC_ALGOLIA_ASSETS_INDEX_NAME') || '',
};

const algoliaClient = algoliasearch(algoliaEnvs.appId, algoliaEnvs.apiKey);

export const algoliaLSPSearch: AlgoliaSearcher = {
profile: algoliaClient.initIndex(algoliaEnvs.profileIndex),
asset: algoliaClient.initIndex(algoliaEnvs.assetIndex),
};
11 changes: 0 additions & 11 deletions lib/api/buildUniversalProfileUrl.ts

This file was deleted.

17 changes: 17 additions & 0 deletions lib/api/fetchLsp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { LSPResponse } from '../../types/api/lsp';

import { getEnvValue } from '../../configs/app/utils';

export const fetchLsp = async <T extends LSPResponse>(address: string) => {
const upApiUrl = getEnvValue('NEXT_PUBLIC_UNIVERSAL_PROFILES_API_URL') || '';
const networkId = getEnvValue('NEXT_PUBLIC_NETWORK_ID') || '42';
const url = `${ upApiUrl }/v1/${ networkId }/address/${ address }`;

try {
const resp = await fetch(url);
const json = await resp.json();
return json as T;
} catch (err) {
return undefined;
}
};
4 changes: 4 additions & 0 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,9 @@ export const RESOURCES = {
universal_profile: {
path: '',
},
lsp7_asset: {
path: '',
},

// API V1
csv_export_txs: {
Expand Down Expand Up @@ -639,6 +642,7 @@ Q extends 'address_logs' ? LogsResponseAddress :
Q extends 'address_tokens' ? AddressTokensResponse :
Q extends 'address_withdrawals' ? AddressWithdrawalsResponse :
Q extends 'universal_profile' ? Array<SearchResultItem> :
Q extends 'lsp7_asset' ? Array<SearchResultItem> :
Q extends 'token' ? TokenInfo :
Q extends 'token_verified_info' ? TokenVerifiedInfo :
Q extends 'token_counters' ? TokenCounters :
Expand Down
47 changes: 47 additions & 0 deletions lib/api/useLSP7ApiFetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';

import type { LSP7Response } from '../../types/api/lsp';
import type { SearchResultToken } from '../../types/api/search';

import type { Params as FetchParams } from 'lib/hooks/useFetch';

import { algoliaLSPSearch } from './algolia';
import { isUniversalProfileEnabled } from './isUniversalProfileEnabled';
import type { ResourceName, ResourcePathParams } from './resources';

export interface Params<R extends ResourceName> {
pathParams?: ResourcePathParams<R>;
queryParams?: Record<string, string | Array<string> | number | undefined>;
fetchParams?: Pick<FetchParams, 'body' | 'method' | 'signal'>;
}

export default function useLSP7ApiFetch() {
return React.useCallback(async(queryParams: string,
) => {
if (!isUniversalProfileEnabled()) {
return [] as Array<SearchResultToken>;
}
try {
const { hits } = await algoliaLSPSearch.asset.search(queryParams);
return hits.map<SearchResultToken>((hit) => {
const lsp = hit as unknown as LSP7Response;
return {
type: 'token',
name: lsp.name,
symbol: lsp.symbol,
address: lsp.address,
token_url: lsp.linkUrl,
address_url: lsp.linkUrl,
icon_url: null,
token_type: 'LSP7',
exchange_rate: null,
total_supply: '0',
is_verified_via_admin_panel: false,
is_smart_contract_verified: false,
};
});
} catch (error) {
return error;
}
}, []);
}
21 changes: 21 additions & 0 deletions lib/api/useLSP7Query.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useQuery } from '@tanstack/react-query';

import type { ResourceError, ResourceName, ResourcePayload } from './resources';
import type { Params } from './useApiQuery';
import { getResourceKey } from './useApiQuery';
import useLSP7ApiFetch from './useLSP7ApiFetch';

export default function useLSP7Query<R extends ResourceName, E = unknown>(
resource: R,
{ queryOptions, pathParams, queryParams }: Params<R, E> = {},
) {
const lspFetch = useLSP7ApiFetch();
return useQuery<ResourcePayload<R>, ResourceError<E>, ResourcePayload<R>>({
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey: getResourceKey(resource, { pathParams, queryParams }),
queryFn: async() => {
return await lspFetch(queryParams?.q as string) as Promise<ResourcePayload<R>>;
},
...queryOptions,
});
}
8 changes: 4 additions & 4 deletions lib/api/useUniversalProfileApiFetch.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';

import type { LSP3Response } from '../../types/api/lsp';
import type { SearchResultAddressOrContractOrUniversalProfile } from '../../types/api/search';
import type { UniversalProfileProxyResponse } from '../../types/api/universalProfile';

import type { Params as FetchParams } from 'lib/hooks/useFetch';

import { algoliaIndex } from './buildUniversalProfileUrl';
import { algoliaLSPSearch } from './algolia';
import { isUniversalProfileEnabled } from './isUniversalProfileEnabled';
import type { ResourceName, ResourcePathParams } from './resources';

Expand All @@ -22,9 +22,9 @@ export default function useUniversalProfileApiFetch() {
return [] as Array<SearchResultAddressOrContractOrUniversalProfile>;
}
try {
const { hits } = await algoliaIndex.search(queryParams);
const { hits } = await algoliaLSPSearch.profile.search(queryParams);
return hits.map<SearchResultAddressOrContractOrUniversalProfile>((hit) => {
const hitAsUp = hit as unknown as UniversalProfileProxyResponse;
const hitAsUp = hit as unknown as LSP3Response;
return {
type: 'universal_profile',
name: hitAsUp.hasProfileName ? hitAsUp.LSP3Profile.name : null,
Expand Down
49 changes: 49 additions & 0 deletions types/api/lsp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
export type LSP3Response = {
type: string;
hasProfileName: boolean;
hasProfileImage: boolean;
LSP3Profile: {
name: string;
profileImage: {
[key: number]: {
url: string;
};
};
};
}

export type LSP7Response = {
address: string;
updatedAtBlockNameAndSymbol: number;
symbol: string;
hasTokenSymbol: boolean;
name: string;
hasTokenName: boolean;
LSP4Metadata: {
description: string;
icon: {
[key: number]: {
url: string;
};
};
images: {
[key: number]: {
[key: number]: {
url: string;
};
};
};
};
assetImageUrl: string;
assetImageRawUrl: string;
hasAssetImage: boolean;
iconImageUrl: string;
iconImageRawUrl: string;
hasIconImage: boolean;
updatedAtBlockMetadata: number;
description: string;
objectID: string;
linkUrl: string;
}

export type LSPResponse = LSP3Response | LSP7Response
2 changes: 1 addition & 1 deletion types/api/token.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { TokenInfoApplication } from './account';
import type { AddressParam } from './addressParams';

export type TokenType = 'ERC-20' | 'ERC-721' | 'ERC-1155';
export type TokenType = 'ERC-20' | 'ERC-721' | 'ERC-1155' | 'LSP7';

export interface TokenInfo<T extends TokenType = TokenType> {
address: string;
Expand Down
27 changes: 0 additions & 27 deletions types/api/universalProfile.ts

This file was deleted.

8 changes: 4 additions & 4 deletions ui/shared/entities/address/IdenticonUniversalProfileQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { QueryClient } from '@tanstack/react-query';
import { useQueryClient } from '@tanstack/react-query';
import React, { useEffect, useState } from 'react';

import type { UniversalProfileProxyResponse } from '../../../../types/api/universalProfile';
import type { LSP3Response } from '../../../../types/api/lsp';

import { getEnvValue } from '../../../../configs/app/utils';
import { isUniversalProfileEnabled } from '../../../../lib/api/isUniversalProfileEnabled';
Expand All @@ -21,7 +21,7 @@ export const getUniversalProfile = async(address: string, queryClient: QueryClie
if (!isUniversalProfileEnabled()) {
return undefined;
}
const query = queryClient.getQueryData<UniversalProfileProxyResponse>([ 'universalProfile', { address: address } ]);
const query = queryClient.getQueryData<LSP3Response>([ 'universalProfile', { address: address } ]);
if (query !== undefined) {
return query;
}
Expand All @@ -36,7 +36,7 @@ export const getUniversalProfile = async(address: string, queryClient: QueryClie
try {
const resp = await fetch(url);
const json = await resp.json();
return json as UniversalProfileProxyResponse;
return json as LSP3Response;
} catch (err) {
return undefined;
}
Expand All @@ -45,7 +45,7 @@ export const getUniversalProfile = async(address: string, queryClient: QueryClie
};

export const IdenticonUniversalProfile: React.FC<Props> = ({ address, fallbackIcon }) => {
const [ up, setUp ] = useState({} as UniversalProfileProxyResponse);
const [ up, setUp ] = useState({} as LSP3Response);
const queryClient = useQueryClient();
useEffect(() => {
(async() => {
Expand Down
6 changes: 4 additions & 2 deletions ui/shared/search/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { SearchResultItem } from 'types/api/search';
import type { MarketplaceAppOverview } from 'types/client/marketplace';

import { isUniversalProfileEnabled } from '../../../lib/api/isUniversalProfileEnabled';

export type ApiCategory = 'token' | 'nft' | 'address' | 'public_tag' | 'transaction' | 'block' | 'universal_profile';
export type Category = ApiCategory | 'app';

Expand All @@ -15,8 +17,8 @@ export type SearchResultAppItem = {

export const searchCategories: Array<{id: Category; title: string }> = [
{ id: 'app', title: 'Apps' },
{ id: 'token', title: 'Tokens (ERC-20)' },
{ id: 'nft', title: 'NFTs (ERC-721 & 1155)' },
{ id: 'token', title: isUniversalProfileEnabled() ? 'Tokens (ERC-20 & LSP7)' : 'Tokens (ERC-20)' },
{ id: 'nft', title: isUniversalProfileEnabled() ? 'NFTs (ERC-721 & 1155 & LSP8)' : 'NFTs (ERC-721 & 1155)' },
{ id: 'address', title: 'Addresses' },
{ id: 'public_tag', title: 'Public tags' },
{ id: 'transaction', title: 'Transactions' },
Expand Down
11 changes: 9 additions & 2 deletions ui/snippets/searchBar/useQuickSearchQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { SearchResultItem } from '../../../types/api/search';
import useApiQuery from 'lib/api/useApiQuery';
import useDebounce from 'lib/hooks/useDebounce';

import useLSP7Query from '../../../lib/api/useLSP7Query';
import useUniversalProfileQuery from '../../../lib/api/useUniversalProfileQuery';

export default function useQuickSearchQuery() {
Expand Down Expand Up @@ -34,13 +35,19 @@ export default function useQuickSearchQuery() {
queryOptions: { enabled: debouncedSearchTerm.trim().length > 0 },
});

const lspQuery = useLSP7Query('lsp7_asset', {
queryParams: { q: debouncedSearchTerm },
queryOptions: { enabled: debouncedSearchTerm.trim().length > 0 },
});

const query = useQuery({
queryKey: [ 'merged_query', quickSearchQuery, upQuery ],
queryKey: [ 'merged_query', quickSearchQuery, upQuery, lspQuery ],
queryFn: () => {
const q1 = quickSearchQuery.data as Array<SearchResultItem>;
const q2 = upQuery.data as Array<SearchResultItem>;
const q3 = lspQuery.data as Array<SearchResultItem>;

return [ ...q1, ...q2 ];
return [ ...q1, ...q2, ...q3 ];
},
});

Expand Down

0 comments on commit 67ad55a

Please sign in to comment.