Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nifty asset pages #294

Merged
merged 6 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"dependencies": {
"@coral-xyz/anchor": "^0.29.0",
"@lottiefiles/svelte-lottie-player": "^0.3.0",
"@nifty-oss/asset": "^0.3.0",
"@onsol/tldparser": "^0.5.3",
"@solana/spl-account-compression": "^0.1.8",
"@solana/spl-token-registry": "^0.2.4574",
Expand Down
98 changes: 98 additions & 0 deletions src/lib/components/providers/nifty-asset-provider.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<script lang="ts">
import type { UINiftyAsset, UITokenMetadata } from "$lib/types";
import { SOL } from "$lib/xray";
import { page } from "$app/stores";
import { trpcWithQuery } from "$lib/trpc/client";
import IntersectionObserver from "svelte-intersection-observer";
import { ExtensionType, getExtension } from "@nifty-oss/asset";

export let address: string | undefined = undefined;

export let asset: UINiftyAsset | undefined = undefined;

export let status: { isLoading: boolean; isError: boolean } = {
isError: false,
isLoading: true,
};

let intersecting = false;
const params = new URLSearchParams(window.location.search);
const network = params.get("network");
const isMainnetValue = network !== "devnet";
const client = trpcWithQuery($page);

let account: any | undefined;

if (address) {
account = client.niftyAsset.createQuery([address, isMainnetValue], {
refetchOnMount: false,
refetchOnWindowFocus: false,
});
}

$: if ($account && $account.data) {
status = { isError: $account.isError, isLoading: $account.isLoading };
updateAsset($account.data[0]);
}

const updateAsset = (data: UINiftyAsset) => {
asset = data;

if (asset) {
const metadata = getExtension(asset, ExtensionType.Metadata);

if (metadata && metadata.uri) {
(async () => {
try {
asset.json = await fetchJsonMetadata(metadata.uri);
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error in fetchJsonMetadata:", error);
}
})();
}
}
};

const fetchJsonMetadata = async (jsonUri: string) => {
try {
const response = await fetch(jsonUri);
if (!response.ok) {
throw new Error(`Status ${response.status}`);
}
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
throw new TypeError("Received non-JSON content type");
}
return await response.json();
} catch (error) {
// eslint-disable-next-line no-console
console.error("Error fetching or parsing JSON metadata:", error);
return "";
}
};

let element: HTMLDivElement;

$: loading = address !== SOL && status.isLoading;

$: failed = status.isError;
</script>

<div>
<IntersectionObserver
once={true}
{element}
bind:intersecting
>
<div bind:this={element} />

{#if intersecting}
<slot
{asset}
{loading}
{failed}
/>
{/if}
</IntersectionObserver>
</div>
2 changes: 2 additions & 0 deletions src/lib/trpc/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { balances } from "$lib/trpc/routes/balances";
import { concurrentMerkleTree } from "$lib/trpc/routes/concurrent-merkle-tree";
import { currentSlot } from "$lib/trpc/routes/current-slot";
import { deprecatedImage } from "./routes/deprecated-image";
import { niftyAsset } from "$lib/trpc/routes/nifty-asset";
import { price } from "$lib/trpc/routes/price";
import { rawTransaction } from "$lib/trpc/routes/raw-transaction";
import { searchAssets } from "./routes/search-assets";
Expand All @@ -33,6 +34,7 @@ export const router = t.router({
concurrentMerkleTree,
currentSlot,
deprecatedImage,
niftyAsset,
price,
rawTransaction,
searchAssets,
Expand Down
32 changes: 32 additions & 0 deletions src/lib/trpc/routes/nifty-asset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { z } from "zod";

import { t } from "$lib/trpc/t";

import { connect } from "$lib/xray";
import { LAMPORTS_PER_SOL, PublicKey, Connection } from "@solana/web3.js";
import { getRPCUrl } from "$lib/util/get-rpc-url";

import { HELIUS_API_KEY } from "$env/static/private";
import { getAssetAccountDataSerializer } from "@nifty-oss/asset";

export const niftyAsset = t.procedure
.input(z.tuple([z.string(), z.boolean()]))
.query(async ({ input }) => {
const [address, isMainnet] = input;
const connection = new Connection(
getRPCUrl(`?api-key=${HELIUS_API_KEY}`, isMainnet),
"confirmed"
);

const pubKey = new PublicKey(address);

const accountInfo = await connection.getAccountInfo(pubKey);

if (accountInfo) {
return getAssetAccountDataSerializer().deserialize(
accountInfo.data
);
}

return null;
});
6 changes: 6 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import type { EnrichedTransaction } from "helius-sdk";
import type { ProtonTransaction, ProtonTransactionAction } from "$lib/xray";

import type { IconPaths, modals } from "$lib/config";

import type { SOL } from "$lib/xray";

import type { Asset } from "@nifty-oss/asset";

export * from "$lib/config";

export interface UIConfig {
Expand Down Expand Up @@ -143,3 +146,6 @@ export type UISolAccountToken = {
balanceInUSD: number;
price: number;
};

/** Used in the Nifty Asset pages. */
export type UINiftyAsset = Asset & { json: any };
4 changes: 4 additions & 0 deletions src/lib/util/stores/nifty-asset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { UINiftyAsset } from "$lib/types";
import { writable } from "svelte/store";

export const niftyAssetStore = writable<UINiftyAsset | null>(null);
23 changes: 14 additions & 9 deletions src/lib/xray/lib/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TldParser } from "@onsol/tldparser";
import { browser } from "$app/environment";

import getJupiterTokens from "$lib/util/get-tokens";
import { ASSET_PROGRAM_ID } from "@nifty-oss/asset";

export interface SearchResult {
url: string;
Expand All @@ -23,6 +24,7 @@ type SearchResultType =
| "transaction"
| "bonfida-domain"
| "ans-domain"
| "nifty-asset"
| null;

const searchDefaults: SearchResult = {
Expand Down Expand Up @@ -60,16 +62,19 @@ export const search = async (
const pubkey = new PublicKey(query);
const account = await connection.getAccountInfo(pubkey);
const program = account?.owner.toString();

const probablyToken =
program === "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ||
program === "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" ||
account === null;

let addressType!: "token" | "account";
if (probablyToken) {
addressType = "token";
let addressType!: "token" | "account" | "nifty-asset";

if (account) {
if (
program === "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ||
program === "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
) {
addressType = "token";
} else if (program === ASSET_PROGRAM_ID) {
addressType = "nifty-asset";
}
}

addressType ??= "account";

return {
Expand Down
6 changes: 4 additions & 2 deletions src/routes/account/[account]/assets/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,17 @@

{#if asset.burnt !== true}
<a
href="/token/{asset.id}?network={isMainnetValue ? 'mainnet' : 'devnet'}"
href="/token/{asset.id}?network={isMainnetValue
? 'mainnet'
: 'devnet'}"
>
<Image
src={image?.uri}
className="aspect-square w-full rounded-lg"
alt=""
/>
</a>
{/if}
{/if}
{/each}
{/each}
</div>
Expand Down
Loading
Loading