feat: use nftscan and remove unidata

This commit is contained in:
DIYgod 2023-06-15 19:19:55 +01:00
parent 664cefabe1
commit 73e8dc29f3
No known key found for this signature in database
12 changed files with 104 additions and 223 deletions

View File

@ -16,12 +16,7 @@ NEXT_PUBLIC_MIRA_LINK="https://mira.crossbell.io"
# NFT
MORALIS_WEB3_API_KEY=
ALCHEMY_ETHEREUM_API_KEY=
ALCHEMY_POLYGON_API_KEY=
NFTSCAN_API_KEY=
OPENSEA_API_KEY=
POAP_API_KEY=
# OPENAI

View File

@ -63,7 +63,6 @@
"tokenomics",
"Topbar",
"Unfollow",
"unidata",
"unist",
"urlset",
"wagmi",

View File

@ -3,12 +3,7 @@ apiVersion: v1
stringData:
REDIS_URL: ${REDIS_URL_DEV}
DATABASE_URL: ${PG_CONN_STRING_DEV}
MORALIS_WEB3_API_KEY: ${MORALIS_WEB3_API_KEY}
ALCHEMY_ETHEREUM_API_KEY: ${ALCHEMY_ETHEREUM_API_KEY}
ALCHEMY_POLYGON_API_KEY: ${ALCHEMY_POLYGON_API_KEY}
NFTSCAN_API_KEY: ${NFTSCAN_API_KEY}
OPENSEA_API_KEY: ${OPENSEA_API_KEY}
POAP_API_KEY: ${POAP_API_KEY}
OPENAI_API_KEY: ${OPENAI_API_KEY}
ANONYMOUS_ACCOUNT_PRIVATEKEY: ${ANONYMOUS_ACCOUNT_PRIVATEKEY}
kind: Secret

View File

@ -3,12 +3,7 @@ apiVersion: v1
stringData:
REDIS_URL: ${REDIS_URL}
DATABASE_URL: ${PG_CONN_STRING}
MORALIS_WEB3_API_KEY: ${MORALIS_WEB3_API_KEY}
ALCHEMY_ETHEREUM_API_KEY: ${ALCHEMY_ETHEREUM_API_KEY}
ALCHEMY_POLYGON_API_KEY: ${ALCHEMY_POLYGON_API_KEY}
NFTSCAN_API_KEY: ${NFTSCAN_API_KEY}
OPENSEA_API_KEY: ${OPENSEA_API_KEY}
POAP_API_KEY: ${POAP_API_KEY}
OPENAI_API_KEY: ${OPENAI_API_KEY}
ANONYMOUS_ACCOUNT_PRIVATEKEY: ${ANONYMOUS_ACCOUNT_PRIVATEKEY}
kind: Secret

View File

