@xyo-network/crypto-nft-collection-witness-plugin 2.78.3 → 2.78.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/index.cjs +97 -65
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.js +98 -66
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/lib/contractHasFunctions.d.cts +4 -0
- package/dist/browser/lib/contractHasFunctions.d.cts.map +1 -0
- package/dist/browser/lib/contractHasFunctions.d.mts +4 -0
- package/dist/browser/lib/contractHasFunctions.d.mts.map +1 -0
- package/dist/browser/lib/contractHasFunctions.d.ts +4 -0
- package/dist/browser/lib/contractHasFunctions.d.ts.map +1 -0
- 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/browser/lib/getProviderFromEnv.d.cts +3 -2
- package/dist/browser/lib/getProviderFromEnv.d.cts.map +1 -1
- package/dist/browser/lib/getProviderFromEnv.d.mts +3 -2
- package/dist/browser/lib/getProviderFromEnv.d.mts.map +1 -1
- package/dist/browser/lib/getProviderFromEnv.d.ts +3 -2
- package/dist/browser/lib/getProviderFromEnv.d.ts.map +1 -1
- package/dist/browser/lib/index.d.cts +1 -0
- package/dist/browser/lib/index.d.cts.map +1 -1
- package/dist/browser/lib/index.d.mts +1 -0
- package/dist/browser/lib/index.d.mts.map +1 -1
- package/dist/browser/lib/index.d.ts +1 -0
- package/dist/browser/lib/index.d.ts.map +1 -1
- package/dist/browser/lib/tokenTypes.d.cts +4 -6
- package/dist/browser/lib/tokenTypes.d.cts.map +1 -1
- package/dist/browser/lib/tokenTypes.d.mts +4 -6
- package/dist/browser/lib/tokenTypes.d.mts.map +1 -1
- package/dist/browser/lib/tokenTypes.d.ts +4 -6
- package/dist/browser/lib/tokenTypes.d.ts.map +1 -1
- package/dist/browser/lib/tryCall.d.cts +2 -0
- package/dist/browser/lib/tryCall.d.cts.map +1 -0
- package/dist/browser/lib/tryCall.d.mts +2 -0
- package/dist/browser/lib/tryCall.d.mts.map +1 -0
- package/dist/browser/lib/tryCall.d.ts +2 -0
- package/dist/browser/lib/tryCall.d.ts.map +1 -0
- package/dist/node/index.js +99 -66
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +98 -66
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/lib/contractHasFunctions.d.cts +4 -0
- package/dist/node/lib/contractHasFunctions.d.cts.map +1 -0
- package/dist/node/lib/contractHasFunctions.d.mts +4 -0
- package/dist/node/lib/contractHasFunctions.d.mts.map +1 -0
- package/dist/node/lib/contractHasFunctions.d.ts +4 -0
- package/dist/node/lib/contractHasFunctions.d.ts.map +1 -0
- 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/dist/node/lib/getProviderFromEnv.d.cts +3 -2
- package/dist/node/lib/getProviderFromEnv.d.cts.map +1 -1
- package/dist/node/lib/getProviderFromEnv.d.mts +3 -2
- package/dist/node/lib/getProviderFromEnv.d.mts.map +1 -1
- package/dist/node/lib/getProviderFromEnv.d.ts +3 -2
- package/dist/node/lib/getProviderFromEnv.d.ts.map +1 -1
- package/dist/node/lib/index.d.cts +1 -0
- package/dist/node/lib/index.d.cts.map +1 -1
- package/dist/node/lib/index.d.mts +1 -0
- package/dist/node/lib/index.d.mts.map +1 -1
- package/dist/node/lib/index.d.ts +1 -0
- package/dist/node/lib/index.d.ts.map +1 -1
- package/dist/node/lib/tokenTypes.d.cts +4 -6
- package/dist/node/lib/tokenTypes.d.cts.map +1 -1
- package/dist/node/lib/tokenTypes.d.mts +4 -6
- package/dist/node/lib/tokenTypes.d.mts.map +1 -1
- package/dist/node/lib/tokenTypes.d.ts +4 -6
- package/dist/node/lib/tokenTypes.d.ts.map +1 -1
- package/dist/node/lib/tryCall.d.cts +2 -0
- package/dist/node/lib/tryCall.d.cts.map +1 -0
- package/dist/node/lib/tryCall.d.mts +2 -0
- package/dist/node/lib/tryCall.d.mts.map +1 -0
- package/dist/node/lib/tryCall.d.ts +2 -0
- package/dist/node/lib/tryCall.d.ts.map +1 -0
- package/package.json +14 -14
- package/src/Witness.ts +1 -1
- package/src/lib/contractHasFunctions.ts +20 -0
- package/src/lib/getNftCollectionNfts.ts +44 -35
- package/src/lib/getProviderFromEnv.ts +19 -7
- package/src/lib/index.ts +1 -0
- package/src/lib/tokenTypes.ts +8 -19
- package/src/lib/tryCall.ts +11 -0
package/dist/browser/index.cjs
CHANGED
|
@@ -23,11 +23,12 @@ __export(src_exports, {
|
|
|
23
23
|
CryptoNftCollectionWitness: () => CryptoNftCollectionWitness,
|
|
24
24
|
CryptoNftCollectionWitnessPlugin: () => CryptoNftCollectionWitnessPlugin,
|
|
25
25
|
checkIpfsUrl: () => checkIpfsUrl,
|
|
26
|
+
contractHasFunctions: () => contractHasFunctions,
|
|
26
27
|
default: () => src_default,
|
|
27
28
|
getNftCollectionMetrics: () => getNftCollectionMetrics,
|
|
28
29
|
getNftCollectionNfts: () => getNftCollectionNfts,
|
|
29
30
|
getProviderFromEnv: () => getProviderFromEnv,
|
|
30
|
-
|
|
31
|
+
getProviderFromEnvInternal: () => getProviderFromEnvInternal,
|
|
31
32
|
isErc1155: () => isErc1155,
|
|
32
33
|
isErc721: () => isErc721,
|
|
33
34
|
tokenTypes: () => tokenTypes
|
|
@@ -104,38 +105,40 @@ var getNftCollectionMetrics = (nfts) => {
|
|
|
104
105
|
return { metadata: { attributes } };
|
|
105
106
|
};
|
|
106
107
|
|
|
108
|
+
// src/lib/contractHasFunctions.ts
|
|
109
|
+
var contractHasFunctions = async (provider, address, contractInterface, functionNames) => {
|
|
110
|
+
try {
|
|
111
|
+
const bytecode = await provider.getCode(address, "latest");
|
|
112
|
+
for (let i = 0; i < functionNames.length; i++) {
|
|
113
|
+
const nameSig = contractInterface.getSighash(functionNames[i]).substring(2);
|
|
114
|
+
if (!bytecode.includes(nameSig)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
} catch (ex) {
|
|
121
|
+
const error = ex;
|
|
122
|
+
console.log(error);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
107
127
|
// src/lib/getNftCollectionNfts.ts
|
|
108
128
|
var import_axios = require("@xyo-network/axios");
|
|
109
129
|
var import_crypto_nft_payload_plugin = require("@xyo-network/crypto-nft-payload-plugin");
|
|
110
130
|
var import_open_zeppelin_typechain2 = require("@xyo-network/open-zeppelin-typechain");
|
|
111
131
|
|
|
112
|
-
// src/lib/nonEvaluableContractAddresses.ts
|
|
113
|
-
var nonEvaluableContractAddresses = [
|
|
114
|
-
// ENS
|
|
115
|
-
"0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72"
|
|
116
|
-
].map((address) => address.toUpperCase());
|
|
117
|
-
|
|
118
132
|
// src/lib/tokenTypes.ts
|
|
119
133
|
var import_open_zeppelin_typechain = require("@xyo-network/open-zeppelin-typechain");
|
|
120
|
-
var isErc1155 = async (
|
|
121
|
-
return await
|
|
134
|
+
var isErc1155 = async (provider, address) => {
|
|
135
|
+
return await contractHasFunctions(provider, address, import_open_zeppelin_typechain.ERC1155URIStorage__factory.createInterface(), ["uri"]);
|
|
122
136
|
};
|
|
123
|
-
var isErc721 = async (
|
|
124
|
-
return await
|
|
137
|
+
var isErc721 = async (provider, address) => {
|
|
138
|
+
return await contractHasFunctions(provider, address, import_open_zeppelin_typechain.IERC721Metadata__factory.createInterface(), ["name", "symbol", "tokenURI"]);
|
|
125
139
|
};
|
|
126
|
-
var
|
|
127
|
-
const
|
|
128
|
-
for (let i = 0; i < functionNames.length; i++) {
|
|
129
|
-
const nameSig = contractInterface.getSighash(functionNames[i]).substring(2);
|
|
130
|
-
if (!bytecode.includes(nameSig)) {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
return false;
|
|
136
|
-
};
|
|
137
|
-
var tokenTypes = async (contract) => {
|
|
138
|
-
const [erc721, erc1155] = await Promise.all([isErc721(contract), isErc1155(contract)]);
|
|
140
|
+
var tokenTypes = async (provider, address) => {
|
|
141
|
+
const [erc721, erc1155] = await Promise.all([isErc721(provider, address), isErc1155(provider, address)]);
|
|
139
142
|
const result = [];
|
|
140
143
|
if (erc721) {
|
|
141
144
|
result.push("ERC721");
|
|
@@ -146,6 +149,19 @@ var tokenTypes = async (contract) => {
|
|
|
146
149
|
return result;
|
|
147
150
|
};
|
|
148
151
|
|
|
152
|
+
// src/lib/tryCall.ts
|
|
153
|
+
var tryCall = async (func, name) => {
|
|
154
|
+
try {
|
|
155
|
+
return await func();
|
|
156
|
+
} catch (ex) {
|
|
157
|
+
if (name) {
|
|
158
|
+
const error = ex;
|
|
159
|
+
console.log(`tryCall failed [${name}]: ${error.message}`);
|
|
160
|
+
}
|
|
161
|
+
return void 0;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
149
165
|
// src/lib/getNftCollectionNfts.ts
|
|
150
166
|
var ipfsGateway = "5d7b6582.beta.decentralnetworkservices.com";
|
|
151
167
|
var checkIpfsUrl = (urlToCheck, ipfsGateway2) => {
|
|
@@ -165,58 +181,74 @@ var checkIpfsUrl = (urlToCheck, ipfsGateway2) => {
|
|
|
165
181
|
}
|
|
166
182
|
};
|
|
167
183
|
var getNftCollectionNfts = async (contractAddress, provider, types, maxNfts = 100) => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
184
|
+
try {
|
|
185
|
+
const axios = new import_axios.AxiosJson({ timeout: 2e3 });
|
|
186
|
+
const enumerable = import_open_zeppelin_typechain2.ERC721Enumerable__factory.connect(contractAddress, provider);
|
|
187
|
+
const storage = import_open_zeppelin_typechain2.ERC721URIStorage__factory.connect(contractAddress, provider);
|
|
188
|
+
const supply1155 = import_open_zeppelin_typechain2.ERC1155Supply__factory.connect(contractAddress, provider);
|
|
189
|
+
const finalTypes = types ?? await tokenTypes(provider, contractAddress);
|
|
190
|
+
const result = [];
|
|
191
|
+
for (let i = 0; i < maxNfts; i++) {
|
|
192
|
+
const tokenId = await tryCall(async () => (await enumerable.tokenByIndex(i)).toHexString());
|
|
193
|
+
if (tokenId !== void 0) {
|
|
194
|
+
const supply = finalTypes.includes((0, import_crypto_nft_payload_plugin.toTokenType)("ERC1155")) ? await tryCall(async () => (await supply1155.totalSupply(tokenId)).toHexString()) ?? "0x01" : "0x01";
|
|
195
|
+
const metadataUri = await tryCall(async () => await storage.tokenURI(tokenId));
|
|
196
|
+
const checkedMetaDataUri = metadataUri ? checkIpfsUrl(metadataUri, ipfsGateway) : void 0;
|
|
197
|
+
let metadata = void 0;
|
|
198
|
+
if (checkedMetaDataUri !== void 0) {
|
|
199
|
+
try {
|
|
200
|
+
metadata = (await axios.get(checkedMetaDataUri)).data;
|
|
201
|
+
} catch (ex) {
|
|
202
|
+
const error = ex;
|
|
203
|
+
console.error(`Get Metadata failed: ${error.message}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const info = {
|
|
207
|
+
address: contractAddress,
|
|
208
|
+
chainId: provider.network.chainId,
|
|
209
|
+
metadata,
|
|
210
|
+
metadataUri,
|
|
211
|
+
schema: import_crypto_nft_payload_plugin.NftSchema,
|
|
212
|
+
supply,
|
|
213
|
+
tokenId,
|
|
214
|
+
type: finalTypes.at(0),
|
|
215
|
+
types: finalTypes
|
|
216
|
+
};
|
|
217
|
+
result.push(info);
|
|
218
|
+
}
|
|
189
219
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
supply,
|
|
197
|
-
tokenId,
|
|
198
|
-
type: finalTypes.at(0),
|
|
199
|
-
types
|
|
200
|
-
};
|
|
201
|
-
result.push(info);
|
|
220
|
+
return result;
|
|
221
|
+
} catch (ex) {
|
|
222
|
+
const error = ex;
|
|
223
|
+
console.error(`getNftCollectionNfts failed: [${error.name}] ${error.message}`);
|
|
224
|
+
console.log(error.stack);
|
|
225
|
+
return [];
|
|
202
226
|
}
|
|
203
|
-
return result;
|
|
204
227
|
};
|
|
205
228
|
|
|
206
229
|
// src/lib/getProviderFromEnv.ts
|
|
207
230
|
var import_providers = require("@ethersproject/providers");
|
|
208
|
-
var
|
|
231
|
+
var getProviderFromEnvInternal = (chainId = 1) => {
|
|
232
|
+
console.log(`getProviderFromEnvInternal: ${chainId}`);
|
|
209
233
|
const infuraWssUri = process.env.INFURA_WSS_URI;
|
|
210
|
-
const infuraProvider = new import_providers.InfuraProvider(
|
|
234
|
+
const infuraProvider = new import_providers.InfuraProvider(chainId, {
|
|
211
235
|
projectId: process.env.INFURA_PROJECT_ID,
|
|
212
236
|
projectSecret: process.env.INFURA_PROJECT_SECRET
|
|
213
237
|
});
|
|
214
|
-
const
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
const
|
|
238
|
+
const infuraGenericWebsocketProvider = infuraWssUri ? new import_providers.WebSocketProvider(infuraWssUri, chainId) : void 0;
|
|
239
|
+
const quickNodeWSSUri = process.env.QUICKNODE_WSS_URI;
|
|
240
|
+
const quickNodeWebSocketProvider = quickNodeWSSUri ? new import_providers.WebSocketProvider(quickNodeWSSUri, chainId) : void 0;
|
|
241
|
+
const quickNodeHttpsUri = process.env.QUICKNODE_WSS_URI;
|
|
242
|
+
const quickRpcProvider = quickNodeHttpsUri ? new import_providers.JsonRpcProvider(quickNodeHttpsUri, chainId) : void 0;
|
|
243
|
+
const provider = infuraProvider ?? quickNodeWebSocketProvider ?? infuraProvider ?? infuraGenericWebsocketProvider ?? quickRpcProvider;
|
|
218
244
|
return provider;
|
|
219
245
|
};
|
|
246
|
+
var providers = {};
|
|
247
|
+
var getProviderFromEnv = (chainId = 1) => {
|
|
248
|
+
console.log(`getProviderFromEnv: ${chainId}`);
|
|
249
|
+
providers[chainId] = providers[chainId] ?? getProviderFromEnvInternal(chainId);
|
|
250
|
+
return providers[chainId];
|
|
251
|
+
};
|
|
220
252
|
|
|
221
253
|
// src/Witness.ts
|
|
222
254
|
var defaultMaxNfts = 100;
|
|
@@ -251,7 +283,7 @@ var CryptoNftCollectionWitness = class extends import_abstract_witness.AbstractW
|
|
|
251
283
|
await erc721Enumerable.name(),
|
|
252
284
|
await erc721Enumerable.symbol(),
|
|
253
285
|
(await erc721Enumerable.totalSupply()).toNumber(),
|
|
254
|
-
await tokenTypes(
|
|
286
|
+
await tokenTypes(provider, address),
|
|
255
287
|
await this.writeArchivist()
|
|
256
288
|
]);
|
|
257
289
|
const types = resolvedValue(typesSettled, true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../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/getNftCollectionNfts.ts","../../src/lib/nonEvaluableContractAddresses.ts","../../src/lib/tokenTypes.ts","../../src/lib/getProviderFromEnv.ts"],"sourcesContent":["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","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 { WitnessParams } from '@xyo-network/witness-model'\n\nimport { getNftCollectionMetrics, getNftCollectionNfts, getProviderFromEnv, 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(erc721Enumerable),\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 { JsonRpcProvider } 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'\n\nimport { nonEvaluableContractAddresses } from './nonEvaluableContractAddresses'\nimport { tokenTypes } from './tokenTypes'\n\nconst ipfsGateway = '5d7b6582.beta.decentralnetworkservices.com'\n\n/**\n * Returns the equivalent IPFS gateway URL for the supplied URL.\n * @param urlToCheck The URL to check\n * @returns If the supplied URL is an IPFS URL, it converts the URL to the\n * equivalent IPFS gateway URL. Otherwise, returns the original URL.\n */\nexport const checkIpfsUrl = (urlToCheck: string, ipfsGateway: string) => {\n const url = new URL(urlToCheck)\n let protocol = url.protocol\n let host = url.host\n let path = url.pathname\n const query = url.search\n if (protocol === 'ipfs:') {\n protocol = 'https:'\n host = ipfsGateway\n path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`\n const root = `${protocol}//${host}/${path}`\n return query?.length > 0 ? `${root}?${query}` : root\n } else {\n return urlToCheck\n }\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: JsonRpcProvider,\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 if (nonEvaluableContractAddresses.includes(contractAddress.toUpperCase())) {\n throw new Error(`Unable to evaluate collection with contractAddress: ${contractAddress}`)\n }\n const axios = new AxiosJson({ timeout: 2000 })\n const enumerable = ERC721Enumerable__factory.connect(contractAddress, provider)\n const storage = ERC721URIStorage__factory.connect(contractAddress, provider)\n const supply1155 = ERC1155Supply__factory.connect(contractAddress, provider)\n const finalTypes = types ?? (await tokenTypes(enumerable))\n const result: NftInfo[] = []\n\n for (let i = 0; i < maxNfts; i++) {\n console.log(`Getting Token [${i}]`)\n const tokenId = (await enumerable.tokenByIndex(i)).toHexString()\n const supply = finalTypes.includes(toTokenType('ERC1155')) ? (await supply1155.totalSupply(tokenId)).toHexString() : '0x01'\n const metadataUri = await storage.tokenURI(tokenId)\n const checkedMetaDataUri = checkIpfsUrl(metadataUri, ipfsGateway)\n let metadata: NftMetadata | undefined = undefined\n try {\n metadata = (await axios.get(checkedMetaDataUri)).data\n } catch (ex) {\n const error = ex as Error\n console.error(error.message)\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,\n }\n result.push(info)\n }\n return result\n}\n","/**\n * These contracts are not evaluable for some\n * reason (too large, nonsensical, etc.)\n */\nexport const nonEvaluableContractAddresses = [\n // ENS\n '0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72',\n].map((address) => address.toUpperCase())\n","import { Interface } from '@ethersproject/abi'\nimport { Contract } from '@ethersproject/contracts'\nimport { TokenType } from '@xyo-network/crypto-nft-payload-plugin'\nimport { ERC1155URIStorage__factory, IERC721Metadata__factory } from '@xyo-network/open-zeppelin-typechain'\n\nexport const isErc1155 = async (contract: Contract) => {\n return await hasFunctions(contract, ERC1155URIStorage__factory.createInterface(), ['uri'])\n}\n\nexport const isErc721 = async (contract: Contract) => {\n return await hasFunctions(contract, IERC721Metadata__factory.createInterface(), ['name', 'symbol', 'tokenURI'])\n}\n\nexport const hasFunctions = async (contract: Contract, contractInterface: Interface, functionNames: string[]) => {\n const bytecode = await contract.provider.getCode(contract.address)\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}\n\nexport const tokenTypes = async (contract: Contract) => {\n const [erc721, erc1155] = await Promise.all([isErc721(contract), isErc1155(contract)])\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","import { InfuraProvider, WebSocketProvider } from '@ethersproject/providers'\n\nexport const getProviderFromEnv = (chainId: string | number = 'homestead') => {\n const infuraWssUri = process.env.INFURA_WSS_URI\n const infuraProvider = new InfuraProvider('homestead', {\n projectId: process.env.INFURA_PROJECT_ID,\n projectSecret: process.env.INFURA_PROJECT_SECRET,\n })\n\n const infuraWebsocketProvider = infuraWssUri ? new WebSocketProvider(infuraWssUri, chainId) : undefined\n\n const quickNodeUri = process.env.QUICKNODE_WSS_URI\n const quickNodeProvider = quickNodeUri ? new WebSocketProvider(quickNodeUri, chainId) : undefined\n\n const provider = infuraWebsocketProvider ?? infuraProvider ?? infuraWebsocketProvider ?? quickNodeProvider ?? infuraProvider\n return provider\n}\n"],"mappings":"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","names":["import_crypto_nft_payload_plugin","import_open_zeppelin_typechain","attributes","p","import_open_zeppelin_typechain","ipfsGateway"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../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/lib/getProviderFromEnv.ts"],"sourcesContent":["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","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 { WitnessParams } from '@xyo-network/witness-model'\n\nimport { getNftCollectionMetrics, getNftCollectionNfts, getProviderFromEnv, 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 { JsonRpcProvider } 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'\n\nimport { tokenTypes } from './tokenTypes'\nimport { tryCall } from './tryCall'\n\nconst ipfsGateway = '5d7b6582.beta.decentralnetworkservices.com'\n\n/**\n * Returns the equivalent IPFS gateway URL for the supplied URL.\n * @param urlToCheck The URL to check\n * @returns If the supplied URL is an IPFS URL, it converts the URL to the\n * equivalent IPFS gateway URL. Otherwise, returns the original URL.\n */\nexport const checkIpfsUrl = (urlToCheck: string, ipfsGateway: string) => {\n const url = new URL(urlToCheck)\n let protocol = url.protocol\n let host = url.host\n let path = url.pathname\n const query = url.search\n if (protocol === 'ipfs:') {\n protocol = 'https:'\n host = ipfsGateway\n path = url.host === 'ipfs' ? `ipfs${path}` : `ipfs/${url.host}${path}`\n const root = `${protocol}//${host}/${path}`\n return query?.length > 0 ? `${root}?${query}` : root\n } else {\n return urlToCheck\n }\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: JsonRpcProvider,\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 const axios = new AxiosJson({ timeout: 2000 })\n const enumerable = ERC721Enumerable__factory.connect(contractAddress, provider)\n const storage = ERC721URIStorage__factory.connect(contractAddress, provider)\n const supply1155 = ERC1155Supply__factory.connect(contractAddress, provider)\n const finalTypes = types ?? (await tokenTypes(provider, contractAddress))\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 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 { InfuraProvider, JsonRpcProvider, WebSocketProvider } from '@ethersproject/providers'\n\nexport const getProviderFromEnvInternal = (chainId: number = 0x01) => {\n console.log(`getProviderFromEnvInternal: ${chainId}`)\n const infuraWssUri = process.env.INFURA_WSS_URI\n const infuraProvider = new InfuraProvider(chainId, {\n projectId: process.env.INFURA_PROJECT_ID,\n projectSecret: process.env.INFURA_PROJECT_SECRET,\n })\n\n const infuraGenericWebsocketProvider = infuraWssUri ? new WebSocketProvider(infuraWssUri, chainId) : undefined\n\n const quickNodeWSSUri = process.env.QUICKNODE_WSS_URI\n const quickNodeWebSocketProvider = quickNodeWSSUri ? new WebSocketProvider(quickNodeWSSUri, chainId) : undefined\n\n const quickNodeHttpsUri = process.env.QUICKNODE_WSS_URI\n const quickRpcProvider = quickNodeHttpsUri ? new JsonRpcProvider(quickNodeHttpsUri, chainId) : undefined\n\n const provider = infuraProvider ?? quickNodeWebSocketProvider ?? infuraProvider ?? infuraGenericWebsocketProvider ?? quickRpcProvider\n return provider\n}\n\nconst providers: Record<number, JsonRpcProvider | WebSocketProvider> = {}\n\nexport const getProviderFromEnv = (chainId: number = 0x01) => {\n console.log(`getProviderFromEnv: ${chainId}`)\n providers[chainId] = providers[chainId] ?? getProviderFromEnvInternal(chainId)\n return providers[chainId]\n}\n"],"mappings":"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","names":["import_crypto_nft_payload_plugin","import_open_zeppelin_typechain","attributes","p","import_open_zeppelin_typechain","ipfsGateway"]}
|
package/dist/browser/index.js
CHANGED
|
@@ -72,38 +72,40 @@ var getNftCollectionMetrics = (nfts) => {
|
|
|
72
72
|
return { metadata: { attributes } };
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
+
// src/lib/contractHasFunctions.ts
|
|
76
|
+
var contractHasFunctions = async (provider, address, contractInterface, functionNames) => {
|
|
77
|
+
try {
|
|
78
|
+
const bytecode = await provider.getCode(address, "latest");
|
|
79
|
+
for (let i = 0; i < functionNames.length; i++) {
|
|
80
|
+
const nameSig = contractInterface.getSighash(functionNames[i]).substring(2);
|
|
81
|
+
if (!bytecode.includes(nameSig)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
} catch (ex) {
|
|
88
|
+
const error = ex;
|
|
89
|
+
console.log(error);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
75
94
|
// src/lib/getNftCollectionNfts.ts
|
|
76
95
|
import { AxiosJson } from "@xyo-network/axios";
|
|
77
96
|
import { NftSchema, toTokenType } from "@xyo-network/crypto-nft-payload-plugin";
|
|
78
97
|
import { ERC721Enumerable__factory, ERC721URIStorage__factory, ERC1155Supply__factory } from "@xyo-network/open-zeppelin-typechain";
|
|
79
98
|
|
|
80
|
-
// src/lib/nonEvaluableContractAddresses.ts
|
|
81
|
-
var nonEvaluableContractAddresses = [
|
|
82
|
-
// ENS
|
|
83
|
-
"0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72"
|
|
84
|
-
].map((address) => address.toUpperCase());
|
|
85
|
-
|
|
86
99
|
// src/lib/tokenTypes.ts
|
|
87
100
|
import { ERC1155URIStorage__factory, IERC721Metadata__factory } from "@xyo-network/open-zeppelin-typechain";
|
|
88
|
-
var isErc1155 = async (
|
|
89
|
-
return await
|
|
101
|
+
var isErc1155 = async (provider, address) => {
|
|
102
|
+
return await contractHasFunctions(provider, address, ERC1155URIStorage__factory.createInterface(), ["uri"]);
|
|
90
103
|
};
|
|
91
|
-
var isErc721 = async (
|
|
92
|
-
return await
|
|
104
|
+
var isErc721 = async (provider, address) => {
|
|
105
|
+
return await contractHasFunctions(provider, address, IERC721Metadata__factory.createInterface(), ["name", "symbol", "tokenURI"]);
|
|
93
106
|
};
|
|
94
|
-
var
|
|
95
|
-
const
|
|
96
|
-
for (let i = 0; i < functionNames.length; i++) {
|
|
97
|
-
const nameSig = contractInterface.getSighash(functionNames[i]).substring(2);
|
|
98
|
-
if (!bytecode.includes(nameSig)) {
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
return false;
|
|
104
|
-
};
|
|
105
|
-
var tokenTypes = async (contract) => {
|
|
106
|
-
const [erc721, erc1155] = await Promise.all([isErc721(contract), isErc1155(contract)]);
|
|
107
|
+
var tokenTypes = async (provider, address) => {
|
|
108
|
+
const [erc721, erc1155] = await Promise.all([isErc721(provider, address), isErc1155(provider, address)]);
|
|
107
109
|
const result = [];
|
|
108
110
|
if (erc721) {
|
|
109
111
|
result.push("ERC721");
|
|
@@ -114,6 +116,19 @@ var tokenTypes = async (contract) => {
|
|
|
114
116
|
return result;
|
|
115
117
|
};
|
|
116
118
|
|
|
119
|
+
// src/lib/tryCall.ts
|
|
120
|
+
var tryCall = async (func, name) => {
|
|
121
|
+
try {
|
|
122
|
+
return await func();
|
|
123
|
+
} catch (ex) {
|
|
124
|
+
if (name) {
|
|
125
|
+
const error = ex;
|
|
126
|
+
console.log(`tryCall failed [${name}]: ${error.message}`);
|
|
127
|
+
}
|
|
128
|
+
return void 0;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
117
132
|
// src/lib/getNftCollectionNfts.ts
|
|
118
133
|
var ipfsGateway = "5d7b6582.beta.decentralnetworkservices.com";
|
|
119
134
|
var checkIpfsUrl = (urlToCheck, ipfsGateway2) => {
|
|
@@ -133,58 +148,74 @@ var checkIpfsUrl = (urlToCheck, ipfsGateway2) => {
|
|
|
133
148
|
}
|
|
134
149
|
};
|
|
135
150
|
var getNftCollectionNfts = async (contractAddress, provider, types, maxNfts = 100) => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
try {
|
|
152
|
+
const axios = new AxiosJson({ timeout: 2e3 });
|
|
153
|
+
const enumerable = ERC721Enumerable__factory.connect(contractAddress, provider);
|
|
154
|
+
const storage = ERC721URIStorage__factory.connect(contractAddress, provider);
|
|
155
|
+
const supply1155 = ERC1155Supply__factory.connect(contractAddress, provider);
|
|
156
|
+
const finalTypes = types ?? await tokenTypes(provider, contractAddress);
|
|
157
|
+
const result = [];
|
|
158
|
+
for (let i = 0; i < maxNfts; i++) {
|
|
159
|
+
const tokenId = await tryCall(async () => (await enumerable.tokenByIndex(i)).toHexString());
|
|
160
|
+
if (tokenId !== void 0) {
|
|
161
|
+
const supply = finalTypes.includes(toTokenType("ERC1155")) ? await tryCall(async () => (await supply1155.totalSupply(tokenId)).toHexString()) ?? "0x01" : "0x01";
|
|
162
|
+
const metadataUri = await tryCall(async () => await storage.tokenURI(tokenId));
|
|
163
|
+
const checkedMetaDataUri = metadataUri ? checkIpfsUrl(metadataUri, ipfsGateway) : void 0;
|
|
164
|
+
let metadata = void 0;
|
|
165
|
+
if (checkedMetaDataUri !== void 0) {
|
|
166
|
+
try {
|
|
167
|
+
metadata = (await axios.get(checkedMetaDataUri)).data;
|
|
168
|
+
} catch (ex) {
|
|
169
|
+
const error = ex;
|
|
170
|
+
console.error(`Get Metadata failed: ${error.message}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const info = {
|
|
174
|
+
address: contractAddress,
|
|
175
|
+
chainId: provider.network.chainId,
|
|
176
|
+
metadata,
|
|
177
|
+
metadataUri,
|
|
178
|
+
schema: NftSchema,
|
|
179
|
+
supply,
|
|
180
|
+
tokenId,
|
|
181
|
+
type: finalTypes.at(0),
|
|
182
|
+
types: finalTypes
|
|
183
|
+
};
|
|
184
|
+
result.push(info);
|
|
185
|
+
}
|
|
157
186
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
supply,
|
|
165
|
-
tokenId,
|
|
166
|
-
type: finalTypes.at(0),
|
|
167
|
-
types
|
|
168
|
-
};
|
|
169
|
-
result.push(info);
|
|
187
|
+
return result;
|
|
188
|
+
} catch (ex) {
|
|
189
|
+
const error = ex;
|
|
190
|
+
console.error(`getNftCollectionNfts failed: [${error.name}] ${error.message}`);
|
|
191
|
+
console.log(error.stack);
|
|
192
|
+
return [];
|
|
170
193
|
}
|
|
171
|
-
return result;
|
|
172
194
|
};
|
|
173
195
|
|
|
174
196
|
// src/lib/getProviderFromEnv.ts
|
|
175
|
-
import { InfuraProvider, WebSocketProvider } from "@ethersproject/providers";
|
|
176
|
-
var
|
|
197
|
+
import { InfuraProvider, JsonRpcProvider, WebSocketProvider } from "@ethersproject/providers";
|
|
198
|
+
var getProviderFromEnvInternal = (chainId = 1) => {
|
|
199
|
+
console.log(`getProviderFromEnvInternal: ${chainId}`);
|
|
177
200
|
const infuraWssUri = process.env.INFURA_WSS_URI;
|
|
178
|
-
const infuraProvider = new InfuraProvider(
|
|
201
|
+
const infuraProvider = new InfuraProvider(chainId, {
|
|
179
202
|
projectId: process.env.INFURA_PROJECT_ID,
|
|
180
203
|
projectSecret: process.env.INFURA_PROJECT_SECRET
|
|
181
204
|
});
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
const
|
|
205
|
+
const infuraGenericWebsocketProvider = infuraWssUri ? new WebSocketProvider(infuraWssUri, chainId) : void 0;
|
|
206
|
+
const quickNodeWSSUri = process.env.QUICKNODE_WSS_URI;
|
|
207
|
+
const quickNodeWebSocketProvider = quickNodeWSSUri ? new WebSocketProvider(quickNodeWSSUri, chainId) : void 0;
|
|
208
|
+
const quickNodeHttpsUri = process.env.QUICKNODE_WSS_URI;
|
|
209
|
+
const quickRpcProvider = quickNodeHttpsUri ? new JsonRpcProvider(quickNodeHttpsUri, chainId) : void 0;
|
|
210
|
+
const provider = infuraProvider ?? quickNodeWebSocketProvider ?? infuraProvider ?? infuraGenericWebsocketProvider ?? quickRpcProvider;
|
|
186
211
|
return provider;
|
|
187
212
|
};
|
|
213
|
+
var providers = {};
|
|
214
|
+
var getProviderFromEnv = (chainId = 1) => {
|
|
215
|
+
console.log(`getProviderFromEnv: ${chainId}`);
|
|
216
|
+
providers[chainId] = providers[chainId] ?? getProviderFromEnvInternal(chainId);
|
|
217
|
+
return providers[chainId];
|
|
218
|
+
};
|
|
188
219
|
|
|
189
220
|
// src/Witness.ts
|
|
190
221
|
var defaultMaxNfts = 100;
|
|
@@ -219,7 +250,7 @@ var CryptoNftCollectionWitness = class extends AbstractWitness {
|
|
|
219
250
|
await erc721Enumerable.name(),
|
|
220
251
|
await erc721Enumerable.symbol(),
|
|
221
252
|
(await erc721Enumerable.totalSupply()).toNumber(),
|
|
222
|
-
await tokenTypes(
|
|
253
|
+
await tokenTypes(provider, address),
|
|
223
254
|
await this.writeArchivist()
|
|
224
255
|
]);
|
|
225
256
|
const types = resolvedValue(typesSettled, true);
|
|
@@ -268,11 +299,12 @@ export {
|
|
|
268
299
|
CryptoNftCollectionWitness,
|
|
269
300
|
CryptoNftCollectionWitnessPlugin,
|
|
270
301
|
checkIpfsUrl,
|
|
302
|
+
contractHasFunctions,
|
|
271
303
|
src_default as default,
|
|
272
304
|
getNftCollectionMetrics,
|
|
273
305
|
getNftCollectionNfts,
|
|
274
306
|
getProviderFromEnv,
|
|
275
|
-
|
|
307
|
+
getProviderFromEnvInternal,
|
|
276
308
|
isErc1155,
|
|
277
309
|
isErc721,
|
|
278
310
|
tokenTypes
|