@xyo-network/crypto-nft-collection-diviner-score-plugin 2.86.5 → 2.87.0-rc.1
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/Diviner.d.cts.map +1 -1
- package/dist/browser/Diviner.d.mts.map +1 -1
- package/dist/browser/Diviner.d.ts.map +1 -1
- package/dist/browser/Plugin.d.cts +3 -1
- package/dist/browser/Plugin.d.cts.map +1 -1
- package/dist/browser/Plugin.d.mts +3 -1
- package/dist/browser/Plugin.d.mts.map +1 -1
- package/dist/browser/Plugin.d.ts +3 -1
- package/dist/browser/Plugin.d.ts.map +1 -1
- package/dist/browser/index.cjs +2 -2
- package/dist/browser/index.cjs.map +1 -1
- package/dist/browser/index.js +2 -2
- package/dist/browser/index.js.map +1 -1
- package/dist/node/Diviner.d.cts.map +1 -1
- package/dist/node/Diviner.d.mts.map +1 -1
- package/dist/node/Diviner.d.ts.map +1 -1
- package/dist/node/Plugin.d.cts +3 -1
- package/dist/node/Plugin.d.cts.map +1 -1
- package/dist/node/Plugin.d.mts +3 -1
- package/dist/node/Plugin.d.mts.map +1 -1
- package/dist/node/Plugin.d.ts +3 -1
- package/dist/node/Plugin.d.ts.map +1 -1
- package/dist/node/index.cjs +2 -2
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.js +2 -2
- package/dist/node/index.js.map +1 -1
- package/package.json +15 -14
- package/src/Diviner.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,MAAM,MAAM,+BAA+B,GAAG,aAAa,CAAC,eAAe,CAAC,+BAA+B,CAAC,CAAC,CAAA;AAQ7G,qBAAa,yBAAyB,CACpC,OAAO,SAAS,+BAA+B,GAAG,+BAA+B,CACjF,SAAQ,eAAe,CAAC,OAAO,CAAC;IAChC,OAAgB,aAAa,6DAA0C;IAEvE,UAAmB,aAAa,cAAqB,OAAO,EAAE,KAAG,QAAQ,OAAO,EAAE,CAAC,CAclF;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,MAAM,MAAM,+BAA+B,GAAG,aAAa,CAAC,eAAe,CAAC,+BAA+B,CAAC,CAAC,CAAA;AAQ7G,qBAAa,yBAAyB,CACpC,OAAO,SAAS,+BAA+B,GAAG,+BAA+B,CACjF,SAAQ,eAAe,CAAC,OAAO,CAAC;IAChC,OAAgB,aAAa,6DAA0C;IAEvE,UAAmB,aAAa,cAAqB,OAAO,EAAE,KAAG,QAAQ,OAAO,EAAE,CAAC,CAclF;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,MAAM,MAAM,+BAA+B,GAAG,aAAa,CAAC,eAAe,CAAC,+BAA+B,CAAC,CAAC,CAAA;AAQ7G,qBAAa,yBAAyB,CACpC,OAAO,SAAS,+BAA+B,GAAG,+BAA+B,CACjF,SAAQ,eAAe,CAAC,OAAO,CAAC;IAChC,OAAgB,aAAa,6DAA0C;IAEvE,UAAmB,aAAa,cAAqB,OAAO,EAAE,KAAG,QAAQ,OAAO,EAAE,CAAC,CAclF;CACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NftCollectionScoreDiviner } from './Diviner';
|
|
2
|
-
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@
|
|
2
|
+
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@xylabs/object").BaseParamsFields & {
|
|
3
3
|
account?: import("@xyo-network/account-model").AccountInstance | "random" | undefined;
|
|
4
4
|
config: import("@xyo-network/payload-model").SchemaFields & object & Omit<{
|
|
5
5
|
readonly archivist?: string | undefined;
|
|
@@ -8,6 +8,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
8
8
|
readonly paging?: Record<string, {
|
|
9
9
|
size?: number | undefined;
|
|
10
10
|
}> | undefined;
|
|
11
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
11
12
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
12
13
|
readonly security?: {
|
|
13
14
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -24,6 +25,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
24
25
|
readonly paging?: Record<string, {
|
|
25
26
|
size?: number | undefined;
|
|
26
27
|
}> | undefined;
|
|
28
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
27
29
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
28
30
|
readonly security?: {
|
|
29
31
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B
|
|
1
|
+
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GASzC,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NftCollectionScoreDiviner } from './Diviner';
|
|
2
|
-
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@
|
|
2
|
+
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@xylabs/object").BaseParamsFields & {
|
|
3
3
|
account?: import("@xyo-network/account-model").AccountInstance | "random" | undefined;
|
|
4
4
|
config: import("@xyo-network/payload-model").SchemaFields & object & Omit<{
|
|
5
5
|
readonly archivist?: string | undefined;
|
|
@@ -8,6 +8,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
8
8
|
readonly paging?: Record<string, {
|
|
9
9
|
size?: number | undefined;
|
|
10
10
|
}> | undefined;
|
|
11
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
11
12
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
12
13
|
readonly security?: {
|
|
13
14
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -24,6 +25,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
24
25
|
readonly paging?: Record<string, {
|
|
25
26
|
size?: number | undefined;
|
|
26
27
|
}> | undefined;
|
|
28
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
27
29
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
28
30
|
readonly security?: {
|
|
29
31
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B
|
|
1
|
+
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GASzC,CAAA"}
|
package/dist/browser/Plugin.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NftCollectionScoreDiviner } from './Diviner';
|
|
2
|
-
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@
|
|
2
|
+
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@xylabs/object").BaseParamsFields & {
|
|
3
3
|
account?: import("@xyo-network/account-model").AccountInstance | "random" | undefined;
|
|
4
4
|
config: import("@xyo-network/payload-model").SchemaFields & object & Omit<{
|
|
5
5
|
readonly archivist?: string | undefined;
|
|
@@ -8,6 +8,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
8
8
|
readonly paging?: Record<string, {
|
|
9
9
|
size?: number | undefined;
|
|
10
10
|
}> | undefined;
|
|
11
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
11
12
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
12
13
|
readonly security?: {
|
|
13
14
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -24,6 +25,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
24
25
|
readonly paging?: Record<string, {
|
|
25
26
|
size?: number | undefined;
|
|
26
27
|
}> | undefined;
|
|
28
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
27
29
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
28
30
|
readonly security?: {
|
|
29
31
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B
|
|
1
|
+
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GASzC,CAAA"}
|
package/dist/browser/index.cjs
CHANGED
|
@@ -31,7 +31,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
31
31
|
// src/Diviner.ts
|
|
32
32
|
var import_crypto_nft_collection_payload_plugin = require("@xyo-network/crypto-nft-collection-payload-plugin");
|
|
33
33
|
var import_diviner_abstract = require("@xyo-network/diviner-abstract");
|
|
34
|
-
var
|
|
34
|
+
var import_payload_builder = require("@xyo-network/payload-builder");
|
|
35
35
|
|
|
36
36
|
// src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts
|
|
37
37
|
var import_crypto_nft_score_model = require("@xyo-network/crypto-nft-score-model");
|
|
@@ -138,7 +138,7 @@ var NftCollectionScoreDiviner = class extends import_diviner_abstract.AbstractDi
|
|
|
138
138
|
// Get score
|
|
139
139
|
toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),
|
|
140
140
|
// Hash sources
|
|
141
|
-
|
|
141
|
+
import_payload_builder.PayloadBuilder.dataHash(nftCollectionInfo)
|
|
142
142
|
]);
|
|
143
143
|
return { ...score, schema: import_crypto_nft_collection_payload_plugin.NftCollectionScoreSchema, sources: [sourceHash] };
|
|
144
144
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/Diviner.ts","../../src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/scoreTotalAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/metadata.ts","../../src/lib/rating/criteria/scoring/total.ts","../../src/lib/rating/criteria/index.ts","../../src/lib/rating/analyzeNftCollection.ts","../../src/Plugin.ts"],"sourcesContent":["export * from './Diviner'\nexport * from './lib'\nexport { NftCollectionScoreDivinerPlugin as default, NftCollectionScoreDivinerPlugin } from './Plugin'\n","import {\n isNftCollectionInfo,\n NftCollectionInfo,\n NftCollectionMetadata,\n NftCollectionScore,\n NftCollectionScoreDivinerConfig,\n NftCollectionScoreDivinerConfigSchema,\n NftCollectionScoreSchema,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { DivinerParams } from '@xyo-network/diviner-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { Payload } from '@xyo-network/payload-model'\n\nimport { analyzeNftCollection, NftCollectionAnalysis } from './lib'\n\nexport type NftCollectionScoreDivinerParams = DivinerParams<AnyConfigSchema<NftCollectionScoreDivinerConfig>>\n\nconst toNftCollectionScore = (nftCollectionInfo: NftCollectionInfo, scores: NftCollectionAnalysis): NftCollectionScore => {\n const { name, symbol, address, chainId, type } = nftCollectionInfo\n const metadata: NftCollectionMetadata = { address, chainId, name, symbol, type }\n return { ...metadata, schema: NftCollectionScoreSchema, scores }\n}\n\nexport class NftCollectionScoreDiviner<\n TParams extends NftCollectionScoreDivinerParams = NftCollectionScoreDivinerParams,\n> extends AbstractDiviner<TParams> {\n static override configSchemas = [NftCollectionScoreDivinerConfigSchema]\n\n protected override divineHandler = async (payloads?: Payload[]): Promise<Payload[]> => {\n const nftCollectionInfos = payloads?.filter(isNftCollectionInfo) ?? []\n const results = await Promise.all(\n nftCollectionInfos.map<Promise<NftCollectionScore>>(async (nftCollectionInfo) => {\n const [score, sourceHash] = await Promise.all([\n // Get score\n toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),\n // Hash sources\n PayloadHasher.hashAsync(nftCollectionInfo),\n ])\n return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore\n }),\n )\n return results\n }\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\nexport const scoreIndividualAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n const entries = Object.entries(attributes)\n if (entries.length === 0) return [0, maxScore]\n const scores = Object.entries(attributes).flatMap(([_trait, { values }]) => {\n return Object.entries(values).map<Score>(([_traitValue, metrics]) => {\n const rarity = Math.min(Math.round((1 - metrics.binomial.p) * maxScore), maxScore)\n return [rarity, maxScore]\n })\n })\n // eslint-disable-next-line unicorn/no-array-reduce\n const total = scores.reduce<Score>(([a, b], [c, d]) => [a + c, b + d], [0, 0])\n return normalize(total, maxScore)\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\n/**\n * Mean: What value is the distribution centered around\n */\nconst defaultMu = 0.15\n\n/**\n * Standard Deviation: How spread out is the distribution\n */\nconst defaultSigma = 0.1\n\n/**\n * Calculates the Gaussian probability density\n * @param x\n * @param mu Mean\n * @param sigma Standard Deviation\n * @returns\n */\nconst gaussianProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n const sqrtTwoPi = Math.sqrt(2 * Math.PI)\n const denominator = sigma * sqrtTwoPi\n const power = -0.5 * Math.pow((x - mu) / sigma, 2)\n return (1 / denominator) * Math.exp(power)\n}\n\n/**\n * For a Gaussian distribution, the peak of the distribution is the mean\n */\nconst maxProbabilityDensity = gaussianProbabilityDensity(defaultMu)\n\n/**\n * We're working on some assumptions here:\n *\n * - If you have a 100% chance of getting a trait, everyone get's a trophy\n * - If you have a 50% chance of getting a trait, it's not rare\n * - If you have a 0% chance of getting a trait, it's not fun\n *\n * So we're looking for something Pareto-ish (somewhere between\n * 80/20 or 90/10) as that's a good & sustainable model for the\n * distribution of many traits in real life.\n * However, we also don't want to maximally reward collections\n * that have a lot of single attributes distributed uniformly\n * (basically a 0% trait probably) as that's perfectly entropic\n * but not very interesting (some overlap is desirable).\n * So we're using a Gaussian distribution to model the\n * probability density of the joint probability of all traits\n * centered around 15%.\n * @param info\n * @returns\n */\nexport const scoreTotalAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n // This has somewhat of a filtering function by causing anything with 100% probability to\n // add no value to the end score\n const jointProbability = Object.entries(attributes).reduce((acc, [_trait, { metrics }]) => {\n return acc * metrics.binomial.p\n }, 1)\n const probabilityDensity = gaussianProbabilityDensity(jointProbability)\n const score: Score = [probabilityDensity, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreIndividualAttributes } from './scoreIndividualAttributes'\nimport { scoreTotalAttributes } from './scoreTotalAttributes'\n\nexport const attributeScoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n 'Metadata Attributes Individual': { score: scoreIndividualAttributes, weight: 2 },\n 'Metadata Attributes Total': { score: scoreTotalAttributes, weight: 2 },\n}\n\nexport const scoreMetadata: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...attributeScoringCriteria,\n}\n","import { NftCollectionCount } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\n/**\n * This \"magic\" value was obtained using Solver in Excel\n * to find the median, with mu/sigma fixed, which maximizes\n * the distribution (the mode for lognormal) at 10,000\n */\nconst median = 81_030_839.821_735_2\nconst defaultMu = Math.log(median)\nconst defaultSigma = 3\nconst mode = Math.exp(defaultMu - Math.pow(defaultSigma, 2))\n/**\n * Calculates the log-normal probability density\n * @param x the value at which you want to calculate the probability density\n * @param mu mean of the associated normal distribution\n * @param sigma standard deviation of the associated normal distribution\n * @returns\n */\nconst logNormalProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n if (x <= 0) return 0\n const logX = Math.log(x)\n return (1 / (x * sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((logX - mu) / sigma, 2))\n}\n\n/**\n * For a lognormal distribution, the peak of the distribution is the mode\n */\nconst maxProbabilityDensity = logNormalProbabilityDensity(mode)\n\nconst maxScore = 10\n\n/**\n * We're working on some assumptions here:\n * - If there's < 1000 NFTs in your collection it starts becoming too niche\n * - If there's > 20,000 NFTs in your collection it starts becoming too broad\n * So there's a sweet spot somewhere between 2000 and 10,000\n * where a collection has enough NFTs to be interesting, but\n * not so many that it's teetering on a diluted money grab.\n * To model that we're using a log-normal distribution optimized\n * to maximally reward collections in the aforementioned range\n * @param nft\n * @returns\n */\nexport const scoreTotal = (nft: NftCollectionCount): Score => {\n const density = logNormalProbabilityDensity(nft.total)\n const score: Score = [density, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreMetadata, scoreTotal } from './scoring'\n\nexport const scoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...scoreMetadata,\n Total: { score: scoreTotal, weight: 2 },\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { Score } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoringCriteria } from './criteria'\n\nexport type ScoringCriteriaKey = keyof typeof scoringCriteria & PropertyKey\n\nexport type NftCollectionAnalysis = {\n [key in ScoringCriteriaKey]: Score\n}\n\nexport const analyzeNftCollection = async (\n /**\n * The NFT to evaluate\n */\n nft: NftCollectionInfo,\n): Promise<NftCollectionAnalysis> => {\n const result = Object.fromEntries(\n await Promise.all(\n Object.entries(scoringCriteria).map(async ([key, { score, weight }]) => {\n const rawScore = await score(nft)\n const weighted = rawScore.map((v) => v * weight) as Score\n return [key, weighted] as const\n }),\n ),\n ) as NftCollectionAnalysis\n return result\n}\n","import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDivinerPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { NftCollectionScoreDiviner } from './Diviner'\n\nexport const NftCollectionScoreDivinerPlugin = () =>\n createPayloadSetDivinerPlugin<NftCollectionScoreDiviner>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await NftCollectionScoreDiviner.create(params)\n return result\n },\n },\n )\n"],"mappings":"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","names":["import_crypto_nft_score_model","maxScore","import_crypto_nft_score_model","defaultMu","defaultSigma","maxProbabilityDensity","maxScore"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/Diviner.ts","../../src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/scoreTotalAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/metadata.ts","../../src/lib/rating/criteria/scoring/total.ts","../../src/lib/rating/criteria/index.ts","../../src/lib/rating/analyzeNftCollection.ts","../../src/Plugin.ts"],"sourcesContent":["export * from './Diviner'\nexport * from './lib'\nexport { NftCollectionScoreDivinerPlugin as default, NftCollectionScoreDivinerPlugin } from './Plugin'\n","import {\n isNftCollectionInfo,\n NftCollectionInfo,\n NftCollectionMetadata,\n NftCollectionScore,\n NftCollectionScoreDivinerConfig,\n NftCollectionScoreDivinerConfigSchema,\n NftCollectionScoreSchema,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { DivinerParams } from '@xyo-network/diviner-model'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport { Payload } from '@xyo-network/payload-model'\n\nimport { analyzeNftCollection, NftCollectionAnalysis } from './lib'\n\nexport type NftCollectionScoreDivinerParams = DivinerParams<AnyConfigSchema<NftCollectionScoreDivinerConfig>>\n\nconst toNftCollectionScore = (nftCollectionInfo: NftCollectionInfo, scores: NftCollectionAnalysis): NftCollectionScore => {\n const { name, symbol, address, chainId, type } = nftCollectionInfo\n const metadata: NftCollectionMetadata = { address, chainId, name, symbol, type }\n return { ...metadata, schema: NftCollectionScoreSchema, scores }\n}\n\nexport class NftCollectionScoreDiviner<\n TParams extends NftCollectionScoreDivinerParams = NftCollectionScoreDivinerParams,\n> extends AbstractDiviner<TParams> {\n static override configSchemas = [NftCollectionScoreDivinerConfigSchema]\n\n protected override divineHandler = async (payloads?: Payload[]): Promise<Payload[]> => {\n const nftCollectionInfos = payloads?.filter(isNftCollectionInfo) ?? []\n const results = await Promise.all(\n nftCollectionInfos.map<Promise<NftCollectionScore>>(async (nftCollectionInfo) => {\n const [score, sourceHash] = await Promise.all([\n // Get score\n toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),\n // Hash sources\n PayloadBuilder.dataHash(nftCollectionInfo),\n ])\n return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore\n }),\n )\n return results\n }\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\nexport const scoreIndividualAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n const entries = Object.entries(attributes)\n if (entries.length === 0) return [0, maxScore]\n const scores = Object.entries(attributes).flatMap(([_trait, { values }]) => {\n return Object.entries(values).map<Score>(([_traitValue, metrics]) => {\n const rarity = Math.min(Math.round((1 - metrics.binomial.p) * maxScore), maxScore)\n return [rarity, maxScore]\n })\n })\n // eslint-disable-next-line unicorn/no-array-reduce\n const total = scores.reduce<Score>(([a, b], [c, d]) => [a + c, b + d], [0, 0])\n return normalize(total, maxScore)\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\n/**\n * Mean: What value is the distribution centered around\n */\nconst defaultMu = 0.15\n\n/**\n * Standard Deviation: How spread out is the distribution\n */\nconst defaultSigma = 0.1\n\n/**\n * Calculates the Gaussian probability density\n * @param x\n * @param mu Mean\n * @param sigma Standard Deviation\n * @returns\n */\nconst gaussianProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n const sqrtTwoPi = Math.sqrt(2 * Math.PI)\n const denominator = sigma * sqrtTwoPi\n const power = -0.5 * Math.pow((x - mu) / sigma, 2)\n return (1 / denominator) * Math.exp(power)\n}\n\n/**\n * For a Gaussian distribution, the peak of the distribution is the mean\n */\nconst maxProbabilityDensity = gaussianProbabilityDensity(defaultMu)\n\n/**\n * We're working on some assumptions here:\n *\n * - If you have a 100% chance of getting a trait, everyone get's a trophy\n * - If you have a 50% chance of getting a trait, it's not rare\n * - If you have a 0% chance of getting a trait, it's not fun\n *\n * So we're looking for something Pareto-ish (somewhere between\n * 80/20 or 90/10) as that's a good & sustainable model for the\n * distribution of many traits in real life.\n * However, we also don't want to maximally reward collections\n * that have a lot of single attributes distributed uniformly\n * (basically a 0% trait probably) as that's perfectly entropic\n * but not very interesting (some overlap is desirable).\n * So we're using a Gaussian distribution to model the\n * probability density of the joint probability of all traits\n * centered around 15%.\n * @param info\n * @returns\n */\nexport const scoreTotalAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n // This has somewhat of a filtering function by causing anything with 100% probability to\n // add no value to the end score\n const jointProbability = Object.entries(attributes).reduce((acc, [_trait, { metrics }]) => {\n return acc * metrics.binomial.p\n }, 1)\n const probabilityDensity = gaussianProbabilityDensity(jointProbability)\n const score: Score = [probabilityDensity, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreIndividualAttributes } from './scoreIndividualAttributes'\nimport { scoreTotalAttributes } from './scoreTotalAttributes'\n\nexport const attributeScoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n 'Metadata Attributes Individual': { score: scoreIndividualAttributes, weight: 2 },\n 'Metadata Attributes Total': { score: scoreTotalAttributes, weight: 2 },\n}\n\nexport const scoreMetadata: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...attributeScoringCriteria,\n}\n","import { NftCollectionCount } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\n/**\n * This \"magic\" value was obtained using Solver in Excel\n * to find the median, with mu/sigma fixed, which maximizes\n * the distribution (the mode for lognormal) at 10,000\n */\nconst median = 81_030_839.821_735_2\nconst defaultMu = Math.log(median)\nconst defaultSigma = 3\nconst mode = Math.exp(defaultMu - Math.pow(defaultSigma, 2))\n/**\n * Calculates the log-normal probability density\n * @param x the value at which you want to calculate the probability density\n * @param mu mean of the associated normal distribution\n * @param sigma standard deviation of the associated normal distribution\n * @returns\n */\nconst logNormalProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n if (x <= 0) return 0\n const logX = Math.log(x)\n return (1 / (x * sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((logX - mu) / sigma, 2))\n}\n\n/**\n * For a lognormal distribution, the peak of the distribution is the mode\n */\nconst maxProbabilityDensity = logNormalProbabilityDensity(mode)\n\nconst maxScore = 10\n\n/**\n * We're working on some assumptions here:\n * - If there's < 1000 NFTs in your collection it starts becoming too niche\n * - If there's > 20,000 NFTs in your collection it starts becoming too broad\n * So there's a sweet spot somewhere between 2000 and 10,000\n * where a collection has enough NFTs to be interesting, but\n * not so many that it's teetering on a diluted money grab.\n * To model that we're using a log-normal distribution optimized\n * to maximally reward collections in the aforementioned range\n * @param nft\n * @returns\n */\nexport const scoreTotal = (nft: NftCollectionCount): Score => {\n const density = logNormalProbabilityDensity(nft.total)\n const score: Score = [density, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreMetadata, scoreTotal } from './scoring'\n\nexport const scoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...scoreMetadata,\n Total: { score: scoreTotal, weight: 2 },\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { Score } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoringCriteria } from './criteria'\n\nexport type ScoringCriteriaKey = keyof typeof scoringCriteria & PropertyKey\n\nexport type NftCollectionAnalysis = {\n [key in ScoringCriteriaKey]: Score\n}\n\nexport const analyzeNftCollection = async (\n /**\n * The NFT to evaluate\n */\n nft: NftCollectionInfo,\n): Promise<NftCollectionAnalysis> => {\n const result = Object.fromEntries(\n await Promise.all(\n Object.entries(scoringCriteria).map(async ([key, { score, weight }]) => {\n const rawScore = await score(nft)\n const weighted = rawScore.map((v) => v * weight) as Score\n return [key, weighted] as const\n }),\n ),\n ) as NftCollectionAnalysis\n return result\n}\n","import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDivinerPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { NftCollectionScoreDiviner } from './Diviner'\n\nexport const NftCollectionScoreDivinerPlugin = () =>\n createPayloadSetDivinerPlugin<NftCollectionScoreDiviner>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await NftCollectionScoreDiviner.create(params)\n return result\n },\n },\n )\n"],"mappings":"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","names":["import_crypto_nft_score_model","maxScore","import_crypto_nft_score_model","defaultMu","defaultSigma","maxProbabilityDensity","maxScore"]}
|
package/dist/browser/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
NftCollectionScoreSchema
|
|
6
6
|
} from "@xyo-network/crypto-nft-collection-payload-plugin";
|
|
7
7
|
import { AbstractDiviner } from "@xyo-network/diviner-abstract";
|
|
8
|
-
import {
|
|
8
|
+
import { PayloadBuilder } from "@xyo-network/payload-builder";
|
|
9
9
|
|
|
10
10
|
// src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts
|
|
11
11
|
import { normalize } from "@xyo-network/crypto-nft-score-model";
|
|
@@ -112,7 +112,7 @@ var NftCollectionScoreDiviner = class extends AbstractDiviner {
|
|
|
112
112
|
// Get score
|
|
113
113
|
toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),
|
|
114
114
|
// Hash sources
|
|
115
|
-
|
|
115
|
+
PayloadBuilder.dataHash(nftCollectionInfo)
|
|
116
116
|
]);
|
|
117
117
|
return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] };
|
|
118
118
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Diviner.ts","../../src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/scoreTotalAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/metadata.ts","../../src/lib/rating/criteria/scoring/total.ts","../../src/lib/rating/criteria/index.ts","../../src/lib/rating/analyzeNftCollection.ts","../../src/Plugin.ts"],"sourcesContent":["import {\n isNftCollectionInfo,\n NftCollectionInfo,\n NftCollectionMetadata,\n NftCollectionScore,\n NftCollectionScoreDivinerConfig,\n NftCollectionScoreDivinerConfigSchema,\n NftCollectionScoreSchema,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { DivinerParams } from '@xyo-network/diviner-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { Payload } from '@xyo-network/payload-model'\n\nimport { analyzeNftCollection, NftCollectionAnalysis } from './lib'\n\nexport type NftCollectionScoreDivinerParams = DivinerParams<AnyConfigSchema<NftCollectionScoreDivinerConfig>>\n\nconst toNftCollectionScore = (nftCollectionInfo: NftCollectionInfo, scores: NftCollectionAnalysis): NftCollectionScore => {\n const { name, symbol, address, chainId, type } = nftCollectionInfo\n const metadata: NftCollectionMetadata = { address, chainId, name, symbol, type }\n return { ...metadata, schema: NftCollectionScoreSchema, scores }\n}\n\nexport class NftCollectionScoreDiviner<\n TParams extends NftCollectionScoreDivinerParams = NftCollectionScoreDivinerParams,\n> extends AbstractDiviner<TParams> {\n static override configSchemas = [NftCollectionScoreDivinerConfigSchema]\n\n protected override divineHandler = async (payloads?: Payload[]): Promise<Payload[]> => {\n const nftCollectionInfos = payloads?.filter(isNftCollectionInfo) ?? []\n const results = await Promise.all(\n nftCollectionInfos.map<Promise<NftCollectionScore>>(async (nftCollectionInfo) => {\n const [score, sourceHash] = await Promise.all([\n // Get score\n toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),\n // Hash sources\n PayloadHasher.hashAsync(nftCollectionInfo),\n ])\n return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore\n }),\n )\n return results\n }\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\nexport const scoreIndividualAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n const entries = Object.entries(attributes)\n if (entries.length === 0) return [0, maxScore]\n const scores = Object.entries(attributes).flatMap(([_trait, { values }]) => {\n return Object.entries(values).map<Score>(([_traitValue, metrics]) => {\n const rarity = Math.min(Math.round((1 - metrics.binomial.p) * maxScore), maxScore)\n return [rarity, maxScore]\n })\n })\n // eslint-disable-next-line unicorn/no-array-reduce\n const total = scores.reduce<Score>(([a, b], [c, d]) => [a + c, b + d], [0, 0])\n return normalize(total, maxScore)\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\n/**\n * Mean: What value is the distribution centered around\n */\nconst defaultMu = 0.15\n\n/**\n * Standard Deviation: How spread out is the distribution\n */\nconst defaultSigma = 0.1\n\n/**\n * Calculates the Gaussian probability density\n * @param x\n * @param mu Mean\n * @param sigma Standard Deviation\n * @returns\n */\nconst gaussianProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n const sqrtTwoPi = Math.sqrt(2 * Math.PI)\n const denominator = sigma * sqrtTwoPi\n const power = -0.5 * Math.pow((x - mu) / sigma, 2)\n return (1 / denominator) * Math.exp(power)\n}\n\n/**\n * For a Gaussian distribution, the peak of the distribution is the mean\n */\nconst maxProbabilityDensity = gaussianProbabilityDensity(defaultMu)\n\n/**\n * We're working on some assumptions here:\n *\n * - If you have a 100% chance of getting a trait, everyone get's a trophy\n * - If you have a 50% chance of getting a trait, it's not rare\n * - If you have a 0% chance of getting a trait, it's not fun\n *\n * So we're looking for something Pareto-ish (somewhere between\n * 80/20 or 90/10) as that's a good & sustainable model for the\n * distribution of many traits in real life.\n * However, we also don't want to maximally reward collections\n * that have a lot of single attributes distributed uniformly\n * (basically a 0% trait probably) as that's perfectly entropic\n * but not very interesting (some overlap is desirable).\n * So we're using a Gaussian distribution to model the\n * probability density of the joint probability of all traits\n * centered around 15%.\n * @param info\n * @returns\n */\nexport const scoreTotalAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n // This has somewhat of a filtering function by causing anything with 100% probability to\n // add no value to the end score\n const jointProbability = Object.entries(attributes).reduce((acc, [_trait, { metrics }]) => {\n return acc * metrics.binomial.p\n }, 1)\n const probabilityDensity = gaussianProbabilityDensity(jointProbability)\n const score: Score = [probabilityDensity, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreIndividualAttributes } from './scoreIndividualAttributes'\nimport { scoreTotalAttributes } from './scoreTotalAttributes'\n\nexport const attributeScoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n 'Metadata Attributes Individual': { score: scoreIndividualAttributes, weight: 2 },\n 'Metadata Attributes Total': { score: scoreTotalAttributes, weight: 2 },\n}\n\nexport const scoreMetadata: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...attributeScoringCriteria,\n}\n","import { NftCollectionCount } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\n/**\n * This \"magic\" value was obtained using Solver in Excel\n * to find the median, with mu/sigma fixed, which maximizes\n * the distribution (the mode for lognormal) at 10,000\n */\nconst median = 81_030_839.821_735_2\nconst defaultMu = Math.log(median)\nconst defaultSigma = 3\nconst mode = Math.exp(defaultMu - Math.pow(defaultSigma, 2))\n/**\n * Calculates the log-normal probability density\n * @param x the value at which you want to calculate the probability density\n * @param mu mean of the associated normal distribution\n * @param sigma standard deviation of the associated normal distribution\n * @returns\n */\nconst logNormalProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n if (x <= 0) return 0\n const logX = Math.log(x)\n return (1 / (x * sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((logX - mu) / sigma, 2))\n}\n\n/**\n * For a lognormal distribution, the peak of the distribution is the mode\n */\nconst maxProbabilityDensity = logNormalProbabilityDensity(mode)\n\nconst maxScore = 10\n\n/**\n * We're working on some assumptions here:\n * - If there's < 1000 NFTs in your collection it starts becoming too niche\n * - If there's > 20,000 NFTs in your collection it starts becoming too broad\n * So there's a sweet spot somewhere between 2000 and 10,000\n * where a collection has enough NFTs to be interesting, but\n * not so many that it's teetering on a diluted money grab.\n * To model that we're using a log-normal distribution optimized\n * to maximally reward collections in the aforementioned range\n * @param nft\n * @returns\n */\nexport const scoreTotal = (nft: NftCollectionCount): Score => {\n const density = logNormalProbabilityDensity(nft.total)\n const score: Score = [density, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreMetadata, scoreTotal } from './scoring'\n\nexport const scoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...scoreMetadata,\n Total: { score: scoreTotal, weight: 2 },\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { Score } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoringCriteria } from './criteria'\n\nexport type ScoringCriteriaKey = keyof typeof scoringCriteria & PropertyKey\n\nexport type NftCollectionAnalysis = {\n [key in ScoringCriteriaKey]: Score\n}\n\nexport const analyzeNftCollection = async (\n /**\n * The NFT to evaluate\n */\n nft: NftCollectionInfo,\n): Promise<NftCollectionAnalysis> => {\n const result = Object.fromEntries(\n await Promise.all(\n Object.entries(scoringCriteria).map(async ([key, { score, weight }]) => {\n const rawScore = await score(nft)\n const weighted = rawScore.map((v) => v * weight) as Score\n return [key, weighted] as const\n }),\n ),\n ) as NftCollectionAnalysis\n return result\n}\n","import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDivinerPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { NftCollectionScoreDiviner } from './Diviner'\n\nexport const NftCollectionScoreDivinerPlugin = () =>\n createPayloadSetDivinerPlugin<NftCollectionScoreDiviner>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await NftCollectionScoreDiviner.create(params)\n return result\n },\n },\n )\n"],"mappings":"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","names":["normalize","maxScore","normalize","defaultMu","defaultSigma","maxProbabilityDensity","maxScore"]}
|
|
1
|
+
{"version":3,"sources":["../../src/Diviner.ts","../../src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/scoreTotalAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/metadata.ts","../../src/lib/rating/criteria/scoring/total.ts","../../src/lib/rating/criteria/index.ts","../../src/lib/rating/analyzeNftCollection.ts","../../src/Plugin.ts"],"sourcesContent":["import {\n isNftCollectionInfo,\n NftCollectionInfo,\n NftCollectionMetadata,\n NftCollectionScore,\n NftCollectionScoreDivinerConfig,\n NftCollectionScoreDivinerConfigSchema,\n NftCollectionScoreSchema,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { DivinerParams } from '@xyo-network/diviner-model'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport { Payload } from '@xyo-network/payload-model'\n\nimport { analyzeNftCollection, NftCollectionAnalysis } from './lib'\n\nexport type NftCollectionScoreDivinerParams = DivinerParams<AnyConfigSchema<NftCollectionScoreDivinerConfig>>\n\nconst toNftCollectionScore = (nftCollectionInfo: NftCollectionInfo, scores: NftCollectionAnalysis): NftCollectionScore => {\n const { name, symbol, address, chainId, type } = nftCollectionInfo\n const metadata: NftCollectionMetadata = { address, chainId, name, symbol, type }\n return { ...metadata, schema: NftCollectionScoreSchema, scores }\n}\n\nexport class NftCollectionScoreDiviner<\n TParams extends NftCollectionScoreDivinerParams = NftCollectionScoreDivinerParams,\n> extends AbstractDiviner<TParams> {\n static override configSchemas = [NftCollectionScoreDivinerConfigSchema]\n\n protected override divineHandler = async (payloads?: Payload[]): Promise<Payload[]> => {\n const nftCollectionInfos = payloads?.filter(isNftCollectionInfo) ?? []\n const results = await Promise.all(\n nftCollectionInfos.map<Promise<NftCollectionScore>>(async (nftCollectionInfo) => {\n const [score, sourceHash] = await Promise.all([\n // Get score\n toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),\n // Hash sources\n PayloadBuilder.dataHash(nftCollectionInfo),\n ])\n return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore\n }),\n )\n return results\n }\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\nexport const scoreIndividualAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n const entries = Object.entries(attributes)\n if (entries.length === 0) return [0, maxScore]\n const scores = Object.entries(attributes).flatMap(([_trait, { values }]) => {\n return Object.entries(values).map<Score>(([_traitValue, metrics]) => {\n const rarity = Math.min(Math.round((1 - metrics.binomial.p) * maxScore), maxScore)\n return [rarity, maxScore]\n })\n })\n // eslint-disable-next-line unicorn/no-array-reduce\n const total = scores.reduce<Score>(([a, b], [c, d]) => [a + c, b + d], [0, 0])\n return normalize(total, maxScore)\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\n/**\n * Mean: What value is the distribution centered around\n */\nconst defaultMu = 0.15\n\n/**\n * Standard Deviation: How spread out is the distribution\n */\nconst defaultSigma = 0.1\n\n/**\n * Calculates the Gaussian probability density\n * @param x\n * @param mu Mean\n * @param sigma Standard Deviation\n * @returns\n */\nconst gaussianProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n const sqrtTwoPi = Math.sqrt(2 * Math.PI)\n const denominator = sigma * sqrtTwoPi\n const power = -0.5 * Math.pow((x - mu) / sigma, 2)\n return (1 / denominator) * Math.exp(power)\n}\n\n/**\n * For a Gaussian distribution, the peak of the distribution is the mean\n */\nconst maxProbabilityDensity = gaussianProbabilityDensity(defaultMu)\n\n/**\n * We're working on some assumptions here:\n *\n * - If you have a 100% chance of getting a trait, everyone get's a trophy\n * - If you have a 50% chance of getting a trait, it's not rare\n * - If you have a 0% chance of getting a trait, it's not fun\n *\n * So we're looking for something Pareto-ish (somewhere between\n * 80/20 or 90/10) as that's a good & sustainable model for the\n * distribution of many traits in real life.\n * However, we also don't want to maximally reward collections\n * that have a lot of single attributes distributed uniformly\n * (basically a 0% trait probably) as that's perfectly entropic\n * but not very interesting (some overlap is desirable).\n * So we're using a Gaussian distribution to model the\n * probability density of the joint probability of all traits\n * centered around 15%.\n * @param info\n * @returns\n */\nexport const scoreTotalAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n // This has somewhat of a filtering function by causing anything with 100% probability to\n // add no value to the end score\n const jointProbability = Object.entries(attributes).reduce((acc, [_trait, { metrics }]) => {\n return acc * metrics.binomial.p\n }, 1)\n const probabilityDensity = gaussianProbabilityDensity(jointProbability)\n const score: Score = [probabilityDensity, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreIndividualAttributes } from './scoreIndividualAttributes'\nimport { scoreTotalAttributes } from './scoreTotalAttributes'\n\nexport const attributeScoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n 'Metadata Attributes Individual': { score: scoreIndividualAttributes, weight: 2 },\n 'Metadata Attributes Total': { score: scoreTotalAttributes, weight: 2 },\n}\n\nexport const scoreMetadata: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...attributeScoringCriteria,\n}\n","import { NftCollectionCount } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\n/**\n * This \"magic\" value was obtained using Solver in Excel\n * to find the median, with mu/sigma fixed, which maximizes\n * the distribution (the mode for lognormal) at 10,000\n */\nconst median = 81_030_839.821_735_2\nconst defaultMu = Math.log(median)\nconst defaultSigma = 3\nconst mode = Math.exp(defaultMu - Math.pow(defaultSigma, 2))\n/**\n * Calculates the log-normal probability density\n * @param x the value at which you want to calculate the probability density\n * @param mu mean of the associated normal distribution\n * @param sigma standard deviation of the associated normal distribution\n * @returns\n */\nconst logNormalProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n if (x <= 0) return 0\n const logX = Math.log(x)\n return (1 / (x * sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((logX - mu) / sigma, 2))\n}\n\n/**\n * For a lognormal distribution, the peak of the distribution is the mode\n */\nconst maxProbabilityDensity = logNormalProbabilityDensity(mode)\n\nconst maxScore = 10\n\n/**\n * We're working on some assumptions here:\n * - If there's < 1000 NFTs in your collection it starts becoming too niche\n * - If there's > 20,000 NFTs in your collection it starts becoming too broad\n * So there's a sweet spot somewhere between 2000 and 10,000\n * where a collection has enough NFTs to be interesting, but\n * not so many that it's teetering on a diluted money grab.\n * To model that we're using a log-normal distribution optimized\n * to maximally reward collections in the aforementioned range\n * @param nft\n * @returns\n */\nexport const scoreTotal = (nft: NftCollectionCount): Score => {\n const density = logNormalProbabilityDensity(nft.total)\n const score: Score = [density, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreMetadata, scoreTotal } from './scoring'\n\nexport const scoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...scoreMetadata,\n Total: { score: scoreTotal, weight: 2 },\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { Score } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoringCriteria } from './criteria'\n\nexport type ScoringCriteriaKey = keyof typeof scoringCriteria & PropertyKey\n\nexport type NftCollectionAnalysis = {\n [key in ScoringCriteriaKey]: Score\n}\n\nexport const analyzeNftCollection = async (\n /**\n * The NFT to evaluate\n */\n nft: NftCollectionInfo,\n): Promise<NftCollectionAnalysis> => {\n const result = Object.fromEntries(\n await Promise.all(\n Object.entries(scoringCriteria).map(async ([key, { score, weight }]) => {\n const rawScore = await score(nft)\n const weighted = rawScore.map((v) => v * weight) as Score\n return [key, weighted] as const\n }),\n ),\n ) as NftCollectionAnalysis\n return result\n}\n","import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDivinerPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { NftCollectionScoreDiviner } from './Diviner'\n\nexport const NftCollectionScoreDivinerPlugin = () =>\n createPayloadSetDivinerPlugin<NftCollectionScoreDiviner>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await NftCollectionScoreDiviner.create(params)\n return result\n },\n },\n )\n"],"mappings":"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","names":["normalize","maxScore","normalize","defaultMu","defaultSigma","maxProbabilityDensity","maxScore"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,MAAM,MAAM,+BAA+B,GAAG,aAAa,CAAC,eAAe,CAAC,+BAA+B,CAAC,CAAC,CAAA;AAQ7G,qBAAa,yBAAyB,CACpC,OAAO,SAAS,+BAA+B,GAAG,+BAA+B,CACjF,SAAQ,eAAe,CAAC,OAAO,CAAC;IAChC,OAAgB,aAAa,6DAA0C;IAEvE,UAAmB,aAAa,cAAqB,OAAO,EAAE,KAAG,QAAQ,OAAO,EAAE,CAAC,CAclF;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,MAAM,MAAM,+BAA+B,GAAG,aAAa,CAAC,eAAe,CAAC,+BAA+B,CAAC,CAAC,CAAA;AAQ7G,qBAAa,yBAAyB,CACpC,OAAO,SAAS,+BAA+B,GAAG,+BAA+B,CACjF,SAAQ,eAAe,CAAC,OAAO,CAAC;IAChC,OAAgB,aAAa,6DAA0C;IAEvE,UAAmB,aAAa,cAAqB,OAAO,EAAE,KAAG,QAAQ,OAAO,EAAE,CAAC,CAclF;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../src/Diviner.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,+BAA+B,EAGhC,MAAM,mDAAmD,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAE3D,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAIpD,MAAM,MAAM,+BAA+B,GAAG,aAAa,CAAC,eAAe,CAAC,+BAA+B,CAAC,CAAC,CAAA;AAQ7G,qBAAa,yBAAyB,CACpC,OAAO,SAAS,+BAA+B,GAAG,+BAA+B,CACjF,SAAQ,eAAe,CAAC,OAAO,CAAC;IAChC,OAAgB,aAAa,6DAA0C;IAEvE,UAAmB,aAAa,cAAqB,OAAO,EAAE,KAAG,QAAQ,OAAO,EAAE,CAAC,CAclF;CACF"}
|
package/dist/node/Plugin.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NftCollectionScoreDiviner } from './Diviner';
|
|
2
|
-
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@
|
|
2
|
+
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@xylabs/object").BaseParamsFields & {
|
|
3
3
|
account?: import("@xyo-network/account-model").AccountInstance | "random" | undefined;
|
|
4
4
|
config: import("@xyo-network/payload-model").SchemaFields & object & Omit<{
|
|
5
5
|
readonly archivist?: string | undefined;
|
|
@@ -8,6 +8,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
8
8
|
readonly paging?: Record<string, {
|
|
9
9
|
size?: number | undefined;
|
|
10
10
|
}> | undefined;
|
|
11
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
11
12
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
12
13
|
readonly security?: {
|
|
13
14
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -24,6 +25,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
24
25
|
readonly paging?: Record<string, {
|
|
25
26
|
size?: number | undefined;
|
|
26
27
|
}> | undefined;
|
|
28
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
27
29
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
28
30
|
readonly security?: {
|
|
29
31
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B
|
|
1
|
+
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GASzC,CAAA"}
|
package/dist/node/Plugin.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NftCollectionScoreDiviner } from './Diviner';
|
|
2
|
-
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@
|
|
2
|
+
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@xylabs/object").BaseParamsFields & {
|
|
3
3
|
account?: import("@xyo-network/account-model").AccountInstance | "random" | undefined;
|
|
4
4
|
config: import("@xyo-network/payload-model").SchemaFields & object & Omit<{
|
|
5
5
|
readonly archivist?: string | undefined;
|
|
@@ -8,6 +8,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
8
8
|
readonly paging?: Record<string, {
|
|
9
9
|
size?: number | undefined;
|
|
10
10
|
}> | undefined;
|
|
11
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
11
12
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
12
13
|
readonly security?: {
|
|
13
14
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -24,6 +25,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
24
25
|
readonly paging?: Record<string, {
|
|
25
26
|
size?: number | undefined;
|
|
26
27
|
}> | undefined;
|
|
28
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
27
29
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
28
30
|
readonly security?: {
|
|
29
31
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B
|
|
1
|
+
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GASzC,CAAA"}
|
package/dist/node/Plugin.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NftCollectionScoreDiviner } from './Diviner';
|
|
2
|
-
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@
|
|
2
|
+
export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network/payloadset-plugin").PayloadSetDivinerPlugin<NftCollectionScoreDiviner<import("@xylabs/object").BaseParamsFields & {
|
|
3
3
|
account?: import("@xyo-network/account-model").AccountInstance | "random" | undefined;
|
|
4
4
|
config: import("@xyo-network/payload-model").SchemaFields & object & Omit<{
|
|
5
5
|
readonly archivist?: string | undefined;
|
|
@@ -8,6 +8,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
8
8
|
readonly paging?: Record<string, {
|
|
9
9
|
size?: number | undefined;
|
|
10
10
|
}> | undefined;
|
|
11
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
11
12
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
12
13
|
readonly security?: {
|
|
13
14
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -24,6 +25,7 @@ export declare const NftCollectionScoreDivinerPlugin: () => import("@xyo-network
|
|
|
24
25
|
readonly paging?: Record<string, {
|
|
25
26
|
size?: number | undefined;
|
|
26
27
|
}> | undefined;
|
|
28
|
+
readonly retry?: import("@xylabs/retry").RetryConfig | undefined;
|
|
27
29
|
schema: "network.xyo.crypto.nft.collection.score.diviner.config";
|
|
28
30
|
readonly security?: {
|
|
29
31
|
readonly allowAnonymous?: boolean | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B
|
|
1
|
+
{"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/Plugin.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAA;AAErD,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GASzC,CAAA"}
|
package/dist/node/index.cjs
CHANGED
|
@@ -31,7 +31,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
31
31
|
// src/Diviner.ts
|
|
32
32
|
var import_crypto_nft_collection_payload_plugin = require("@xyo-network/crypto-nft-collection-payload-plugin");
|
|
33
33
|
var import_diviner_abstract = require("@xyo-network/diviner-abstract");
|
|
34
|
-
var
|
|
34
|
+
var import_payload_builder = require("@xyo-network/payload-builder");
|
|
35
35
|
|
|
36
36
|
// src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts
|
|
37
37
|
var import_crypto_nft_score_model = require("@xyo-network/crypto-nft-score-model");
|
|
@@ -138,7 +138,7 @@ var NftCollectionScoreDiviner = class extends import_diviner_abstract.AbstractDi
|
|
|
138
138
|
// Get score
|
|
139
139
|
toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),
|
|
140
140
|
// Hash sources
|
|
141
|
-
|
|
141
|
+
import_payload_builder.PayloadBuilder.dataHash(nftCollectionInfo)
|
|
142
142
|
]);
|
|
143
143
|
return { ...score, schema: import_crypto_nft_collection_payload_plugin.NftCollectionScoreSchema, sources: [sourceHash] };
|
|
144
144
|
})
|
package/dist/node/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/Diviner.ts","../../src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/scoreTotalAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/metadata.ts","../../src/lib/rating/criteria/scoring/total.ts","../../src/lib/rating/criteria/index.ts","../../src/lib/rating/analyzeNftCollection.ts","../../src/Plugin.ts"],"sourcesContent":["export * from './Diviner'\nexport * from './lib'\nexport { NftCollectionScoreDivinerPlugin as default, NftCollectionScoreDivinerPlugin } from './Plugin'\n","import {\n isNftCollectionInfo,\n NftCollectionInfo,\n NftCollectionMetadata,\n NftCollectionScore,\n NftCollectionScoreDivinerConfig,\n NftCollectionScoreDivinerConfigSchema,\n NftCollectionScoreSchema,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { DivinerParams } from '@xyo-network/diviner-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { Payload } from '@xyo-network/payload-model'\n\nimport { analyzeNftCollection, NftCollectionAnalysis } from './lib'\n\nexport type NftCollectionScoreDivinerParams = DivinerParams<AnyConfigSchema<NftCollectionScoreDivinerConfig>>\n\nconst toNftCollectionScore = (nftCollectionInfo: NftCollectionInfo, scores: NftCollectionAnalysis): NftCollectionScore => {\n const { name, symbol, address, chainId, type } = nftCollectionInfo\n const metadata: NftCollectionMetadata = { address, chainId, name, symbol, type }\n return { ...metadata, schema: NftCollectionScoreSchema, scores }\n}\n\nexport class NftCollectionScoreDiviner<\n TParams extends NftCollectionScoreDivinerParams = NftCollectionScoreDivinerParams,\n> extends AbstractDiviner<TParams> {\n static override configSchemas = [NftCollectionScoreDivinerConfigSchema]\n\n protected override divineHandler = async (payloads?: Payload[]): Promise<Payload[]> => {\n const nftCollectionInfos = payloads?.filter(isNftCollectionInfo) ?? []\n const results = await Promise.all(\n nftCollectionInfos.map<Promise<NftCollectionScore>>(async (nftCollectionInfo) => {\n const [score, sourceHash] = await Promise.all([\n // Get score\n toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),\n // Hash sources\n PayloadHasher.hashAsync(nftCollectionInfo),\n ])\n return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore\n }),\n )\n return results\n }\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\nexport const scoreIndividualAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n const entries = Object.entries(attributes)\n if (entries.length === 0) return [0, maxScore]\n const scores = Object.entries(attributes).flatMap(([_trait, { values }]) => {\n return Object.entries(values).map<Score>(([_traitValue, metrics]) => {\n const rarity = Math.min(Math.round((1 - metrics.binomial.p) * maxScore), maxScore)\n return [rarity, maxScore]\n })\n })\n // eslint-disable-next-line unicorn/no-array-reduce\n const total = scores.reduce<Score>(([a, b], [c, d]) => [a + c, b + d], [0, 0])\n return normalize(total, maxScore)\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\n/**\n * Mean: What value is the distribution centered around\n */\nconst defaultMu = 0.15\n\n/**\n * Standard Deviation: How spread out is the distribution\n */\nconst defaultSigma = 0.1\n\n/**\n * Calculates the Gaussian probability density\n * @param x\n * @param mu Mean\n * @param sigma Standard Deviation\n * @returns\n */\nconst gaussianProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n const sqrtTwoPi = Math.sqrt(2 * Math.PI)\n const denominator = sigma * sqrtTwoPi\n const power = -0.5 * Math.pow((x - mu) / sigma, 2)\n return (1 / denominator) * Math.exp(power)\n}\n\n/**\n * For a Gaussian distribution, the peak of the distribution is the mean\n */\nconst maxProbabilityDensity = gaussianProbabilityDensity(defaultMu)\n\n/**\n * We're working on some assumptions here:\n *\n * - If you have a 100% chance of getting a trait, everyone get's a trophy\n * - If you have a 50% chance of getting a trait, it's not rare\n * - If you have a 0% chance of getting a trait, it's not fun\n *\n * So we're looking for something Pareto-ish (somewhere between\n * 80/20 or 90/10) as that's a good & sustainable model for the\n * distribution of many traits in real life.\n * However, we also don't want to maximally reward collections\n * that have a lot of single attributes distributed uniformly\n * (basically a 0% trait probably) as that's perfectly entropic\n * but not very interesting (some overlap is desirable).\n * So we're using a Gaussian distribution to model the\n * probability density of the joint probability of all traits\n * centered around 15%.\n * @param info\n * @returns\n */\nexport const scoreTotalAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n // This has somewhat of a filtering function by causing anything with 100% probability to\n // add no value to the end score\n const jointProbability = Object.entries(attributes).reduce((acc, [_trait, { metrics }]) => {\n return acc * metrics.binomial.p\n }, 1)\n const probabilityDensity = gaussianProbabilityDensity(jointProbability)\n const score: Score = [probabilityDensity, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreIndividualAttributes } from './scoreIndividualAttributes'\nimport { scoreTotalAttributes } from './scoreTotalAttributes'\n\nexport const attributeScoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n 'Metadata Attributes Individual': { score: scoreIndividualAttributes, weight: 2 },\n 'Metadata Attributes Total': { score: scoreTotalAttributes, weight: 2 },\n}\n\nexport const scoreMetadata: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...attributeScoringCriteria,\n}\n","import { NftCollectionCount } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\n/**\n * This \"magic\" value was obtained using Solver in Excel\n * to find the median, with mu/sigma fixed, which maximizes\n * the distribution (the mode for lognormal) at 10,000\n */\nconst median = 81_030_839.821_735_2\nconst defaultMu = Math.log(median)\nconst defaultSigma = 3\nconst mode = Math.exp(defaultMu - Math.pow(defaultSigma, 2))\n/**\n * Calculates the log-normal probability density\n * @param x the value at which you want to calculate the probability density\n * @param mu mean of the associated normal distribution\n * @param sigma standard deviation of the associated normal distribution\n * @returns\n */\nconst logNormalProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n if (x <= 0) return 0\n const logX = Math.log(x)\n return (1 / (x * sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((logX - mu) / sigma, 2))\n}\n\n/**\n * For a lognormal distribution, the peak of the distribution is the mode\n */\nconst maxProbabilityDensity = logNormalProbabilityDensity(mode)\n\nconst maxScore = 10\n\n/**\n * We're working on some assumptions here:\n * - If there's < 1000 NFTs in your collection it starts becoming too niche\n * - If there's > 20,000 NFTs in your collection it starts becoming too broad\n * So there's a sweet spot somewhere between 2000 and 10,000\n * where a collection has enough NFTs to be interesting, but\n * not so many that it's teetering on a diluted money grab.\n * To model that we're using a log-normal distribution optimized\n * to maximally reward collections in the aforementioned range\n * @param nft\n * @returns\n */\nexport const scoreTotal = (nft: NftCollectionCount): Score => {\n const density = logNormalProbabilityDensity(nft.total)\n const score: Score = [density, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreMetadata, scoreTotal } from './scoring'\n\nexport const scoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...scoreMetadata,\n Total: { score: scoreTotal, weight: 2 },\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { Score } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoringCriteria } from './criteria'\n\nexport type ScoringCriteriaKey = keyof typeof scoringCriteria & PropertyKey\n\nexport type NftCollectionAnalysis = {\n [key in ScoringCriteriaKey]: Score\n}\n\nexport const analyzeNftCollection = async (\n /**\n * The NFT to evaluate\n */\n nft: NftCollectionInfo,\n): Promise<NftCollectionAnalysis> => {\n const result = Object.fromEntries(\n await Promise.all(\n Object.entries(scoringCriteria).map(async ([key, { score, weight }]) => {\n const rawScore = await score(nft)\n const weighted = rawScore.map((v) => v * weight) as Score\n return [key, weighted] as const\n }),\n ),\n ) as NftCollectionAnalysis\n return result\n}\n","import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDivinerPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { NftCollectionScoreDiviner } from './Diviner'\n\nexport const NftCollectionScoreDivinerPlugin = () =>\n createPayloadSetDivinerPlugin<NftCollectionScoreDiviner>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await NftCollectionScoreDiviner.create(params)\n return result\n },\n },\n )\n"],"mappings":"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","names":["import_crypto_nft_score_model","maxScore","import_crypto_nft_score_model","defaultMu","defaultSigma","maxProbabilityDensity","maxScore"]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/Diviner.ts","../../src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/scoreTotalAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/metadata.ts","../../src/lib/rating/criteria/scoring/total.ts","../../src/lib/rating/criteria/index.ts","../../src/lib/rating/analyzeNftCollection.ts","../../src/Plugin.ts"],"sourcesContent":["export * from './Diviner'\nexport * from './lib'\nexport { NftCollectionScoreDivinerPlugin as default, NftCollectionScoreDivinerPlugin } from './Plugin'\n","import {\n isNftCollectionInfo,\n NftCollectionInfo,\n NftCollectionMetadata,\n NftCollectionScore,\n NftCollectionScoreDivinerConfig,\n NftCollectionScoreDivinerConfigSchema,\n NftCollectionScoreSchema,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { DivinerParams } from '@xyo-network/diviner-model'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport { Payload } from '@xyo-network/payload-model'\n\nimport { analyzeNftCollection, NftCollectionAnalysis } from './lib'\n\nexport type NftCollectionScoreDivinerParams = DivinerParams<AnyConfigSchema<NftCollectionScoreDivinerConfig>>\n\nconst toNftCollectionScore = (nftCollectionInfo: NftCollectionInfo, scores: NftCollectionAnalysis): NftCollectionScore => {\n const { name, symbol, address, chainId, type } = nftCollectionInfo\n const metadata: NftCollectionMetadata = { address, chainId, name, symbol, type }\n return { ...metadata, schema: NftCollectionScoreSchema, scores }\n}\n\nexport class NftCollectionScoreDiviner<\n TParams extends NftCollectionScoreDivinerParams = NftCollectionScoreDivinerParams,\n> extends AbstractDiviner<TParams> {\n static override configSchemas = [NftCollectionScoreDivinerConfigSchema]\n\n protected override divineHandler = async (payloads?: Payload[]): Promise<Payload[]> => {\n const nftCollectionInfos = payloads?.filter(isNftCollectionInfo) ?? []\n const results = await Promise.all(\n nftCollectionInfos.map<Promise<NftCollectionScore>>(async (nftCollectionInfo) => {\n const [score, sourceHash] = await Promise.all([\n // Get score\n toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),\n // Hash sources\n PayloadBuilder.dataHash(nftCollectionInfo),\n ])\n return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore\n }),\n )\n return results\n }\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\nexport const scoreIndividualAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n const entries = Object.entries(attributes)\n if (entries.length === 0) return [0, maxScore]\n const scores = Object.entries(attributes).flatMap(([_trait, { values }]) => {\n return Object.entries(values).map<Score>(([_traitValue, metrics]) => {\n const rarity = Math.min(Math.round((1 - metrics.binomial.p) * maxScore), maxScore)\n return [rarity, maxScore]\n })\n })\n // eslint-disable-next-line unicorn/no-array-reduce\n const total = scores.reduce<Score>(([a, b], [c, d]) => [a + c, b + d], [0, 0])\n return normalize(total, maxScore)\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\n/**\n * Mean: What value is the distribution centered around\n */\nconst defaultMu = 0.15\n\n/**\n * Standard Deviation: How spread out is the distribution\n */\nconst defaultSigma = 0.1\n\n/**\n * Calculates the Gaussian probability density\n * @param x\n * @param mu Mean\n * @param sigma Standard Deviation\n * @returns\n */\nconst gaussianProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n const sqrtTwoPi = Math.sqrt(2 * Math.PI)\n const denominator = sigma * sqrtTwoPi\n const power = -0.5 * Math.pow((x - mu) / sigma, 2)\n return (1 / denominator) * Math.exp(power)\n}\n\n/**\n * For a Gaussian distribution, the peak of the distribution is the mean\n */\nconst maxProbabilityDensity = gaussianProbabilityDensity(defaultMu)\n\n/**\n * We're working on some assumptions here:\n *\n * - If you have a 100% chance of getting a trait, everyone get's a trophy\n * - If you have a 50% chance of getting a trait, it's not rare\n * - If you have a 0% chance of getting a trait, it's not fun\n *\n * So we're looking for something Pareto-ish (somewhere between\n * 80/20 or 90/10) as that's a good & sustainable model for the\n * distribution of many traits in real life.\n * However, we also don't want to maximally reward collections\n * that have a lot of single attributes distributed uniformly\n * (basically a 0% trait probably) as that's perfectly entropic\n * but not very interesting (some overlap is desirable).\n * So we're using a Gaussian distribution to model the\n * probability density of the joint probability of all traits\n * centered around 15%.\n * @param info\n * @returns\n */\nexport const scoreTotalAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n // This has somewhat of a filtering function by causing anything with 100% probability to\n // add no value to the end score\n const jointProbability = Object.entries(attributes).reduce((acc, [_trait, { metrics }]) => {\n return acc * metrics.binomial.p\n }, 1)\n const probabilityDensity = gaussianProbabilityDensity(jointProbability)\n const score: Score = [probabilityDensity, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreIndividualAttributes } from './scoreIndividualAttributes'\nimport { scoreTotalAttributes } from './scoreTotalAttributes'\n\nexport const attributeScoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n 'Metadata Attributes Individual': { score: scoreIndividualAttributes, weight: 2 },\n 'Metadata Attributes Total': { score: scoreTotalAttributes, weight: 2 },\n}\n\nexport const scoreMetadata: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...attributeScoringCriteria,\n}\n","import { NftCollectionCount } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\n/**\n * This \"magic\" value was obtained using Solver in Excel\n * to find the median, with mu/sigma fixed, which maximizes\n * the distribution (the mode for lognormal) at 10,000\n */\nconst median = 81_030_839.821_735_2\nconst defaultMu = Math.log(median)\nconst defaultSigma = 3\nconst mode = Math.exp(defaultMu - Math.pow(defaultSigma, 2))\n/**\n * Calculates the log-normal probability density\n * @param x the value at which you want to calculate the probability density\n * @param mu mean of the associated normal distribution\n * @param sigma standard deviation of the associated normal distribution\n * @returns\n */\nconst logNormalProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n if (x <= 0) return 0\n const logX = Math.log(x)\n return (1 / (x * sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((logX - mu) / sigma, 2))\n}\n\n/**\n * For a lognormal distribution, the peak of the distribution is the mode\n */\nconst maxProbabilityDensity = logNormalProbabilityDensity(mode)\n\nconst maxScore = 10\n\n/**\n * We're working on some assumptions here:\n * - If there's < 1000 NFTs in your collection it starts becoming too niche\n * - If there's > 20,000 NFTs in your collection it starts becoming too broad\n * So there's a sweet spot somewhere between 2000 and 10,000\n * where a collection has enough NFTs to be interesting, but\n * not so many that it's teetering on a diluted money grab.\n * To model that we're using a log-normal distribution optimized\n * to maximally reward collections in the aforementioned range\n * @param nft\n * @returns\n */\nexport const scoreTotal = (nft: NftCollectionCount): Score => {\n const density = logNormalProbabilityDensity(nft.total)\n const score: Score = [density, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreMetadata, scoreTotal } from './scoring'\n\nexport const scoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...scoreMetadata,\n Total: { score: scoreTotal, weight: 2 },\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { Score } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoringCriteria } from './criteria'\n\nexport type ScoringCriteriaKey = keyof typeof scoringCriteria & PropertyKey\n\nexport type NftCollectionAnalysis = {\n [key in ScoringCriteriaKey]: Score\n}\n\nexport const analyzeNftCollection = async (\n /**\n * The NFT to evaluate\n */\n nft: NftCollectionInfo,\n): Promise<NftCollectionAnalysis> => {\n const result = Object.fromEntries(\n await Promise.all(\n Object.entries(scoringCriteria).map(async ([key, { score, weight }]) => {\n const rawScore = await score(nft)\n const weighted = rawScore.map((v) => v * weight) as Score\n return [key, weighted] as const\n }),\n ),\n ) as NftCollectionAnalysis\n return result\n}\n","import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDivinerPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { NftCollectionScoreDiviner } from './Diviner'\n\nexport const NftCollectionScoreDivinerPlugin = () =>\n createPayloadSetDivinerPlugin<NftCollectionScoreDiviner>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await NftCollectionScoreDiviner.create(params)\n return result\n },\n },\n )\n"],"mappings":"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","names":["import_crypto_nft_score_model","maxScore","import_crypto_nft_score_model","defaultMu","defaultSigma","maxProbabilityDensity","maxScore"]}
|
package/dist/node/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
NftCollectionScoreSchema
|
|
6
6
|
} from "@xyo-network/crypto-nft-collection-payload-plugin";
|
|
7
7
|
import { AbstractDiviner } from "@xyo-network/diviner-abstract";
|
|
8
|
-
import {
|
|
8
|
+
import { PayloadBuilder } from "@xyo-network/payload-builder";
|
|
9
9
|
|
|
10
10
|
// src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts
|
|
11
11
|
import { normalize } from "@xyo-network/crypto-nft-score-model";
|
|
@@ -112,7 +112,7 @@ var NftCollectionScoreDiviner = class extends AbstractDiviner {
|
|
|
112
112
|
// Get score
|
|
113
113
|
toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),
|
|
114
114
|
// Hash sources
|
|
115
|
-
|
|
115
|
+
PayloadBuilder.dataHash(nftCollectionInfo)
|
|
116
116
|
]);
|
|
117
117
|
return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] };
|
|
118
118
|
})
|
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/Diviner.ts","../../src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/scoreTotalAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/metadata.ts","../../src/lib/rating/criteria/scoring/total.ts","../../src/lib/rating/criteria/index.ts","../../src/lib/rating/analyzeNftCollection.ts","../../src/Plugin.ts"],"sourcesContent":["import {\n isNftCollectionInfo,\n NftCollectionInfo,\n NftCollectionMetadata,\n NftCollectionScore,\n NftCollectionScoreDivinerConfig,\n NftCollectionScoreDivinerConfigSchema,\n NftCollectionScoreSchema,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { DivinerParams } from '@xyo-network/diviner-model'\nimport { PayloadHasher } from '@xyo-network/hash'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { Payload } from '@xyo-network/payload-model'\n\nimport { analyzeNftCollection, NftCollectionAnalysis } from './lib'\n\nexport type NftCollectionScoreDivinerParams = DivinerParams<AnyConfigSchema<NftCollectionScoreDivinerConfig>>\n\nconst toNftCollectionScore = (nftCollectionInfo: NftCollectionInfo, scores: NftCollectionAnalysis): NftCollectionScore => {\n const { name, symbol, address, chainId, type } = nftCollectionInfo\n const metadata: NftCollectionMetadata = { address, chainId, name, symbol, type }\n return { ...metadata, schema: NftCollectionScoreSchema, scores }\n}\n\nexport class NftCollectionScoreDiviner<\n TParams extends NftCollectionScoreDivinerParams = NftCollectionScoreDivinerParams,\n> extends AbstractDiviner<TParams> {\n static override configSchemas = [NftCollectionScoreDivinerConfigSchema]\n\n protected override divineHandler = async (payloads?: Payload[]): Promise<Payload[]> => {\n const nftCollectionInfos = payloads?.filter(isNftCollectionInfo) ?? []\n const results = await Promise.all(\n nftCollectionInfos.map<Promise<NftCollectionScore>>(async (nftCollectionInfo) => {\n const [score, sourceHash] = await Promise.all([\n // Get score\n toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),\n // Hash sources\n PayloadHasher.hashAsync(nftCollectionInfo),\n ])\n return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore\n }),\n )\n return results\n }\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\nexport const scoreIndividualAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n const entries = Object.entries(attributes)\n if (entries.length === 0) return [0, maxScore]\n const scores = Object.entries(attributes).flatMap(([_trait, { values }]) => {\n return Object.entries(values).map<Score>(([_traitValue, metrics]) => {\n const rarity = Math.min(Math.round((1 - metrics.binomial.p) * maxScore), maxScore)\n return [rarity, maxScore]\n })\n })\n // eslint-disable-next-line unicorn/no-array-reduce\n const total = scores.reduce<Score>(([a, b], [c, d]) => [a + c, b + d], [0, 0])\n return normalize(total, maxScore)\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\n/**\n * Mean: What value is the distribution centered around\n */\nconst defaultMu = 0.15\n\n/**\n * Standard Deviation: How spread out is the distribution\n */\nconst defaultSigma = 0.1\n\n/**\n * Calculates the Gaussian probability density\n * @param x\n * @param mu Mean\n * @param sigma Standard Deviation\n * @returns\n */\nconst gaussianProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n const sqrtTwoPi = Math.sqrt(2 * Math.PI)\n const denominator = sigma * sqrtTwoPi\n const power = -0.5 * Math.pow((x - mu) / sigma, 2)\n return (1 / denominator) * Math.exp(power)\n}\n\n/**\n * For a Gaussian distribution, the peak of the distribution is the mean\n */\nconst maxProbabilityDensity = gaussianProbabilityDensity(defaultMu)\n\n/**\n * We're working on some assumptions here:\n *\n * - If you have a 100% chance of getting a trait, everyone get's a trophy\n * - If you have a 50% chance of getting a trait, it's not rare\n * - If you have a 0% chance of getting a trait, it's not fun\n *\n * So we're looking for something Pareto-ish (somewhere between\n * 80/20 or 90/10) as that's a good & sustainable model for the\n * distribution of many traits in real life.\n * However, we also don't want to maximally reward collections\n * that have a lot of single attributes distributed uniformly\n * (basically a 0% trait probably) as that's perfectly entropic\n * but not very interesting (some overlap is desirable).\n * So we're using a Gaussian distribution to model the\n * probability density of the joint probability of all traits\n * centered around 15%.\n * @param info\n * @returns\n */\nexport const scoreTotalAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n // This has somewhat of a filtering function by causing anything with 100% probability to\n // add no value to the end score\n const jointProbability = Object.entries(attributes).reduce((acc, [_trait, { metrics }]) => {\n return acc * metrics.binomial.p\n }, 1)\n const probabilityDensity = gaussianProbabilityDensity(jointProbability)\n const score: Score = [probabilityDensity, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreIndividualAttributes } from './scoreIndividualAttributes'\nimport { scoreTotalAttributes } from './scoreTotalAttributes'\n\nexport const attributeScoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n 'Metadata Attributes Individual': { score: scoreIndividualAttributes, weight: 2 },\n 'Metadata Attributes Total': { score: scoreTotalAttributes, weight: 2 },\n}\n\nexport const scoreMetadata: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...attributeScoringCriteria,\n}\n","import { NftCollectionCount } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\n/**\n * This \"magic\" value was obtained using Solver in Excel\n * to find the median, with mu/sigma fixed, which maximizes\n * the distribution (the mode for lognormal) at 10,000\n */\nconst median = 81_030_839.821_735_2\nconst defaultMu = Math.log(median)\nconst defaultSigma = 3\nconst mode = Math.exp(defaultMu - Math.pow(defaultSigma, 2))\n/**\n * Calculates the log-normal probability density\n * @param x the value at which you want to calculate the probability density\n * @param mu mean of the associated normal distribution\n * @param sigma standard deviation of the associated normal distribution\n * @returns\n */\nconst logNormalProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n if (x <= 0) return 0\n const logX = Math.log(x)\n return (1 / (x * sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((logX - mu) / sigma, 2))\n}\n\n/**\n * For a lognormal distribution, the peak of the distribution is the mode\n */\nconst maxProbabilityDensity = logNormalProbabilityDensity(mode)\n\nconst maxScore = 10\n\n/**\n * We're working on some assumptions here:\n * - If there's < 1000 NFTs in your collection it starts becoming too niche\n * - If there's > 20,000 NFTs in your collection it starts becoming too broad\n * So there's a sweet spot somewhere between 2000 and 10,000\n * where a collection has enough NFTs to be interesting, but\n * not so many that it's teetering on a diluted money grab.\n * To model that we're using a log-normal distribution optimized\n * to maximally reward collections in the aforementioned range\n * @param nft\n * @returns\n */\nexport const scoreTotal = (nft: NftCollectionCount): Score => {\n const density = logNormalProbabilityDensity(nft.total)\n const score: Score = [density, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreMetadata, scoreTotal } from './scoring'\n\nexport const scoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...scoreMetadata,\n Total: { score: scoreTotal, weight: 2 },\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { Score } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoringCriteria } from './criteria'\n\nexport type ScoringCriteriaKey = keyof typeof scoringCriteria & PropertyKey\n\nexport type NftCollectionAnalysis = {\n [key in ScoringCriteriaKey]: Score\n}\n\nexport const analyzeNftCollection = async (\n /**\n * The NFT to evaluate\n */\n nft: NftCollectionInfo,\n): Promise<NftCollectionAnalysis> => {\n const result = Object.fromEntries(\n await Promise.all(\n Object.entries(scoringCriteria).map(async ([key, { score, weight }]) => {\n const rawScore = await score(nft)\n const weighted = rawScore.map((v) => v * weight) as Score\n return [key, weighted] as const\n }),\n ),\n ) as NftCollectionAnalysis\n return result\n}\n","import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDivinerPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { NftCollectionScoreDiviner } from './Diviner'\n\nexport const NftCollectionScoreDivinerPlugin = () =>\n createPayloadSetDivinerPlugin<NftCollectionScoreDiviner>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await NftCollectionScoreDiviner.create(params)\n return result\n },\n },\n )\n"],"mappings":"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","names":["normalize","maxScore","normalize","defaultMu","defaultSigma","maxProbabilityDensity","maxScore"]}
|
|
1
|
+
{"version":3,"sources":["../../src/Diviner.ts","../../src/lib/rating/criteria/scoring/metadata/scoreIndividualAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/scoreTotalAttributes.ts","../../src/lib/rating/criteria/scoring/metadata/metadata.ts","../../src/lib/rating/criteria/scoring/total.ts","../../src/lib/rating/criteria/index.ts","../../src/lib/rating/analyzeNftCollection.ts","../../src/Plugin.ts"],"sourcesContent":["import {\n isNftCollectionInfo,\n NftCollectionInfo,\n NftCollectionMetadata,\n NftCollectionScore,\n NftCollectionScoreDivinerConfig,\n NftCollectionScoreDivinerConfigSchema,\n NftCollectionScoreSchema,\n} from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { DivinerParams } from '@xyo-network/diviner-model'\nimport { AnyConfigSchema } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport { Payload } from '@xyo-network/payload-model'\n\nimport { analyzeNftCollection, NftCollectionAnalysis } from './lib'\n\nexport type NftCollectionScoreDivinerParams = DivinerParams<AnyConfigSchema<NftCollectionScoreDivinerConfig>>\n\nconst toNftCollectionScore = (nftCollectionInfo: NftCollectionInfo, scores: NftCollectionAnalysis): NftCollectionScore => {\n const { name, symbol, address, chainId, type } = nftCollectionInfo\n const metadata: NftCollectionMetadata = { address, chainId, name, symbol, type }\n return { ...metadata, schema: NftCollectionScoreSchema, scores }\n}\n\nexport class NftCollectionScoreDiviner<\n TParams extends NftCollectionScoreDivinerParams = NftCollectionScoreDivinerParams,\n> extends AbstractDiviner<TParams> {\n static override configSchemas = [NftCollectionScoreDivinerConfigSchema]\n\n protected override divineHandler = async (payloads?: Payload[]): Promise<Payload[]> => {\n const nftCollectionInfos = payloads?.filter(isNftCollectionInfo) ?? []\n const results = await Promise.all(\n nftCollectionInfos.map<Promise<NftCollectionScore>>(async (nftCollectionInfo) => {\n const [score, sourceHash] = await Promise.all([\n // Get score\n toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),\n // Hash sources\n PayloadBuilder.dataHash(nftCollectionInfo),\n ])\n return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore\n }),\n )\n return results\n }\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\nexport const scoreIndividualAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n const entries = Object.entries(attributes)\n if (entries.length === 0) return [0, maxScore]\n const scores = Object.entries(attributes).flatMap(([_trait, { values }]) => {\n return Object.entries(values).map<Score>(([_traitValue, metrics]) => {\n const rarity = Math.min(Math.round((1 - metrics.binomial.p) * maxScore), maxScore)\n return [rarity, maxScore]\n })\n })\n // eslint-disable-next-line unicorn/no-array-reduce\n const total = scores.reduce<Score>(([a, b], [c, d]) => [a + c, b + d], [0, 0])\n return normalize(total, maxScore)\n}\n","import { NftCollectionAttributeMetrics } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\nconst maxScore = 10\n\n/**\n * Mean: What value is the distribution centered around\n */\nconst defaultMu = 0.15\n\n/**\n * Standard Deviation: How spread out is the distribution\n */\nconst defaultSigma = 0.1\n\n/**\n * Calculates the Gaussian probability density\n * @param x\n * @param mu Mean\n * @param sigma Standard Deviation\n * @returns\n */\nconst gaussianProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n const sqrtTwoPi = Math.sqrt(2 * Math.PI)\n const denominator = sigma * sqrtTwoPi\n const power = -0.5 * Math.pow((x - mu) / sigma, 2)\n return (1 / denominator) * Math.exp(power)\n}\n\n/**\n * For a Gaussian distribution, the peak of the distribution is the mean\n */\nconst maxProbabilityDensity = gaussianProbabilityDensity(defaultMu)\n\n/**\n * We're working on some assumptions here:\n *\n * - If you have a 100% chance of getting a trait, everyone get's a trophy\n * - If you have a 50% chance of getting a trait, it's not rare\n * - If you have a 0% chance of getting a trait, it's not fun\n *\n * So we're looking for something Pareto-ish (somewhere between\n * 80/20 or 90/10) as that's a good & sustainable model for the\n * distribution of many traits in real life.\n * However, we also don't want to maximally reward collections\n * that have a lot of single attributes distributed uniformly\n * (basically a 0% trait probably) as that's perfectly entropic\n * but not very interesting (some overlap is desirable).\n * So we're using a Gaussian distribution to model the\n * probability density of the joint probability of all traits\n * centered around 15%.\n * @param info\n * @returns\n */\nexport const scoreTotalAttributes = (info: NftCollectionAttributeMetrics): Score => {\n const { attributes } = info.metrics.metadata\n // This has somewhat of a filtering function by causing anything with 100% probability to\n // add no value to the end score\n const jointProbability = Object.entries(attributes).reduce((acc, [_trait, { metrics }]) => {\n return acc * metrics.binomial.p\n }, 1)\n const probabilityDensity = gaussianProbabilityDensity(jointProbability)\n const score: Score = [probabilityDensity, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreIndividualAttributes } from './scoreIndividualAttributes'\nimport { scoreTotalAttributes } from './scoreTotalAttributes'\n\nexport const attributeScoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n 'Metadata Attributes Individual': { score: scoreIndividualAttributes, weight: 2 },\n 'Metadata Attributes Total': { score: scoreTotalAttributes, weight: 2 },\n}\n\nexport const scoreMetadata: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...attributeScoringCriteria,\n}\n","import { NftCollectionCount } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { normalize, Score } from '@xyo-network/crypto-nft-score-model'\n\n/**\n * This \"magic\" value was obtained using Solver in Excel\n * to find the median, with mu/sigma fixed, which maximizes\n * the distribution (the mode for lognormal) at 10,000\n */\nconst median = 81_030_839.821_735_2\nconst defaultMu = Math.log(median)\nconst defaultSigma = 3\nconst mode = Math.exp(defaultMu - Math.pow(defaultSigma, 2))\n/**\n * Calculates the log-normal probability density\n * @param x the value at which you want to calculate the probability density\n * @param mu mean of the associated normal distribution\n * @param sigma standard deviation of the associated normal distribution\n * @returns\n */\nconst logNormalProbabilityDensity = (x: number, mu: number = defaultMu, sigma: number = defaultSigma): number => {\n if (x <= 0) return 0\n const logX = Math.log(x)\n return (1 / (x * sigma * Math.sqrt(2 * Math.PI))) * Math.exp(-0.5 * Math.pow((logX - mu) / sigma, 2))\n}\n\n/**\n * For a lognormal distribution, the peak of the distribution is the mode\n */\nconst maxProbabilityDensity = logNormalProbabilityDensity(mode)\n\nconst maxScore = 10\n\n/**\n * We're working on some assumptions here:\n * - If there's < 1000 NFTs in your collection it starts becoming too niche\n * - If there's > 20,000 NFTs in your collection it starts becoming too broad\n * So there's a sweet spot somewhere between 2000 and 10,000\n * where a collection has enough NFTs to be interesting, but\n * not so many that it's teetering on a diluted money grab.\n * To model that we're using a log-normal distribution optimized\n * to maximally reward collections in the aforementioned range\n * @param nft\n * @returns\n */\nexport const scoreTotal = (nft: NftCollectionCount): Score => {\n const density = logNormalProbabilityDensity(nft.total)\n const score: Score = [density, maxProbabilityDensity]\n return normalize(score, maxScore)\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { WeightedScoringCriteria } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoreMetadata, scoreTotal } from './scoring'\n\nexport const scoringCriteria: { [key: string]: WeightedScoringCriteria<NftCollectionInfo> } = {\n ...scoreMetadata,\n Total: { score: scoreTotal, weight: 2 },\n}\n","import { NftCollectionInfo } from '@xyo-network/crypto-nft-collection-payload-plugin'\nimport { Score } from '@xyo-network/crypto-nft-score-model'\n\nimport { scoringCriteria } from './criteria'\n\nexport type ScoringCriteriaKey = keyof typeof scoringCriteria & PropertyKey\n\nexport type NftCollectionAnalysis = {\n [key in ScoringCriteriaKey]: Score\n}\n\nexport const analyzeNftCollection = async (\n /**\n * The NFT to evaluate\n */\n nft: NftCollectionInfo,\n): Promise<NftCollectionAnalysis> => {\n const result = Object.fromEntries(\n await Promise.all(\n Object.entries(scoringCriteria).map(async ([key, { score, weight }]) => {\n const rawScore = await score(nft)\n const weighted = rawScore.map((v) => v * weight) as Score\n return [key, weighted] as const\n }),\n ),\n ) as NftCollectionAnalysis\n return result\n}\n","import { NftSchema } from '@xyo-network/crypto-nft-payload-plugin'\nimport { PayloadSetSchema } from '@xyo-network/payload-model'\nimport { createPayloadSetDivinerPlugin } from '@xyo-network/payloadset-plugin'\n\nimport { NftCollectionScoreDiviner } from './Diviner'\n\nexport const NftCollectionScoreDivinerPlugin = () =>\n createPayloadSetDivinerPlugin<NftCollectionScoreDiviner>(\n { required: { [NftSchema]: 1 }, schema: PayloadSetSchema },\n {\n diviner: async (params) => {\n const result = await NftCollectionScoreDiviner.create(params)\n return result\n },\n },\n )\n"],"mappings":"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","names":["normalize","maxScore","normalize","defaultMu","defaultSigma","maxProbabilityDensity","maxScore"]}
|
package/package.json
CHANGED
|
@@ -10,22 +10,22 @@
|
|
|
10
10
|
"url": "https://github.com/XYOracleNetwork/plugins/issues"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@xyo-network/crypto-nft-collection-payload-plugin": "~2.
|
|
14
|
-
"@xyo-network/crypto-nft-payload-plugin": "~2.
|
|
15
|
-
"@xyo-network/crypto-nft-score-model": "~2.
|
|
16
|
-
"@xyo-network/diviner-abstract": "^2.
|
|
17
|
-
"@xyo-network/diviner-model": "^2.
|
|
18
|
-
"@xyo-network/
|
|
19
|
-
"@xyo-network/
|
|
20
|
-
"@xyo-network/payload-model": "^2.
|
|
21
|
-
"@xyo-network/payloadset-plugin": "^2.
|
|
13
|
+
"@xyo-network/crypto-nft-collection-payload-plugin": "~2.87.0-rc.1",
|
|
14
|
+
"@xyo-network/crypto-nft-payload-plugin": "~2.87.0-rc.1",
|
|
15
|
+
"@xyo-network/crypto-nft-score-model": "~2.87.0-rc.1",
|
|
16
|
+
"@xyo-network/diviner-abstract": "^2.89.0-rc.2",
|
|
17
|
+
"@xyo-network/diviner-model": "^2.89.0-rc.2",
|
|
18
|
+
"@xyo-network/module-model": "^2.89.0-rc.2",
|
|
19
|
+
"@xyo-network/payload-builder": "^2.89.0-rc.2",
|
|
20
|
+
"@xyo-network/payload-model": "^2.89.0-rc.2",
|
|
21
|
+
"@xyo-network/payloadset-plugin": "^2.89.0-rc.2"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@xylabs/jest-helpers": "^2.13.
|
|
24
|
+
"@xylabs/jest-helpers": "^2.13.29",
|
|
25
25
|
"@xylabs/ts-scripts-yarn3": "^3.2.42",
|
|
26
26
|
"@xylabs/tsconfig": "^3.2.42",
|
|
27
|
-
"@xyo-network/account": "^2.
|
|
28
|
-
"@xyo-network/payload-wrapper": "^2.
|
|
27
|
+
"@xyo-network/account": "^2.89.0-rc.2",
|
|
28
|
+
"@xyo-network/payload-wrapper": "^2.89.0-rc.2",
|
|
29
29
|
"ethers": "^6.10.0",
|
|
30
30
|
"jest": "^29.7.0",
|
|
31
31
|
"typescript": "^5.3.3"
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"url": "https://github.com/XYOracleNetwork/plugins.git"
|
|
70
70
|
},
|
|
71
71
|
"sideEffects": false,
|
|
72
|
-
"version": "2.
|
|
73
|
-
"type": "module"
|
|
72
|
+
"version": "2.87.0-rc.1",
|
|
73
|
+
"type": "module",
|
|
74
|
+
"stableVersion": "2.86.5"
|
|
74
75
|
}
|
package/src/Diviner.ts
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
} from '@xyo-network/crypto-nft-collection-payload-plugin'
|
|
10
10
|
import { AbstractDiviner } from '@xyo-network/diviner-abstract'
|
|
11
11
|
import { DivinerParams } from '@xyo-network/diviner-model'
|
|
12
|
-
import { PayloadHasher } from '@xyo-network/hash'
|
|
13
12
|
import { AnyConfigSchema } from '@xyo-network/module-model'
|
|
13
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
14
14
|
import { Payload } from '@xyo-network/payload-model'
|
|
15
15
|
|
|
16
16
|
import { analyzeNftCollection, NftCollectionAnalysis } from './lib'
|
|
@@ -36,7 +36,7 @@ export class NftCollectionScoreDiviner<
|
|
|
36
36
|
// Get score
|
|
37
37
|
toNftCollectionScore(nftCollectionInfo, await analyzeNftCollection(nftCollectionInfo)),
|
|
38
38
|
// Hash sources
|
|
39
|
-
|
|
39
|
+
PayloadBuilder.dataHash(nftCollectionInfo),
|
|
40
40
|
])
|
|
41
41
|
return { ...score, schema: NftCollectionScoreSchema, sources: [sourceHash] } as NftCollectionScore
|
|
42
42
|
}),
|