@ -112,12 +112,7 @@ module.exports = withBundleAnalyzer(
ENV_REDIS_URL: process.env.REDIS_URL,
ENV_REDIS_EXPIRE: process.env.REDIS_EXPIRE,
ENV_REDIS_REFRESH: process.env.REDIS_REFRESH,
ENV_MORALIS_WEB3_API_KEY: process.env.MORALIS_WEB3_API_KEY,
ENV_ALCHEMY_ETHEREUM_API_KEY: process.env.ALCHEMY_ETHEREUM_API_KEY,
ENV_ALCHEMY_POLYGON_API_KEY: process.env.ALCHEMY_POLYGON_API_KEY,
ENV_NFTSCAN_API_KEY: process.env.NFTSCAN_API_KEY,
ENV_OPENSEA_API_KEY: process.env.OPENSEA_API_KEY,
ENV_POAP_API_KEY: process.env.POAP_API_KEY,
ENV_OPENAI_API_KEY: process.env.OPENAI_API_KEY,
ENV_ANONYMOUS_ACCOUNT_PRIVATEKEY:
process.env.ANONYMOUS_ACCOUNT_PRIVATEKEY,

View File

@ -148,7 +148,6 @@
"tailwind-merge": "1.13.1",
"tailwind-scrollbar-hide": "1.1.7",
"tailwindcss-animate": "1.0.6",
"unidata.js": "0.8.0",
"unified": "10.1.2",
"unist-builder": "3.0.1",
"unist-util-visit": "4.1.2",

View File

@ -112,7 +112,7 @@ dependencies:
version: 4.21.3(@codemirror/view@6.13.2)
'@urql/core':
specifier: 4.0.10
version: 4.0.10(graphql@16.6.0)
version: 4.0.10
ahooks:
specifier: 3.7.7
version: 3.7.7(react@18.2.0)
@ -359,9 +359,6 @@ dependencies:
tailwindcss-animate:
specifier: 1.0.6
version: 1.0.6(tailwindcss@3.3.2)
unidata.js:
specifier: 0.8.0
version: 0.8.0(typescript@5.1.3)(zod@3.21.4)
unified:
specifier: 10.1.2
version: 10.1.2
@ -523,15 +520,13 @@ devDependencies:
packages:
/@0no-co/graphql.web@1.0.4(graphql@16.6.0):
/@0no-co/graphql.web@1.0.4:
resolution: {integrity: sha512-W3ezhHGfO0MS1PtGloaTpg0PbaT8aZSmmaerL7idtU5F7oCI+uu25k+MsMS31BVFlp4aMkHSrNRxiD72IlK8TA==}
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0
peerDependenciesMeta:
graphql:
optional: true
dependencies:
graphql: 16.6.0
dev: false
/@adraffy/ens-normalize@1.9.0:
@ -4681,10 +4676,6 @@ packages:
/@types/katex@0.16.0:
resolution: {integrity: sha512-hz+S3nV6Mym5xPbT9fnO8dDhBFQguMYpY0Ipxv06JMi1ORgnEM4M1ymWDUhUNer3ElLmT583opRo4RzxKmh9jw==}
/@types/lodash@4.14.195:
resolution: {integrity: sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==}
dev: false
/@types/mdast@3.0.11:
resolution: {integrity: sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==}
dependencies:
@ -4869,26 +4860,15 @@ packages:
'@codemirror/view': 6.13.2
dev: false
/@urql/core@4.0.10(graphql@16.6.0):
/@urql/core@4.0.10:
resolution: {integrity: sha512-Vs3nOSAnYftqOCg034Ostp/uSqWlQg5ryLIzcOrm8+O43s4M+Ew4GQAuemIH7ZDB8dek6h61zzWI3ujd8FH3NA==}
dependencies:
'@0no-co/graphql.web': 1.0.4(graphql@16.6.0)
'@0no-co/graphql.web': 1.0.4
wonka: 6.3.2
transitivePeerDependencies:
- graphql
dev: false
/@wagmi/chains@1.0.0(typescript@5.1.3):
resolution: {integrity: sha512-eNbqRWyHbivcMNq5tbXJks4NaOzVLHnNQauHPeE/EDT9AlpqzcrMc+v2T1/2Iw8zN4zgqB86NCsxeJHJs7+xng==}
peerDependencies:
typescript: '>=5.0.4'
peerDependenciesMeta:
typescript:
optional: true
dependencies:
typescript: 5.1.3
dev: false
/@wagmi/chains@1.1.0(typescript@5.1.3):
resolution: {integrity: sha512-pWZlxBk0Ql8E7DV8DwqlbBpOyUdaG9UDlQPBxJNALuEK1I0tbQ3AVvSDnlsEIt06UPmPo5o27gzs3hwPQ/A+UA==}
peerDependencies:
@ -5862,13 +5842,6 @@ packages:
engines: {node: '>=4'}
dev: true
/axios-retry@3.5.0:
resolution: {integrity: sha512-g48qNrLX30VU6ECWltpFCPegKK6dWzMDYv2o83W2zUL/Zh/SLXbT6ksGoKqYZHtghzqeeXhZBcSXJkO1fPbCcw==}
dependencies:
'@babel/runtime': 7.22.5
is-retry-allowed: 2.2.0
dev: false
/axios@0.21.4:
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
dependencies:
@ -5895,16 +5868,6 @@ packages:
- debug
dev: false
/axios@1.4.0:
resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==}
dependencies:
follow-redirects: 1.15.2
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
dev: false
/axobject-query@3.1.1:
resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==}
dependencies:
@ -8434,11 +8397,6 @@ packages:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
dev: true
/graphql@16.6.0:
resolution: {integrity: sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==}
engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
dev: false
/gzip-size@6.0.0:
resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==}
engines: {node: '>=10'}
@ -9108,11 +9066,6 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/is-retry-allowed@2.2.0:
resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==}
engines: {node: '>=10'}
dev: false
/is-set@2.0.2:
resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
dev: true
@ -10390,12 +10343,6 @@ packages:
dependencies:
mime-db: 1.52.0
/mime@3.0.0:
resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
engines: {node: '>=10.0.0'}
hasBin: true
dev: false
/mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
@ -13101,27 +13048,6 @@ packages:
engines: {node: '>=4'}
dev: true
/unidata.js@0.8.0(typescript@5.1.3)(zod@3.21.4):
resolution: {integrity: sha512-oHd1JZCOoNbwWYWfrCWbTmAZUoy6WWTAfDFy5rLYB98x2JiH9QV6PBEuYRqEqH1lN4V6Y/rlxPspbEsGVfFGLg==}
dependencies:
'@types/lodash': 4.14.195
'@urql/core': 4.0.10(graphql@16.6.0)
axios: 1.4.0
axios-retry: 3.5.0
crossbell: 1.4.0(typescript@5.1.3)(zod@3.21.4)
graphql: 16.6.0
lodash: 4.17.21
lodash-es: 4.17.21
mime: 3.0.0
viem: 0.3.50(typescript@5.1.3)(zod@3.21.4)
transitivePeerDependencies:
- bufferutil
- debug
- typescript
- utf-8-validate
- zod
dev: false
/unified@10.1.2:
resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==}
dependencies:
@ -13436,25 +13362,6 @@ packages:
vfile-message: 3.1.4
dev: false
/viem@0.3.50(typescript@5.1.3)(zod@3.21.4):
resolution: {integrity: sha512-s+LxCYZTR9F/qPk1/n1YDVAX9vSeVz7GraqBZWGrDuenCJxo9ArCoIceJ6ksI0WwSeNzcZ0VVbD/kWRzTxkipw==}
dependencies:
'@adraffy/ens-normalize': 1.9.0
'@noble/curves': 1.0.0
'@noble/hashes': 1.3.0
'@scure/bip32': 1.3.0
'@scure/bip39': 1.2.0
'@wagmi/chains': 1.0.0(typescript@5.1.3)
abitype: 0.8.7(typescript@5.1.3)(zod@3.21.4)
isomorphic-ws: 5.0.0(ws@8.12.0)
ws: 8.12.0
transitivePeerDependencies:
- bufferutil
- typescript
- utf-8-validate
- zod
dev: false
/viem@1.0.7(typescript@5.1.3)(zod@3.21.4):
resolution: {integrity: sha512-P/T2Zn8+V74kxO2e2NdhNR/MBtldovssigvc36+7g3DgTeETKnSS1fMQdBl0jE+Atlal+M6cuJ2HcFY7U45o0Q==}
peerDependencies:

