@xyo-network/crypto-nft-collection-witness-plugin 2.79.3 → 2.79.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/Plugin.d.cts +1 -21
- package/dist/browser/Plugin.d.cts.map +1 -1
- package/dist/browser/Plugin.d.mts +1 -21
- package/dist/browser/Plugin.d.mts.map +1 -1
- package/dist/browser/Plugin.d.ts +1 -21
- package/dist/browser/Plugin.d.ts.map +1 -1
- package/dist/browser/Witness.d.cts +3 -10
- package/dist/browser/Witness.d.cts.map +1 -1
- package/dist/browser/Witness.d.mts +3 -10
- package/dist/browser/Witness.d.mts.map +1 -1
- package/dist/browser/Witness.d.ts +3 -10
- package/dist/browser/Witness.d.ts.map +1 -1
- package/dist/browser/index.cjs +43 -41
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.js +44 -45
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/lib/getNftCollectionNfts.d.cts.map +1 -1
- package/dist/browser/lib/getNftCollectionNfts.d.mts.map +1 -1
- package/dist/browser/lib/getNftCollectionNfts.d.ts.map +1 -1
- package/dist/node/Plugin.d.cts +1 -21
- package/dist/node/Plugin.d.cts.map +1 -1
- package/dist/node/Plugin.d.mts +1 -21
- package/dist/node/Plugin.d.mts.map +1 -1
- package/dist/node/Plugin.d.ts +1 -21
- package/dist/node/Plugin.d.ts.map +1 -1
- package/dist/node/Witness.d.cts +3 -10
- package/dist/node/Witness.d.cts.map +1 -1
- package/dist/node/Witness.d.mts +3 -10
- package/dist/node/Witness.d.mts.map +1 -1
- package/dist/node/Witness.d.ts +3 -10
- package/dist/node/Witness.d.ts.map +1 -1
- package/dist/node/index.js +43 -41
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +44 -45
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/lib/getNftCollectionNfts.d.cts.map +1 -1
- package/dist/node/lib/getNftCollectionNfts.d.mts.map +1 -1
- package/dist/node/lib/getNftCollectionNfts.d.ts.map +1 -1
- package/package.json +15 -16
- package/src/Witness.ts +7 -24
- package/src/lib/getNftCollectionNfts.ts +48 -42
package/dist/node/index.mjs
CHANGED
|
@@ -6,7 +6,6 @@ import { createPayloadSetWitnessPlugin } from "@xyo-network/payloadset-plugin";
|
|
|
6
6
|
// src/Witness.ts
|
|
7
7
|
import { assertEx } from "@xylabs/assert";
|
|
8
8
|
import { EthAddress } from "@xylabs/eth-address";
|
|
9
|
-
import { AbstractWitness } from "@xyo-network/abstract-witness";
|
|
10
9
|
import { PayloadHasher } from "@xyo-network/core";
|
|
11
10
|
import {
|
|
12
11
|
isNftCollectionWitnessQuery,
|
|
@@ -14,7 +13,7 @@ import {
|
|
|
14
13
|
NftCollectionWitnessConfigSchema
|
|
15
14
|
} from "@xyo-network/crypto-nft-collection-payload-plugin";
|
|
16
15
|
import { ERC721Enumerable__factory as ERC721Enumerable__factory2 } from "@xyo-network/open-zeppelin-typechain";
|
|
17
|
-
import {
|
|
16
|
+
import { AbstractBlockchainWitness } from "@xyo-network/witness-blockchain-abstract";
|
|
18
17
|
|
|
19
18
|
// src/lib/collectionMetrics/lib/calculateAllPropertiesDistribution.ts
|
|
20
19
|
var calculateAllPropertiesDistribution = (array) => {
|
|
@@ -96,13 +95,12 @@ var contractHasFunctions = async (provider, address, contractInterface, function
|
|
|
96
95
|
};
|
|
97
96
|
|
|
98
97
|
// src/lib/getNftCollectionNfts.ts
|
|
99
|
-
import { AxiosJson } from "@
|
|
98
|
+
import { AxiosJson } from "@xylabs/axios";
|
|
99
|
+
import { exists } from "@xylabs/exists";
|
|
100
|
+
import { getErc1967Status } from "@xyo-network/blockchain-erc1967-witness";
|
|
100
101
|
import { NftSchema, toTokenType } from "@xyo-network/crypto-nft-payload-plugin";
|
|
101
102
|
import { ERC721Enumerable__factory, ERC721URIStorage__factory, ERC1155Supply__factory } from "@xyo-network/open-zeppelin-typechain";
|
|
102
|
-
import {
|
|
103
|
-
checkIpfsUrl,
|
|
104
|
-
getErc1967Status
|
|
105
|
-
} from "@xyo-network/witness-blockchain-abstract";
|
|
103
|
+
import { checkIpfsUrl } from "@xyo-network/witness-blockchain-abstract";
|
|
106
104
|
|
|
107
105
|
// src/lib/tokenTypes.ts
|
|
108
106
|
import { ERC1155URIStorage__factory, IERC721Metadata__factory } from "@xyo-network/open-zeppelin-typechain";
|
|
@@ -139,6 +137,9 @@ var tryCall = async (func, name) => {
|
|
|
139
137
|
|
|
140
138
|
// src/lib/getNftCollectionNfts.ts
|
|
141
139
|
var ipfsGateway = "5d7b6582.beta.decentralnetworkservices.com";
|
|
140
|
+
function range(size, startAt = 0) {
|
|
141
|
+
return [...Array(size).keys()].map((i) => i + startAt);
|
|
142
|
+
}
|
|
142
143
|
var getNftCollectionNfts = async (contractAddress, provider, types, maxNfts = 100) => {
|
|
143
144
|
try {
|
|
144
145
|
const { implementation } = await getErc1967Status(provider, contractAddress);
|
|
@@ -147,39 +148,41 @@ var getNftCollectionNfts = async (contractAddress, provider, types, maxNfts = 10
|
|
|
147
148
|
const storage = ERC721URIStorage__factory.connect(implementation, provider);
|
|
148
149
|
const supply1155 = ERC1155Supply__factory.connect(implementation, provider);
|
|
149
150
|
const finalTypes = types ?? await tokenTypes(provider, implementation);
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
const maxNftsArray = range(maxNfts);
|
|
152
|
+
const result = (await Promise.all(
|
|
153
|
+
maxNftsArray.map(async (_value, i) => {
|
|
154
|
+
const tokenId = await tryCall(async () => (await enumerable.tokenByIndex(i)).toHexString()) ?? `${i}`;
|
|
155
|
+
if (tokenId !== void 0) {
|
|
156
|
+
const supply = finalTypes.includes(toTokenType("ERC1155")) ? await tryCall(async () => (await supply1155.totalSupply(tokenId)).toHexString()) ?? "0x01" : "0x01";
|
|
157
|
+
const metadataUri = await tryCall(async () => await storage.tokenURI(tokenId));
|
|
158
|
+
const checkedMetaDataUri = metadataUri ? checkIpfsUrl(metadataUri, ipfsGateway) : void 0;
|
|
159
|
+
let metadata = void 0;
|
|
160
|
+
if (checkedMetaDataUri !== void 0) {
|
|
161
|
+
try {
|
|
162
|
+
metadata = (await axios.get(checkedMetaDataUri)).data;
|
|
163
|
+
} catch (ex) {
|
|
164
|
+
const error = ex;
|
|
165
|
+
console.error(`Get Metadata failed: ${error.message}`);
|
|
166
|
+
}
|
|
164
167
|
}
|
|
168
|
+
const info = {
|
|
169
|
+
address: contractAddress,
|
|
170
|
+
chainId: provider.network.chainId,
|
|
171
|
+
metadata,
|
|
172
|
+
metadataUri,
|
|
173
|
+
schema: NftSchema,
|
|
174
|
+
supply,
|
|
175
|
+
tokenId,
|
|
176
|
+
type: finalTypes.at(0),
|
|
177
|
+
types: finalTypes
|
|
178
|
+
};
|
|
179
|
+
if (implementation !== contractAddress) {
|
|
180
|
+
info.implementation = implementation;
|
|
181
|
+
}
|
|
182
|
+
return info;
|
|
165
183
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
chainId: provider.network.chainId,
|
|
169
|
-
metadata,
|
|
170
|
-
metadataUri,
|
|
171
|
-
schema: NftSchema,
|
|
172
|
-
supply,
|
|
173
|
-
tokenId,
|
|
174
|
-
type: finalTypes.at(0),
|
|
175
|
-
types: finalTypes
|
|
176
|
-
};
|
|
177
|
-
if (implementation !== contractAddress) {
|
|
178
|
-
info.implementation = implementation;
|
|
179
|
-
}
|
|
180
|
-
result.push(info);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
184
|
+
})
|
|
185
|
+
)).filter(exists);
|
|
183
186
|
return result;
|
|
184
187
|
} catch (ex) {
|
|
185
188
|
const error = ex;
|
|
@@ -198,20 +201,16 @@ function resolvedValue(settled, assert) {
|
|
|
198
201
|
}
|
|
199
202
|
return settled.status === "fulfilled" ? settled.value : void 0;
|
|
200
203
|
}
|
|
201
|
-
var CryptoNftCollectionWitness = class extends
|
|
204
|
+
var CryptoNftCollectionWitness = class extends AbstractBlockchainWitness {
|
|
202
205
|
static configSchemas = [NftCollectionWitnessConfigSchema];
|
|
203
|
-
providers = {};
|
|
204
|
-
getProvider(chainId) {
|
|
205
|
-
this.providers[chainId] = this.providers[chainId] ?? getProviderFromEnv(chainId);
|
|
206
|
-
return this.providers[chainId];
|
|
207
|
-
}
|
|
208
206
|
async observeHandler(payloads) {
|
|
209
207
|
await this.started("throw");
|
|
208
|
+
await this.getProviders();
|
|
210
209
|
const queries = (payloads == null ? void 0 : payloads.filter(isNftCollectionWitnessQuery)) ?? [];
|
|
211
210
|
const observations = await Promise.all(
|
|
212
211
|
queries.map(async (query) => {
|
|
213
212
|
const chainId = assertEx((query == null ? void 0 : query.chainId) || this.config.chainId, "params.chainId is required");
|
|
214
|
-
const provider = this.getProvider(
|
|
213
|
+
const provider = await this.getProvider(true, true);
|
|
215
214
|
const address = assertEx(
|
|
216
215
|
EthAddress.parse(assertEx((query == null ? void 0 : query.address) || this.config.address, "params.address is required")),
|
|
217
216
|
"Failed to parse params.address"
|
package/dist/node/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Plugin.ts","../../src/Witness.ts","../../src/lib/collectionMetrics/lib/calculateAllPropertiesDistribution.ts","../../src/lib/collectionMetrics/lib/probabilityDistributions/binomial/calculateBinomialParamsFromProbability.ts","../../src/lib/collectionMetrics/getNftCollectionMetrics.ts","../../src/lib/contractHasFunctions.ts","../../src/lib/getNftCollectionNfts.ts","../../src/lib/tokenTypes.ts","../../src/lib/tryCall.ts","../../src/index.ts"],"sourcesContent":["import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetWitnessPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { CryptoNftCollectionWitness } from './Witness'\n\nexport const CryptoNftCollectionWitnessPlugin = () =>\n createPayloadSetWitnessPlugin<CryptoNftCollectionWitness>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n witness: async (params) => {\n const result = await CryptoNftCollectionWitness.create(params)\n return result\n },\n },\n )\n","import { InfuraProvider, Provider, WebSocketProvider } from '@ethersproject/providers'\nimport { assertEx } from '@xylabs/assert'\nimport { EthAddress } from '@xylabs/eth-address'\nimport { AbstractWitness } from '@xyo-network/abstract-witness'\nimport { PayloadHasher } from '@xyo-network/core'\nimport {\n isNftCollectionWitnessQuery,\n NftCollectionInfo,\n NftCollectionSchema,\n NftCollectionWitnessConfig,\n NftCollectionWitnessConfigSchema,\n NftCollectionWitnessQuery,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { ERC721Enumerable__factory } from '@xyo-network/open-zeppelin-typechain'\nimport { getProviderFromEnv } from '@xyo-network/witness-blockchain-abstract'\nimport { WitnessParams } from '@xyo-network/witness-model'\n\nimport { getNftCollectionMetrics, getNftCollectionNfts, tokenTypes } from './lib'\n\nexport type CryptoNftCollectionWitnessParams = WitnessParams<\n AnyConfigSchema<NftCollectionWitnessConfig>,\n {\n provider: Provider\n }\n>\n\nconst defaultMaxNfts = 100\n\n/**\n * A \"no operation\" Promise to be used\n * when no action is desired but a Promise\n * is required to be returned\n */\nconst NoOp = Promise.resolve()\n\nfunction resolvedValue<T>(settled: PromiseSettledResult<T>, assert: true): T\nfunction resolvedValue<T>(settled: PromiseSettledResult<T>, assert?: false): T | undefined\nfunction resolvedValue<T>(settled: PromiseSettledResult<T>, assert?: boolean) {\n if (assert && settled.status === 'rejected') {\n throw settled.reason\n }\n return settled.status === 'fulfilled' ? settled.value : undefined\n}\n\nexport class CryptoNftCollectionWitness<TParams extends CryptoNftCollectionWitnessParams = CryptoNftCollectionWitnessParams> extends AbstractWitness<\n TParams,\n NftCollectionWitnessQuery,\n NftCollectionInfo\n> {\n static override configSchemas = [NftCollectionWitnessConfigSchema]\n\n protected providers: Record<number, WebSocketProvider | InfuraProvider> = {}\n\n protected getProvider(chainId: number) {\n this.providers[chainId] = this.providers[chainId] ?? getProviderFromEnv(chainId)\n return this.providers[chainId]\n }\n\n protected override async observeHandler(payloads?: NftCollectionWitnessQuery[]): Promise<NftCollectionInfo[]> {\n await this.started('throw')\n const queries = payloads?.filter(isNftCollectionWitnessQuery) ?? []\n const observations = await Promise.all(\n queries.map<Promise<NftCollectionInfo>>(async (query) => {\n const chainId = assertEx(query?.chainId || this.config.chainId, 'params.chainId is required')\n const provider = this.getProvider(chainId)\n const address = assertEx(\n EthAddress.parse(assertEx(query?.address || this.config.address, 'params.address is required')),\n 'Failed to parse params.address',\n ).toString()\n\n const erc721Enumerable = ERC721Enumerable__factory.connect(address, provider)\n\n const maxNfts = query?.maxNfts || defaultMaxNfts\n const [name, symbol, total, typesSettled, archivistSettled] = await Promise.allSettled([\n await erc721Enumerable.name(),\n await erc721Enumerable.symbol(),\n (await erc721Enumerable.totalSupply()).toNumber(),\n await tokenTypes(provider, address),\n await this.writeArchivist(),\n ])\n const types = resolvedValue(typesSettled, true)\n const nfts = await getNftCollectionNfts(address, provider, types, maxNfts)\n const metrics = getNftCollectionMetrics(nfts)\n const archivist = resolvedValue(archivistSettled)\n const [sources] = await Promise.all([\n // Hash all the payloads\n Promise.all(nfts.map((nft) => PayloadHasher.hashAsync(nft))),\n // Insert them into the archivist if we have one\n archivist ? archivist.insert(nfts) : NoOp,\n ])\n const payload: NftCollectionInfo = {\n address,\n chainId,\n metrics,\n name: resolvedValue(name, true),\n schema: NftCollectionSchema,\n sources,\n symbol: resolvedValue(symbol, true),\n total: resolvedValue(total, true),\n type: types.at(0),\n types,\n }\n return payload\n }),\n )\n return observations.flat()\n }\n}\n","import { Distribution } from './distribution'\n\nexport const calculateAllPropertiesDistribution = <T>(array: T[]): Distribution<T> => {\n const distribution: Distribution<T> = {}\n\n array.forEach((item) => {\n for (const property in item) {\n if (Object.prototype.hasOwnProperty.call(item, property)) {\n const value = item[property as keyof T]\n if (value !== undefined && value !== null) {\n const valueString = value.toString()\n if (!distribution[property]) {\n distribution[property] = { [valueString]: 1 }\n } else if (!distribution[property]![valueString]) {\n ;(distribution[property] as Record<string, number>)[valueString] = 1\n } else {\n ;(distribution[property] as Record<string, number>)[valueString] += 1\n }\n }\n }\n }\n })\n\n return distribution\n}\n","import { BinomialDistributionParameters } from '@xyo-network/crypto-nft-collection-payload-plugin'\n\n/**\n * Calculates the parameters of a binomial distribution given the number of trials and success probability\n * @param n Number of trials\n * @param p Success probability\n * @returns The binomial distribution parameters\n */\nexport const calculateBinomialParamsFromProbability = (n: number, p: number): BinomialDistributionParameters => {\n // Mean (µ)\n const mean = n * p\n\n // Variance (σ^2)\n const variance = n * p * (1 - p)\n\n // Standard Deviation (σ)\n const stdDev = Math.sqrt(variance)\n\n return { mean, p, stdDev, variance }\n}\n","import { NftCollectionMetrics, NftTraitMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { NftInfoFields, OpenSeaNftAttribute } from '@xyo-network/crypto-nft-payload-plugin'\n\nimport { calculateAllPropertiesDistribution, calculateBinomialParamsFromProbability } from './lib'\n\ntype TraitDistributionEntry = [string, { [key: string]: number }]\n\nexport const getNftCollectionMetrics = (nfts: NftInfoFields[]): NftCollectionMetrics => {\n const traits = nfts\n .map((nft) => nft?.metadata?.attributes as OpenSeaNftAttribute[] | undefined)\n .filter((v): v is OpenSeaNftAttribute[] => v !== undefined)\n .map((attributes) => {\n return Object.fromEntries(attributes.map((attribute) => [attribute.trait_type, attribute.value]))\n })\n const distribution = calculateAllPropertiesDistribution(traits)\n const n = nfts.length\n const attributes = Object.fromEntries(\n Object.entries(distribution)\n .filter((v): v is TraitDistributionEntry => v[1] !== undefined)\n .map(([trait, entries]) => {\n const traitCount = Object.values(entries).reduce((prev, curr) => prev + curr, 0)\n const { p } = calculateBinomialParamsFromProbability(nfts.length, traitCount / n)\n const values = Object.fromEntries(\n Object.entries(entries).map(([value, traitValueCount]) => {\n const { p } = calculateBinomialParamsFromProbability(n, traitValueCount / n)\n const metrics: NftTraitMetrics = { binomial: { p }, count: traitValueCount }\n return [value, metrics]\n }),\n )\n return [trait, { metrics: { binomial: { p }, count: traitCount }, values }]\n }),\n )\n return { metadata: { attributes } }\n}\n","import { Interface } from '@ethersproject/abi'\nimport { Provider } from '@ethersproject/providers'\n\nexport const contractHasFunctions = async (provider: Provider, address: string, contractInterface: Interface, functionNames: string[]) => {\n try {\n const bytecode = await provider.getCode(address, 'latest')\n for (let i = 0; i < functionNames.length; i++) {\n const nameSig = contractInterface.getSighash(functionNames[i]).substring(2)\n if (!bytecode.includes(nameSig)) {\n return false\n }\n return true\n }\n return false\n } catch (ex) {\n const error = ex as Error\n console.log(error)\n return false\n }\n}\n","import { BaseProvider } from '@ethersproject/providers'\nimport { AxiosJson } from '@xyo-network/axios'\nimport { NftInfo, NftMetadata, NftSchema, TokenType, toTokenType } from '@xyo-network/crypto-nft-payload-plugin'\nimport { ERC721Enumerable__factory, ERC721URIStorage__factory, ERC1155Supply__factory } from '@xyo-network/open-zeppelin-typechain'\nimport {\n checkIpfsUrl,\n ERC1967_PROXY_IMPLEMENTATION_STORAGE_SLOT,\n getErc1967Status,\n readAddressFromSlot,\n} from '@xyo-network/witness-blockchain-abstract'\n\nimport { tokenTypes } from './tokenTypes'\nimport { tryCall } from './tryCall'\n\nconst ipfsGateway = '5d7b6582.beta.decentralnetworkservices.com'\n\nexport const getNftCollectionNfts = async (\n /**\n * The address of the NFT contract to search for\n */\n contractAddress: string,\n /**\n * The chain ID (1 = Ethereum Mainnet, 4 = Rinkeby, etc.) of the chain to search for NFTs on\n */\n provider: BaseProvider,\n types?: TokenType[],\n /**\n * The maximum number of NFTs to return. Configurable to prevent\n * large wallets from exhausting Infura API credits. Ideally a\n * multiple of 100 as that appears to be the default page size.\n */\n maxNfts = 100,\n): Promise<NftInfo[]> => {\n try {\n //Check if ERC-1967 Upgradeable\n const { implementation } = await getErc1967Status(provider, contractAddress)\n\n const axios = new AxiosJson({ timeout: 2000 })\n const enumerable = ERC721Enumerable__factory.connect(implementation, provider)\n const storage = ERC721URIStorage__factory.connect(implementation, provider)\n const supply1155 = ERC1155Supply__factory.connect(implementation, provider)\n const finalTypes = types ?? (await tokenTypes(provider, implementation))\n const result: NftInfo[] = []\n\n for (let i = 0; i < maxNfts; i++) {\n const tokenId = await tryCall(async () => (await enumerable.tokenByIndex(i)).toHexString())\n if (tokenId !== undefined) {\n const supply = finalTypes.includes(toTokenType('ERC1155'))\n ? (await tryCall(async () => (await supply1155.totalSupply(tokenId)).toHexString())) ?? '0x01'\n : '0x01'\n const metadataUri = await tryCall(async () => await storage.tokenURI(tokenId))\n const checkedMetaDataUri = metadataUri ? checkIpfsUrl(metadataUri, ipfsGateway) : undefined\n let metadata: NftMetadata | undefined = undefined\n if (checkedMetaDataUri !== undefined) {\n try {\n metadata = (await axios.get(checkedMetaDataUri)).data\n } catch (ex) {\n const error = ex as Error\n console.error(`Get Metadata failed: ${error.message}`)\n }\n }\n\n const info: NftInfo = {\n address: contractAddress,\n chainId: provider.network.chainId,\n metadata,\n metadataUri,\n schema: NftSchema,\n supply,\n tokenId,\n type: finalTypes.at(0),\n types: finalTypes,\n }\n if (implementation !== contractAddress) {\n info.implementation = implementation\n }\n result.push(info)\n }\n }\n return result\n } catch (ex) {\n const error = ex as Error\n console.error(`getNftCollectionNfts failed: [${error.name}] ${error.message}`)\n console.log(error.stack)\n return []\n }\n}\n","import { Provider } from '@ethersproject/providers'\nimport { TokenType } from '@xyo-network/crypto-nft-payload-plugin'\nimport { ERC1155URIStorage__factory, IERC721Metadata__factory } from '@xyo-network/open-zeppelin-typechain'\n\nimport { contractHasFunctions } from './contractHasFunctions'\n\nexport const isErc1155 = async (provider: Provider, address: string) => {\n return await contractHasFunctions(provider, address, ERC1155URIStorage__factory.createInterface(), ['uri'])\n}\n\nexport const isErc721 = async (provider: Provider, address: string) => {\n return await contractHasFunctions(provider, address, IERC721Metadata__factory.createInterface(), ['name', 'symbol', 'tokenURI'])\n}\n\nexport const tokenTypes = async (provider: Provider, address: string) => {\n const [erc721, erc1155] = await Promise.all([isErc721(provider, address), isErc1155(provider, address)])\n const result: TokenType[] = []\n if (erc721) {\n result.push('ERC721')\n }\n if (erc1155) {\n result.push('ERC1155')\n }\n return result\n}\n","export const tryCall = async <T>(func: () => Promise<T>, name?: string): Promise<T | undefined> => {\n try {\n return await func()\n } catch (ex) {\n if (name) {\n const error = ex as Error\n console.log(`tryCall failed [${name}]: ${error.message}`)\n }\n return undefined\n }\n}\n","import { CryptoNftCollectionWitnessPlugin } from './Plugin'\n\nexport * from './lib'\nexport * from './Witness'\n\nexport { CryptoNftCollectionWitnessPlugin }\n\n// eslint-disable-next-line import/no-default-export\nexport default CryptoNftCollectionWitnessPlugin\n"],"mappings":"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","names":["NftSchema","ERC721Enumerable__factory","attributes","p","ERC721Enumerable__factory","NftSchema"]}
|
|
1
|
+
{"version":3,"sources":["../../src/Plugin.ts","../../src/Witness.ts","../../src/lib/collectionMetrics/lib/calculateAllPropertiesDistribution.ts","../../src/lib/collectionMetrics/lib/probabilityDistributions/binomial/calculateBinomialParamsFromProbability.ts","../../src/lib/collectionMetrics/getNftCollectionMetrics.ts","../../src/lib/contractHasFunctions.ts","../../src/lib/getNftCollectionNfts.ts","../../src/lib/tokenTypes.ts","../../src/lib/tryCall.ts","../../src/index.ts"],"sourcesContent":["import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetWitnessPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { CryptoNftCollectionWitness } from './Witness'\n\nexport const CryptoNftCollectionWitnessPlugin = () =>\n createPayloadSetWitnessPlugin<CryptoNftCollectionWitness>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n witness: async (params) => {\n const result = await CryptoNftCollectionWitness.create(params)\n return result\n },\n },\n )\n","import { assertEx } from '@xylabs/assert'\nimport { EthAddress } from '@xylabs/eth-address'\nimport { PayloadHasher } from '@xyo-network/core'\nimport {\n isNftCollectionWitnessQuery,\n NftCollectionInfo,\n NftCollectionSchema,\n NftCollectionWitnessConfig,\n NftCollectionWitnessConfigSchema,\n NftCollectionWitnessQuery,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { ERC721Enumerable__factory } from '@xyo-network/open-zeppelin-typechain'\nimport { AbstractBlockchainWitness, BlockchainWitnessParams } from '@xyo-network/witness-blockchain-abstract'\n\nimport { getNftCollectionMetrics, getNftCollectionNfts, tokenTypes } from './lib'\n\nexport type CryptoNftCollectionWitnessParams = BlockchainWitnessParams<NftCollectionWitnessConfig>\n\nconst defaultMaxNfts = 100\n\n/**\n * A \"no operation\" Promise to be used\n * when no action is desired but a Promise\n * is required to be returned\n */\nconst NoOp = Promise.resolve()\n\nfunction resolvedValue<T>(settled: PromiseSettledResult<T>, assert: true): T\nfunction resolvedValue<T>(settled: PromiseSettledResult<T>, assert?: false): T | undefined\nfunction resolvedValue<T>(settled: PromiseSettledResult<T>, assert?: boolean) {\n if (assert && settled.status === 'rejected') {\n throw settled.reason\n }\n return settled.status === 'fulfilled' ? settled.value : undefined\n}\n\nexport class CryptoNftCollectionWitness<\n TParams extends CryptoNftCollectionWitnessParams = CryptoNftCollectionWitnessParams,\n> extends AbstractBlockchainWitness<TParams, NftCollectionWitnessQuery, NftCollectionInfo> {\n static override configSchemas = [NftCollectionWitnessConfigSchema]\n\n protected override async observeHandler(payloads?: NftCollectionWitnessQuery[]): Promise<NftCollectionInfo[]> {\n await this.started('throw')\n await this.getProviders() //make sure cache clears\n const queries = payloads?.filter(isNftCollectionWitnessQuery) ?? []\n const observations = await Promise.all(\n queries.map<Promise<NftCollectionInfo>>(async (query) => {\n const chainId = assertEx(query?.chainId || this.config.chainId, 'params.chainId is required')\n const provider = await this.getProvider(true, true)\n const address = assertEx(\n EthAddress.parse(assertEx(query?.address || this.config.address, 'params.address is required')),\n 'Failed to parse params.address',\n ).toString()\n\n const erc721Enumerable = ERC721Enumerable__factory.connect(address, provider)\n\n const maxNfts = query?.maxNfts || defaultMaxNfts\n const [name, symbol, total, typesSettled, archivistSettled] = await Promise.allSettled([\n await erc721Enumerable.name(),\n await erc721Enumerable.symbol(),\n (await erc721Enumerable.totalSupply()).toNumber(),\n await tokenTypes(provider, address),\n await this.writeArchivist(),\n ])\n const types = resolvedValue(typesSettled, true)\n const nfts = await getNftCollectionNfts(address, provider, types, maxNfts)\n const metrics = getNftCollectionMetrics(nfts)\n const archivist = resolvedValue(archivistSettled)\n const [sources] = await Promise.all([\n // Hash all the payloads\n Promise.all(nfts.map((nft) => PayloadHasher.hashAsync(nft))),\n // Insert them into the archivist if we have one\n archivist ? archivist.insert(nfts) : NoOp,\n ])\n const payload: NftCollectionInfo = {\n address,\n chainId,\n metrics,\n name: resolvedValue(name, true),\n schema: NftCollectionSchema,\n sources,\n symbol: resolvedValue(symbol, true),\n total: resolvedValue(total, true),\n type: types.at(0),\n types,\n }\n return payload\n }),\n )\n return observations.flat()\n }\n}\n","import { Distribution } from './distribution'\n\nexport const calculateAllPropertiesDistribution = <T>(array: T[]): Distribution<T> => {\n const distribution: Distribution<T> = {}\n\n array.forEach((item) => {\n for (const property in item) {\n if (Object.prototype.hasOwnProperty.call(item, property)) {\n const value = item[property as keyof T]\n if (value !== undefined && value !== null) {\n const valueString = value.toString()\n if (!distribution[property]) {\n distribution[property] = { [valueString]: 1 }\n } else if (!distribution[property]![valueString]) {\n ;(distribution[property] as Record<string, number>)[valueString] = 1\n } else {\n ;(distribution[property] as Record<string, number>)[valueString] += 1\n }\n }\n }\n }\n })\n\n return distribution\n}\n","import { BinomialDistributionParameters } from '@xyo-network/crypto-nft-collection-payload-plugin'\n\n/**\n * Calculates the parameters of a binomial distribution given the number of trials and success probability\n * @param n Number of trials\n * @param p Success probability\n * @returns The binomial distribution parameters\n */\nexport const calculateBinomialParamsFromProbability = (n: number, p: number): BinomialDistributionParameters => {\n // Mean (µ)\n const mean = n * p\n\n // Variance (σ^2)\n const variance = n * p * (1 - p)\n\n // Standard Deviation (σ)\n const stdDev = Math.sqrt(variance)\n\n return { mean, p, stdDev, variance }\n}\n","import { NftCollectionMetrics, NftTraitMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { NftInfoFields, OpenSeaNftAttribute } from '@xyo-network/crypto-nft-payload-plugin'\n\nimport { calculateAllPropertiesDistribution, calculateBinomialParamsFromProbability } from './lib'\n\ntype TraitDistributionEntry = [string, { [key: string]: number }]\n\nexport const getNftCollectionMetrics = (nfts: NftInfoFields[]): NftCollectionMetrics => {\n const traits = nfts\n .map((nft) => nft?.metadata?.attributes as OpenSeaNftAttribute[] | undefined)\n .filter((v): v is OpenSeaNftAttribute[] => v !== undefined)\n .map((attributes) => {\n return Object.fromEntries(attributes.map((attribute) => [attribute.trait_type, attribute.value]))\n })\n const distribution = calculateAllPropertiesDistribution(traits)\n const n = nfts.length\n const attributes = Object.fromEntries(\n Object.entries(distribution)\n .filter((v): v is TraitDistributionEntry => v[1] !== undefined)\n .map(([trait, entries]) => {\n const traitCount = Object.values(entries).reduce((prev, curr) => prev + curr, 0)\n const { p } = calculateBinomialParamsFromProbability(nfts.length, traitCount / n)\n const values = Object.fromEntries(\n Object.entries(entries).map(([value, traitValueCount]) => {\n const { p } = calculateBinomialParamsFromProbability(n, traitValueCount / n)\n const metrics: NftTraitMetrics = { binomial: { p }, count: traitValueCount }\n return [value, metrics]\n }),\n )\n return [trait, { metrics: { binomial: { p }, count: traitCount }, values }]\n }),\n )\n return { metadata: { attributes } }\n}\n","import { Interface } from '@ethersproject/abi'\nimport { Provider } from '@ethersproject/providers'\n\nexport const contractHasFunctions = async (provider: Provider, address: string, contractInterface: Interface, functionNames: string[]) => {\n try {\n const bytecode = await provider.getCode(address, 'latest')\n for (let i = 0; i < functionNames.length; i++) {\n const nameSig = contractInterface.getSighash(functionNames[i]).substring(2)\n if (!bytecode.includes(nameSig)) {\n return false\n }\n return true\n }\n return false\n } catch (ex) {\n const error = ex as Error\n console.log(error)\n return false\n }\n}\n","import { BaseProvider } from '@ethersproject/providers'\nimport { AxiosJson } from '@xylabs/axios'\nimport { exists } from '@xylabs/exists'\nimport { getErc1967Status } from '@xyo-network/blockchain-erc1967-witness'\nimport { NftInfo, NftMetadata, NftSchema, TokenType, toTokenType } from '@xyo-network/crypto-nft-payload-plugin'\nimport { ERC721Enumerable__factory, ERC721URIStorage__factory, ERC1155Supply__factory } from '@xyo-network/open-zeppelin-typechain'\nimport { checkIpfsUrl } from '@xyo-network/witness-blockchain-abstract'\n\nimport { tokenTypes } from './tokenTypes'\nimport { tryCall } from './tryCall'\n\nconst ipfsGateway = '5d7b6582.beta.decentralnetworkservices.com'\n\nfunction range(size: number, startAt: number = 0): ReadonlyArray<number> {\n return [...Array(size).keys()].map((i) => i + startAt)\n}\n\nexport const getNftCollectionNfts = async (\n /**\n * The address of the NFT contract to search for\n */\n contractAddress: string,\n /**\n * The chain ID (1 = Ethereum Mainnet, 4 = Rinkeby, etc.) of the chain to search for NFTs on\n */\n provider: BaseProvider,\n types?: TokenType[],\n /**\n * The maximum number of NFTs to return. Configurable to prevent\n * large wallets from exhausting Infura API credits. Ideally a\n * multiple of 100 as that appears to be the default page size.\n */\n maxNfts = 100,\n): Promise<NftInfo[]> => {\n try {\n //Check if ERC-1967 Upgradeable\n const { implementation } = await getErc1967Status(provider, contractAddress)\n\n const axios = new AxiosJson({ timeout: 2000 })\n const enumerable = ERC721Enumerable__factory.connect(implementation, provider)\n const storage = ERC721URIStorage__factory.connect(implementation, provider)\n const supply1155 = ERC1155Supply__factory.connect(implementation, provider)\n const finalTypes = types ?? (await tokenTypes(provider, implementation))\n\n const maxNftsArray = range(maxNfts)\n\n const result: NftInfo[] = (\n await Promise.all(\n maxNftsArray.map(async (_value, i) => {\n const tokenId = (await tryCall(async () => (await enumerable.tokenByIndex(i)).toHexString())) ?? `${i}`\n if (tokenId !== undefined) {\n const supply = finalTypes.includes(toTokenType('ERC1155'))\n ? (await tryCall(async () => (await supply1155.totalSupply(tokenId)).toHexString())) ?? '0x01'\n : '0x01'\n const metadataUri = await tryCall(async () => await storage.tokenURI(tokenId))\n const checkedMetaDataUri = metadataUri ? checkIpfsUrl(metadataUri, ipfsGateway) : undefined\n let metadata: NftMetadata | undefined = undefined\n if (checkedMetaDataUri !== undefined) {\n try {\n metadata = (await axios.get(checkedMetaDataUri)).data\n } catch (ex) {\n const error = ex as Error\n console.error(`Get Metadata failed: ${error.message}`)\n }\n }\n\n const info: NftInfo = {\n address: contractAddress,\n chainId: provider.network.chainId,\n metadata,\n metadataUri,\n schema: NftSchema,\n supply,\n tokenId,\n type: finalTypes.at(0),\n types: finalTypes,\n }\n if (implementation !== contractAddress) {\n info.implementation = implementation\n }\n return info\n }\n }),\n )\n ).filter(exists)\n return result\n } catch (ex) {\n const error = ex as Error\n console.error(`getNftCollectionNfts failed: [${error.name}] ${error.message}`)\n console.log(error.stack)\n return []\n }\n}\n","import { Provider } from '@ethersproject/providers'\nimport { TokenType } from '@xyo-network/crypto-nft-payload-plugin'\nimport { ERC1155URIStorage__factory, IERC721Metadata__factory } from '@xyo-network/open-zeppelin-typechain'\n\nimport { contractHasFunctions } from './contractHasFunctions'\n\nexport const isErc1155 = async (provider: Provider, address: string) => {\n return await contractHasFunctions(provider, address, ERC1155URIStorage__factory.createInterface(), ['uri'])\n}\n\nexport const isErc721 = async (provider: Provider, address: string) => {\n return await contractHasFunctions(provider, address, IERC721Metadata__factory.createInterface(), ['name', 'symbol', 'tokenURI'])\n}\n\nexport const tokenTypes = async (provider: Provider, address: string) => {\n const [erc721, erc1155] = await Promise.all([isErc721(provider, address), isErc1155(provider, address)])\n const result: TokenType[] = []\n if (erc721) {\n result.push('ERC721')\n }\n if (erc1155) {\n result.push('ERC1155')\n }\n return result\n}\n","export const tryCall = async <T>(func: () => Promise<T>, name?: string): Promise<T | undefined> => {\n try {\n return await func()\n } catch (ex) {\n if (name) {\n const error = ex as Error\n console.log(`tryCall failed [${name}]: ${error.message}`)\n }\n return undefined\n }\n}\n","import { CryptoNftCollectionWitnessPlugin } from './Plugin'\n\nexport * from './lib'\nexport * from './Witness'\n\nexport { CryptoNftCollectionWitnessPlugin }\n\n// eslint-disable-next-line import/no-default-export\nexport default CryptoNftCollectionWitnessPlugin\n"],"mappings":"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","names":["NftSchema","ERC721Enumerable__factory","attributes","p","ERC721Enumerable__factory","NftSchema"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getNftCollectionNfts.d.ts","sourceRoot":"","sources":["../../../src/lib/getNftCollectionNfts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"getNftCollectionNfts.d.ts","sourceRoot":"","sources":["../../../src/lib/getNftCollectionNfts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIvD,OAAO,EAAE,OAAO,EAA0B,SAAS,EAAe,MAAM,wCAAwC,CAAA;AAahH,eAAO,MAAM,oBAAoB,oBAId,MAAM,YAIb,YAAY,UACd,SAAS,EAAE,uBAOlB,QAAQ,OAAO,EAAE,CA2DnB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getNftCollectionNfts.d.ts","sourceRoot":"","sources":["../../../src/lib/getNftCollectionNfts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"getNftCollectionNfts.d.ts","sourceRoot":"","sources":["../../../src/lib/getNftCollectionNfts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIvD,OAAO,EAAE,OAAO,EAA0B,SAAS,EAAe,MAAM,wCAAwC,CAAA;AAahH,eAAO,MAAM,oBAAoB,oBAId,MAAM,YAIb,YAAY,UACd,SAAS,EAAE,uBAOlB,QAAQ,OAAO,EAAE,CA2DnB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getNftCollectionNfts.d.ts","sourceRoot":"","sources":["../../../src/lib/getNftCollectionNfts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"getNftCollectionNfts.d.ts","sourceRoot":"","sources":["../../../src/lib/getNftCollectionNfts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIvD,OAAO,EAAE,OAAO,EAA0B,SAAS,EAAe,MAAM,wCAAwC,CAAA;AAahH,eAAO,MAAM,oBAAoB,oBAId,MAAM,YAIb,YAAY,UACd,SAAS,EAAE,uBAOlB,QAAQ,OAAO,EAAE,CA2DnB,CAAA"}
|
package/package.json
CHANGED
|
@@ -12,26 +12,25 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@ethersproject/abi": "^5.7.0",
|
|
14
14
|
"@ethersproject/providers": "^5.7.2",
|
|
15
|
-
"@xylabs/assert": "^2.13.
|
|
16
|
-
"@xylabs/
|
|
17
|
-
"@
|
|
18
|
-
"@
|
|
19
|
-
"@xyo-network/
|
|
20
|
-
"@xyo-network/
|
|
21
|
-
"@xyo-network/crypto-nft-payload-plugin": "~2.79.
|
|
22
|
-
"@xyo-network/
|
|
15
|
+
"@xylabs/assert": "^2.13.8",
|
|
16
|
+
"@xylabs/axios": "^2.13.8",
|
|
17
|
+
"@xylabs/eth-address": "^2.13.8",
|
|
18
|
+
"@xylabs/exists": "^2.13.8",
|
|
19
|
+
"@xyo-network/blockchain-erc1967-witness": "~2.79.5",
|
|
20
|
+
"@xyo-network/core": "~2.79.5",
|
|
21
|
+
"@xyo-network/crypto-nft-collection-payload-plugin": "~2.79.5",
|
|
22
|
+
"@xyo-network/crypto-nft-payload-plugin": "~2.79.5",
|
|
23
23
|
"@xyo-network/open-zeppelin-typechain": "^2.2.11",
|
|
24
|
-
"@xyo-network/payload-model": "~2.79.
|
|
25
|
-
"@xyo-network/payloadset-plugin": "~2.79.
|
|
26
|
-
"@xyo-network/witness-blockchain-abstract": "~2.79.
|
|
27
|
-
"@xyo-network/witness-model": "~2.79.3"
|
|
24
|
+
"@xyo-network/payload-model": "~2.79.5",
|
|
25
|
+
"@xyo-network/payloadset-plugin": "~2.79.5",
|
|
26
|
+
"@xyo-network/witness-blockchain-abstract": "~2.79.5"
|
|
28
27
|
},
|
|
29
28
|
"devDependencies": {
|
|
30
|
-
"@xylabs/jest-helpers": "^2.13.
|
|
29
|
+
"@xylabs/jest-helpers": "^2.13.8",
|
|
31
30
|
"@xylabs/ts-scripts-yarn3": "^3.1.21",
|
|
32
31
|
"@xylabs/tsconfig": "^3.1.21",
|
|
33
|
-
"@xyo-network/account": "~2.79.
|
|
34
|
-
"@xyo-network/account-model": "~2.79.
|
|
32
|
+
"@xyo-network/account": "~2.79.5",
|
|
33
|
+
"@xyo-network/account-model": "~2.79.5",
|
|
35
34
|
"ethers": "^5.7.2",
|
|
36
35
|
"jest": "^29.7.0",
|
|
37
36
|
"typescript": "^5.2.2"
|
|
@@ -76,5 +75,5 @@
|
|
|
76
75
|
"url": "https://github.com/XYOracleNetwork/sdk-xyo-client-js.git"
|
|
77
76
|
},
|
|
78
77
|
"sideEffects": false,
|
|
79
|
-
"version": "2.79.
|
|
78
|
+
"version": "2.79.5"
|
|
80
79
|
}
|
package/src/Witness.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { InfuraProvider, Provider, WebSocketProvider } from '@ethersproject/providers'
|
|
2
1
|
import { assertEx } from '@xylabs/assert'
|
|
3
2
|
import { EthAddress } from '@xylabs/eth-address'
|
|
4
|
-
import { AbstractWitness } from '@xyo-network/abstract-witness'
|
|
5
3
|
import { PayloadHasher } from '@xyo-network/core'
|
|
6
4
|
import {
|
|
7
5
|
isNftCollectionWitnessQuery,
|
|
@@ -11,19 +9,12 @@ import {
|
|
|
11
9
|
NftCollectionWitnessConfigSchema,
|
|
12
10
|
NftCollectionWitnessQuery,
|
|
13
11
|
} from '@xyo-network/crypto-nft-collection-payload-plugin'
|
|
14
|
-
import { AnyConfigSchema } from '@xyo-network/module-model'
|
|
15
12
|
import { ERC721Enumerable__factory } from '@xyo-network/open-zeppelin-typechain'
|
|
16
|
-
import {
|
|
17
|
-
import { WitnessParams } from '@xyo-network/witness-model'
|
|
13
|
+
import { AbstractBlockchainWitness, BlockchainWitnessParams } from '@xyo-network/witness-blockchain-abstract'
|
|
18
14
|
|
|
19
15
|
import { getNftCollectionMetrics, getNftCollectionNfts, tokenTypes } from './lib'
|
|
20
16
|
|
|
21
|
-
export type CryptoNftCollectionWitnessParams =
|
|
22
|
-
AnyConfigSchema<NftCollectionWitnessConfig>,
|
|
23
|
-
{
|
|
24
|
-
provider: Provider
|
|
25
|
-
}
|
|
26
|
-
>
|
|
17
|
+
export type CryptoNftCollectionWitnessParams = BlockchainWitnessParams<NftCollectionWitnessConfig>
|
|
27
18
|
|
|
28
19
|
const defaultMaxNfts = 100
|
|
29
20
|
|
|
@@ -43,27 +34,19 @@ function resolvedValue<T>(settled: PromiseSettledResult<T>, assert?: boolean) {
|
|
|
43
34
|
return settled.status === 'fulfilled' ? settled.value : undefined
|
|
44
35
|
}
|
|
45
36
|
|
|
46
|
-
export class CryptoNftCollectionWitness<
|
|
47
|
-
TParams,
|
|
48
|
-
|
|
49
|
-
NftCollectionInfo
|
|
50
|
-
> {
|
|
37
|
+
export class CryptoNftCollectionWitness<
|
|
38
|
+
TParams extends CryptoNftCollectionWitnessParams = CryptoNftCollectionWitnessParams,
|
|
39
|
+
> extends AbstractBlockchainWitness<TParams, NftCollectionWitnessQuery, NftCollectionInfo> {
|
|
51
40
|
static override configSchemas = [NftCollectionWitnessConfigSchema]
|
|
52
41
|
|
|
53
|
-
protected providers: Record<number, WebSocketProvider | InfuraProvider> = {}
|
|
54
|
-
|
|
55
|
-
protected getProvider(chainId: number) {
|
|
56
|
-
this.providers[chainId] = this.providers[chainId] ?? getProviderFromEnv(chainId)
|
|
57
|
-
return this.providers[chainId]
|
|
58
|
-
}
|
|
59
|
-
|
|
60
42
|
protected override async observeHandler(payloads?: NftCollectionWitnessQuery[]): Promise<NftCollectionInfo[]> {
|
|
61
43
|
await this.started('throw')
|
|
44
|
+
await this.getProviders() //make sure cache clears
|
|
62
45
|
const queries = payloads?.filter(isNftCollectionWitnessQuery) ?? []
|
|
63
46
|
const observations = await Promise.all(
|
|
64
47
|
queries.map<Promise<NftCollectionInfo>>(async (query) => {
|
|
65
48
|
const chainId = assertEx(query?.chainId || this.config.chainId, 'params.chainId is required')
|
|
66
|
-
const provider = this.getProvider(
|
|
49
|
+
const provider = await this.getProvider(true, true)
|
|
67
50
|
const address = assertEx(
|
|
68
51
|
EthAddress.parse(assertEx(query?.address || this.config.address, 'params.address is required')),
|
|
69
52
|
'Failed to parse params.address',
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { BaseProvider } from '@ethersproject/providers'
|
|
2
|
-
import { AxiosJson } from '@
|
|
2
|
+
import { AxiosJson } from '@xylabs/axios'
|
|
3
|
+
import { exists } from '@xylabs/exists'
|
|
4
|
+
import { getErc1967Status } from '@xyo-network/blockchain-erc1967-witness'
|
|
3
5
|
import { NftInfo, NftMetadata, NftSchema, TokenType, toTokenType } from '@xyo-network/crypto-nft-payload-plugin'
|
|
4
6
|
import { ERC721Enumerable__factory, ERC721URIStorage__factory, ERC1155Supply__factory } from '@xyo-network/open-zeppelin-typechain'
|
|
5
|
-
import {
|
|
6
|
-
checkIpfsUrl,
|
|
7
|
-
ERC1967_PROXY_IMPLEMENTATION_STORAGE_SLOT,
|
|
8
|
-
getErc1967Status,
|
|
9
|
-
readAddressFromSlot,
|
|
10
|
-
} from '@xyo-network/witness-blockchain-abstract'
|
|
7
|
+
import { checkIpfsUrl } from '@xyo-network/witness-blockchain-abstract'
|
|
11
8
|
|
|
12
9
|
import { tokenTypes } from './tokenTypes'
|
|
13
10
|
import { tryCall } from './tryCall'
|
|
14
11
|
|
|
15
12
|
const ipfsGateway = '5d7b6582.beta.decentralnetworkservices.com'
|
|
16
13
|
|
|
14
|
+
function range(size: number, startAt: number = 0): ReadonlyArray<number> {
|
|
15
|
+
return [...Array(size).keys()].map((i) => i + startAt)
|
|
16
|
+
}
|
|
17
|
+
|
|
17
18
|
export const getNftCollectionNfts = async (
|
|
18
19
|
/**
|
|
19
20
|
* The address of the NFT contract to search for
|
|
@@ -40,43 +41,48 @@ export const getNftCollectionNfts = async (
|
|
|
40
41
|
const storage = ERC721URIStorage__factory.connect(implementation, provider)
|
|
41
42
|
const supply1155 = ERC1155Supply__factory.connect(implementation, provider)
|
|
42
43
|
const finalTypes = types ?? (await tokenTypes(provider, implementation))
|
|
43
|
-
const result: NftInfo[] = []
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
const tokenId = await tryCall(async () => (await enumerable.tokenByIndex(i)).toHexString())
|
|
47
|
-
if (tokenId !== undefined) {
|
|
48
|
-
const supply = finalTypes.includes(toTokenType('ERC1155'))
|
|
49
|
-
? (await tryCall(async () => (await supply1155.totalSupply(tokenId)).toHexString())) ?? '0x01'
|
|
50
|
-
: '0x01'
|
|
51
|
-
const metadataUri = await tryCall(async () => await storage.tokenURI(tokenId))
|
|
52
|
-
const checkedMetaDataUri = metadataUri ? checkIpfsUrl(metadataUri, ipfsGateway) : undefined
|
|
53
|
-
let metadata: NftMetadata | undefined = undefined
|
|
54
|
-
if (checkedMetaDataUri !== undefined) {
|
|
55
|
-
try {
|
|
56
|
-
metadata = (await axios.get(checkedMetaDataUri)).data
|
|
57
|
-
} catch (ex) {
|
|
58
|
-
const error = ex as Error
|
|
59
|
-
console.error(`Get Metadata failed: ${error.message}`)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
45
|
+
const maxNftsArray = range(maxNfts)
|
|
62
46
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
47
|
+
const result: NftInfo[] = (
|
|
48
|
+
await Promise.all(
|
|
49
|
+
maxNftsArray.map(async (_value, i) => {
|
|
50
|
+
const tokenId = (await tryCall(async () => (await enumerable.tokenByIndex(i)).toHexString())) ?? `${i}`
|
|
51
|
+
if (tokenId !== undefined) {
|
|
52
|
+
const supply = finalTypes.includes(toTokenType('ERC1155'))
|
|
53
|
+
? (await tryCall(async () => (await supply1155.totalSupply(tokenId)).toHexString())) ?? '0x01'
|
|
54
|
+
: '0x01'
|
|
55
|
+
const metadataUri = await tryCall(async () => await storage.tokenURI(tokenId))
|
|
56
|
+
const checkedMetaDataUri = metadataUri ? checkIpfsUrl(metadataUri, ipfsGateway) : undefined
|
|
57
|
+
let metadata: NftMetadata | undefined = undefined
|
|
58
|
+
if (checkedMetaDataUri !== undefined) {
|
|
59
|
+
try {
|
|
60
|
+
metadata = (await axios.get(checkedMetaDataUri)).data
|
|
61
|
+
} catch (ex) {
|
|
62
|
+
const error = ex as Error
|
|
63
|
+
console.error(`Get Metadata failed: ${error.message}`)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const info: NftInfo = {
|
|
68
|
+
address: contractAddress,
|
|
69
|
+
chainId: provider.network.chainId,
|
|
70
|
+
metadata,
|
|
71
|
+
metadataUri,
|
|
72
|
+
schema: NftSchema,
|
|
73
|
+
supply,
|
|
74
|
+
tokenId,
|
|
75
|
+
type: finalTypes.at(0),
|
|
76
|
+
types: finalTypes,
|
|
77
|
+
}
|
|
78
|
+
if (implementation !== contractAddress) {
|
|
79
|
+
info.implementation = implementation
|
|
80
|
+
}
|
|
81
|
+
return info
|
|
82
|
+
}
|
|
83
|
+
}),
|
|
84
|
+
)
|
|
85
|
+
).filter(exists)
|
|
80
86
|
return result
|
|
81
87
|
} catch (ex) {
|
|
82
88
|
const error = ex as Error
|