import { ALCHEMY_API_KEY as e, chainIds as t, isSolanaLike as n, networkRegistry as r } from "../../constants/chains.constants.mjs";
import { SOLANA_NFT_INTERFACES as i, TokenType as a, solanaNativeAddress as o, zeroAddress as s } from "../../constants/protocol.constants.mjs";
import { StorageKeys as c } from "../../types/cache.types.mjs";
import { overlayTokenWithRegistryData as l } from "../../functions/utils/erc20tokenFunctions.mjs";
import { caseInsensitiveEqual as u, lowerCaseIncludes as d } from "../../functions/utils/caseInsensitive.utils.mjs";
import { httpClient as f } from "../http/HttpClient.mjs";
import "../http/index.mjs";
import { API as p } from "../../API/API.mjs";
import { Logger as m } from "../../error-handling/logger.mjs";
import { getStateKey as h } from "../../functions/utils/string.utils.mjs";
import { tokenVisibilityStorage as g } from "./token-visibility-db.mjs";
import { createCustomSolanaConnection as _ } from "../../functions/utils/create-provider.mjs";
import { getPublicBalanceByTokenAddress as v } from "../../functions/utils/publicBalance.utils.mjs";
import { PublicKey as y } from "@solana/web3.js";
import { get as b, update as x } from "idb-keyval";
import { TOKEN_2022_PROGRAM_ID as S, TOKEN_PROGRAM_ID as C } from "@solana/spl-token";
var w = new class {
	fetchedTokens = {};
	ALCHEMY_URLS = {
		[t.ethMainnet]: "eth-mainnet",
		[t.polygon]: "polygon-mainnet",
		[t.arbMainnet]: "arb-mainnet",
		[t.optimism]: "opt-mainnet",
		[t.base]: "base-mainnet",
		[t.solanaMainnet]: "solana-mainnet"
	};
	KNOWN_GOOD_NFTS = {
		[t.base]: ["0x20bc8d248fe9c18a947406cea50d716828ab821f"],
		[t.optimism]: ["0x3c3e0e73b53a19bcaf4178b8f9c9428fd2a2775e"]
	};
	isWhitelistedNFT(e, t) {
		return this.KNOWN_GOOD_NFTS[t] ? this.KNOWN_GOOD_NFTS[t].some((t) => u(e, t)) : !1;
	}
	async getPublicTokens(e, t) {
		let n = h(t, e);
		return ((await b(c.ALCHEMY_PUBLIC_TOKENS))?.[n] || []).map((t) => l(t, e));
	}
	getFetchedTokens(e, t) {
		let n = h(t, e);
		return (this.fetchedTokens[n] || []).map((t) => l(t, e));
	}
	fetchAndUpdatePublicTokens(e, t) {
		return n(t) ? this.fetchAndUpdateSolanaTokens(e, t) : this.fetchAndUpdateEVMTokens(e, t);
	}
	async fetchAndUpdateSolanaTokens(e, t) {
		let n = h(e, t), [r, i] = await Promise.all([this.getSolanaTokenAddresses(t, e), this.getNFTAddresses(t, e)]), a = Array.from(new Set([...r, ...i]));
		if (!a.length) return;
		let o = (await p.tokensInfoCall(t, a)).filter((e) => !!e), s = await g.hideSpamsAndFetchHiddenTokensAddresses(t, o), l = o.filter((e) => !d(s, e.erc20TokenAddress));
		await x(c.ALCHEMY_PUBLIC_TOKENS, (e = {}) => {
			let t = {
				...e,
				[n]: l
			};
			return this.fetchedTokens = t, t;
		});
	}
	async getWalletTokenAddresses(e, t) {
		return n(e) ? this.getSolanaTokenAddresses(e, t) : this.getEVMTokenAddresses(e, t);
	}
	async fetchAndUpdateEVMTokens(e, t) {
		let n = h(e, t), r = await this.getEVMTokenAddresses(t, e), i = await this.getNFTTokens(t, e), o = i.map((e) => e.contractAddress), s = [...r, ...o];
		if (!s.length) return;
		let l = (await p.tokensInfoCall(t, s)).map((e, n) => {
			if (e) return e;
			let r = s[n], o = i.find((e) => u(e.contractAddress, r));
			return o && o.tokenIds && o.tokenIds.length > 0 && this.isWhitelistedNFT(r, t) ? {
				chainId: t,
				name: "Unknown NFT",
				symbol: "NFT",
				erc20TokenAddress: r,
				decimals: 0,
				nftTokenType: a.ERC721,
				tokenIds: o.tokenIds
			} : null;
		}).filter((e) => !!e), f = await g.hideSpamsAndFetchHiddenTokensAddresses(t, l), m = l.filter((e) => !d(f, e.erc20TokenAddress)).map((e) => ({
			...e,
			tokenIds: i.find((t) => u(t.contractAddress, e.erc20TokenAddress))?.tokenIds
		}));
		await x(c.ALCHEMY_PUBLIC_TOKENS, (e = {}) => {
			let t = {
				...e,
				[n]: m
			};
			return this.fetchedTokens = t, t;
		});
	}
	async getSolanaTokenAddresses(e, t) {
		let { fetchRpcUrl: n } = r[e];
		if (!n) throw Error("RPC URL not found for the specified chain ID");
		let i = _(n), a = await i.getBalance(new y(t)), [s, c] = await Promise.all([i.getParsedTokenAccountsByOwner(new y(t), { programId: C }), i.getParsedTokenAccountsByOwner(new y(t), { programId: S })]), l = [...s.value, ...c.value].filter((e) => {
			let { amount: t } = e.account.data.parsed.info.tokenAmount;
			return BigInt(t) > 0n;
		}).map((e) => e.account.data.parsed.info.mint);
		return a > 0 && l.unshift(o), Array.from(new Set(l));
	}
	async getNFTAddresses(e, t) {
		let { fetchRpcUrl: n } = r[e];
		if (!n) throw Error("RPC URL not found for the specified chain ID");
		try {
			let e = [], r = 1;
			do {
				let { items: a = [], page: o } = (await f.post(n, {
					jsonrpc: "2.0",
					id: 1,
					method: "getAssetsByOwner",
					params: {
						ownerAddress: t,
						page: r,
						limit: 1e3,
						displayOptions: {
							showFungible: !1,
							showCollectionMetadata: !0
						}
					}
				})).result || {}, s = a.filter((e) => e.interface && i.includes(e.interface)).map((e) => e.id);
				e.push(...s), r = o && o > r ? o : 0;
			} while (r > 0);
			return e;
		} catch (e) {
			return m.error("Failed to fetch NFTs from DAS API", e), [];
		}
	}
	async getEVMTokenAddresses(t, n) {
		let r = await v(t, n, s), i = `https://${this.ALCHEMY_URLS[t]}.g.alchemy.com/v2/${e}`, a = [], o;
		do {
			let e = [n, "erc20"];
			o && e.push({ pageKey: o });
			let { result: t } = await f.post(i, {
				jsonrpc: "2.0",
				id: 1,
				method: "alchemy_getTokenBalances",
				params: e
			});
			a.push(...t?.tokenBalances ?? []), o = t?.pageKey;
		} while (o);
		r && r > 0n && a.push({
			contractAddress: s,
			tokenBalance: r.toString()
		});
		let c = {};
		return a.forEach((e) => {
			(!c[e.contractAddress] || BigInt(e.tokenBalance) > 0n) && (c[e.contractAddress] = e);
		}), Object.keys(c);
	}
	async getNFTTokens(t, n) {
		let r = `https://${this.ALCHEMY_URLS[t]}.g.alchemy.com/nft/v3/${e}/getNFTsForOwner`, i = [], a;
		do {
			let e = new URLSearchParams({
				owner: n,
				withMetadata: "false",
				pageSize: "100"
			});
			a && e.append("pageKey", a);
			let { ownedNfts: t = [], pageKey: o } = await f.get(`${r}?${e.toString()}`);
			i.push(...t), a = o;
		} while (a);
		let o = i.filter(({ contractAddress: e, tokenId: t }) => e && t).reduce((e, { contractAddress: t, tokenId: n }) => {
			let r = t.toLowerCase();
			return e.has(r) || e.set(r, []), e.get(r)?.push(n), e;
		}, /* @__PURE__ */ new Map());
		return Array.from(o.entries()).map(([e, t]) => ({
			contractAddress: e,
			tokenIds: t
		}));
	}
}();
//#endregion
export { w as alchemyPublicTokensDB };