View File

@ -1,10 +1,10 @@
import { Metadata } from "next"
import Script from "next/script"
import type { Asset } from "unidata.js"
import { UniLink } from "~/components/ui/UniLink"
import { UniMedia } from "~/components/ui/UniMedia"
import { useTranslation } from "~/lib/i18n"
import { toGateway } from "~/lib/ipfs-parser"
import getQueryClient from "~/lib/query-client"
import { fetchGetSite, getNFTs } from "~/queries/site.server"
@ -38,7 +38,32 @@ export default async function SiteNFTPage({
const site = await fetchGetSite(params.site, queryClient)
const { t } = await useTranslation("common")
const nfts = await getNFTs(site?.owner)
let nfts = await getNFTs(site?.owner)
nfts.forEach((chain: any) => {
chain.assets = []
chain.collection_assets.forEach((collection: any) => {
chain.assets = chain.assets.concat(collection.assets)
})
chain.assets = chain.assets.filter((asset: any) => asset.content_uri)
})
nfts = nfts
.filter((chain: any) => chain.assets.length)
.sort((a: any, b: any) => b.assets.length - a.assets.length)
const displayNames: Record<string, string> = {
eth: "Ethereum",
bsc: "BNB Chain",
pls: "Polygon",
arbi: "Arbitrum One",
opti: "Optimism",
avax: "Avalanche",
cro: "Cronos",
platon: "PlatON",
glmr: "Moonbeam",
ftm: "Fantom",
gnosis: "Gnosis",
}
return (
<>
@ -46,31 +71,53 @@ export default async function SiteNFTPage({
type="module"
src="https://cdn.jsdelivr.net/npm/@google/model-viewer/dist/model-viewer.min.js"
></Script>
<h2 className="text-xl font-bold page-title">NFT {t("Showcase")}</h2>
<div className="mt-8">
<div className="grid grid-cols-3 md:grid-cols-4 gap-10">
{nfts.list
?.filter((nft: Asset) => nft.items?.[0]?.address)
.map((nft: Asset) => (
<UniLink
key={nft.metadata?.proof}
className="xlog-nft flex items-center flex-col"
href={nft.related_urls?.[nft.related_urls.length - 1]}
data-collection={nft.metadata?.collection_address}
data-network={nft.metadata?.network}
data-token-id={nft.metadata?.token_id}
data-name={nft.name}
>
<UniMedia
src={nft.items?.[0]?.address || ""}
mime_type={nft.items?.[0].mime_type}
/>
<div className="text-xs mt-2 text-center font-medium">
{nft.name}
</div>
</UniLink>
))}
</div>
<h2 className="page-title">NFT {t("Showcase")}</h2>
<div className="mt-8 text-zinc-500 text-sm">
<span className="font-medium">Supported chains:</span>{" "}
{Object.values(displayNames).join(", ")}
</div>
<div>
{nfts.map((chain: any) => (
<div
key={chain.chain}
className="flex flex-col"
data-network={chain.chain}
>
<details open>
<summary>
<span className="inline-block text-2xl font-bold my-8 pl-2">
{displayNames[chain.chain] || chain.chain} (
{chain.assets.length})
</span>
</summary>
<div className="grid grid-cols-3 md:grid-cols-4 gap-10">
{chain.assets.map((nft: any) => (
<UniLink
key={nft.mint_transaction_hash}
className="xlog-nft flex items-center flex-col"
href={nft.external_link}
data-collection={nft.contract_address}
data-token-id={nft.token_id}
data-name={nft.name}
>
<UniMedia
src={
nft.content_uri?.startsWith?.("http") ||
nft.content_uri?.startsWith?.("data:")
? nft.content_uri
: toGateway(`ipfs://${nft.content_uri}`) || ""
}
mime_type={nft.content_type}
/>
<div className="text-xs mt-2 text-center font-medium">
{nft.name}
</div>
</UniLink>
))}
</div>
</details>
</div>
))}
</div>
</>
)

View File

@ -25,7 +25,11 @@ export const UniLink = ({
}
if (!href) {
return <span className={className}>{children}</span>
return (
<span className={className} {...props}>
{children}
</span>
)
}
const isExternal =

View File

@ -10,17 +10,8 @@ export const REDIS_REFRESH =
parseInt(config.ENV_REDIS_REFRESH || process.env.REDIS_REFRESH || "0") ||
5 * 1000 // 5 seconds
export const MORALIS_WEB3_API_KEY =
config.ENV_MORALIS_WEB3_API_KEY || process.env.MORALIS_WEB3_API_KEY
export const ALCHEMY_ETHEREUM_API_KEY =
config.ENV_ALCHEMY_ETHEREUM_API_KEY || process.env.ALCHEMY_ETHEREUM_API_KEY
export const ALCHEMY_POLYGON_API_KEY =
config.ENV_ALCHEMY_POLYGON_API_KEY || process.env.ALCHEMY_POLYGON_API_KEY
export const NFTSCAN_API_KEY =
config.ENV_NFTSCAN_API_KEY || process.env.NFTSCAN_API_KEY
export const OPENSEA_API_KEY =
config.ENV_OPENSEA_API_KEY || process.env.OPENSEA_API_KEY
export const POAP_API_KEY = config.ENV_POAP_API_KEY || process.env.POAP_API_KEY
export const OPENAI_API_KEY =
config.ENV_OPENAI_API_KEY || process.env.OPENAI_API_KEY
export const ANONYMOUS_ACCOUNT_PRIVATEKEY =

View File

@ -1,15 +1,14 @@
import chroma from "chroma-js"
import { getAverageColor } from "fast-average-color-node"
import { Asset } from "unidata.js"
import { QueryClient } from "@tanstack/react-query"
import { IPFS_GATEWAY } from "~/lib/env"
import { toGateway } from "~/lib/ipfs-parser"
import { cacheGet, getRedis } from "~/lib/redis.server"
import { ExpandedCharacter } from "~/lib/types"
import * as siteModel from "~/models/site.model"
import unidata from "~/queries/unidata.server"
import { NFTSCAN_API_KEY } from "../lib/env.server"
export const prefetchGetSite = async (
input: string,
@ -111,58 +110,34 @@ export const fetchGetComments = async (
}
export const getNFTs = async (address?: string) => {
const redis = await getRedis()
const redisKey = `nfts/${address}`
if (!address) {
if (!NFTSCAN_API_KEY || !address) {
return null
}
const redis = await getRedis()
const redisKey = `nfts/${address}`
let cache
try {
cache = await redis?.get(redisKey)
// cache = await redis?.get(redisKey)
} catch (error) {}
if (cache) {
return JSON.parse(cache)
} else {
const result = await unidata.assets.get({
source: "Ethereum NFT",
identity: address,
})
await Promise.all(
result.list.map(async (nft: Asset) => {
if (!nft.items?.[0].mime_type && nft.items?.[0]?.address) {
try {
new URL(nft.items[0].address)
} catch (error) {
return nft
}
try {
const mime_type = await cacheGet({
key: `nft-mimetype/${nft.items[0].address}`,
getValueFun: async () => {
const head = await fetch(
`${nft.items![0].address!.replace(
IPFS_GATEWAY,
"https://gateway.ipfs.io/ipfs/",
)}`,
{
method: "HEAD",
},
)
return head.headers.get("content-type")
},
})
nft.items[0].mime_type = mime_type
} catch (error) {
console.warn(error)
}
}
return nft
}),
)
redis?.set(redisKey, JSON.stringify(result), "EX", 60 * 60 * 24)
return result
const result = await (
await fetch(
`https://restapi.nftscan.com/api/v2/assets/chain/${address}?erc_type=&chain=eth;bnb;polygon;arbitrum;optimism;avax;cronos;platon;moonbeam;fantom;gnosis`,
{
headers: {
"X-API-KEY": NFTSCAN_API_KEY,
},
},
)
).json()
if (result.data) {
redis?.set(redisKey, JSON.stringify(result.data), "EX", 60 * 60 * 24)
}
return result.data
}
}

View File

@ -1,21 +0,0 @@
import Unidata from "unidata.js"
import { IPFS_GATEWAY } from "../lib/env"
import {
ALCHEMY_ETHEREUM_API_KEY,
ALCHEMY_POLYGON_API_KEY,
MORALIS_WEB3_API_KEY,
NFTSCAN_API_KEY,
OPENSEA_API_KEY,
POAP_API_KEY,
} from "../lib/env.server"
export default new Unidata({
ipfsGateway: IPFS_GATEWAY,
moralisWeb3APIKey: MORALIS_WEB3_API_KEY,
alchemyEthereumAPIKey: ALCHEMY_ETHEREUM_API_KEY,
alchemyPolygonAPIKey: ALCHEMY_POLYGON_API_KEY,
nftscanAPIKey: NFTSCAN_API_KEY,
openseaAPIKey: OPENSEA_API_KEY,
poapAPIKey: POAP_API_KEY,
})