@xyo-network/crypto-nft-diviner-score-plugin 4.1.1 β 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.ts +4 -87
- package/dist/neutral/index.d.ts +4 -87
- package/dist/node/index.d.ts +4 -87
- package/package.json +17 -14
- package/src/lib/rating/criteria/scoring/metadata/attributes/spec/evaluateAttributes.spec.ts +81 -0
- package/src/lib/rating/criteria/scoring/metadata/lib/spec/urlHelpers.spec.ts +102 -0
- package/src/lib/rating/criteria/scoring/metadata/spec/animationUrl.spec.ts +43 -0
- package/src/lib/rating/criteria/scoring/metadata/spec/backgroundColor.spec.ts +36 -0
- package/src/lib/rating/criteria/scoring/metadata/spec/description.spec.ts +29 -0
- package/src/lib/rating/criteria/scoring/metadata/spec/externalUrl.spec.ts +44 -0
- package/src/lib/rating/criteria/scoring/metadata/spec/image.spec.ts +55 -0
- package/src/lib/rating/criteria/scoring/metadata/spec/imageData.spec.ts +37 -0
- package/src/lib/rating/criteria/scoring/metadata/spec/name.spec.ts +28 -0
- package/src/lib/rating/criteria/scoring/metadata/spec/youtubeUrl.spec.ts +44 -0
- package/src/lib/rating/spec/evaluateNft.spec.ts +140 -0
- package/src/spec/Diviner.spec.ts +41 -0
- package/src/spec/Plugin.spec.ts +17 -0
- package/src/spec/testData.json +20109 -0
- package/typedoc.json +0 -5
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe,
|
|
5
|
+
it,
|
|
6
|
+
} from 'vitest'
|
|
7
|
+
|
|
8
|
+
import { scoreImage } from '../image.ts'
|
|
9
|
+
import {
|
|
10
|
+
expectLoweredScore, expectMaxPossibleScore, expectMiniumScore,
|
|
11
|
+
} from './testHelpers.ts'
|
|
12
|
+
|
|
13
|
+
const web3Urls = ['ipfs://QmaXTuWjEbDuvcA3dHypqHfxMzDLq78Zj5kBLN4JdMEYDB/3.mp4', 'ipfs://QmTxDYfccVVWoucpbvFECxtysRkMBADDL8j4zVYSRM7Kp3/']
|
|
14
|
+
const secureWeb2Urls = [
|
|
15
|
+
'https://media.niftygateway.com/video/upload/v1659986036/Julian/KennyScharfWestinghouse/WESTINGHOUSE_20-26X44.5X4.5B_x2mz3r.mp4',
|
|
16
|
+
]
|
|
17
|
+
const insecureWeb2Urls = [
|
|
18
|
+
|
|
19
|
+
'http://media.niftygateway.com/video/upload/v1659986036/Julian/KennyScharfWestinghouse/WESTINGHOUSE_20-26X44.5X4.5B_x2mz3r.mp4',
|
|
20
|
+
]
|
|
21
|
+
const invalidUrls = ['', 'not a url', {}]
|
|
22
|
+
const missingUrls = [undefined, null]
|
|
23
|
+
|
|
24
|
+
describe('scoreImage', () => {
|
|
25
|
+
describe('with web3 url', () => {
|
|
26
|
+
it.each(web3Urls)('return max possible score', (value) => {
|
|
27
|
+
const score = scoreImage(value)
|
|
28
|
+
expectMaxPossibleScore(score)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
describe('with valid secure url', () => {
|
|
32
|
+
it.each(secureWeb2Urls)('returns lowered score', (value) => {
|
|
33
|
+
const score = scoreImage(value)
|
|
34
|
+
expectLoweredScore(score)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
describe('with valid insecure url', () => {
|
|
38
|
+
it.each(insecureWeb2Urls)('returns lowered score', (value) => {
|
|
39
|
+
const score = scoreImage(value)
|
|
40
|
+
expectLoweredScore(score)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
describe('with invalid url', () => {
|
|
44
|
+
it.each(invalidUrls)('returns lowered score', (value) => {
|
|
45
|
+
const score = scoreImage(value)
|
|
46
|
+
expectMiniumScore(score)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
describe('with no url', () => {
|
|
50
|
+
it.each(missingUrls)('returns no score', (value) => {
|
|
51
|
+
const score = scoreImage(value)
|
|
52
|
+
expectMiniumScore(score)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe,
|
|
5
|
+
it,
|
|
6
|
+
} from 'vitest'
|
|
7
|
+
|
|
8
|
+
import { scoreImageData } from '../imageData.ts'
|
|
9
|
+
import { expectMaxPossibleScore, expectMiniumScore } from './testHelpers.ts'
|
|
10
|
+
|
|
11
|
+
const valid = [
|
|
12
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
13
|
+
"<svg xmlns='http://www.w3.org/2000/svg' version='1.1' width='2000' height='2000'><style>.m1 #c{fill: #fff;}.m1 #r{fill: #000;}.m2 #c{fill: #fc3;}.m2 #r{fill: #000;}.m3 #c{fill: #fff;}.m3 #r{fill: #33f;}.m4 #c{fill: #fff;}.m4 #r{fill: #f33;}.a #c{fill: #000 !important;}.a #r{fill: #fff !important;}</style><g class='m1 c3'><rect id='r' width='2000' height='2000'/><circle id='c' cx='1000' cy='1000' r='146.2449'/></g></svg>",
|
|
14
|
+
]
|
|
15
|
+
const invalid = ['<svg>\n\t\t<path\td=" class="" />\n</svg>', '<svg></svg', '<svg></', '<html></html>', '', 'not an SVG', {}]
|
|
16
|
+
const missing = [undefined, null]
|
|
17
|
+
|
|
18
|
+
describe('scoreImageData', () => {
|
|
19
|
+
describe('with valid image data', () => {
|
|
20
|
+
it.each(valid)('return max possible score', (value) => {
|
|
21
|
+
const score = scoreImageData(value)
|
|
22
|
+
expectMaxPossibleScore(score)
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
describe('with invalid image data', () => {
|
|
26
|
+
it.each(invalid)('returns minimum score', (value) => {
|
|
27
|
+
const score = scoreImageData(value)
|
|
28
|
+
expectMiniumScore(score)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
describe('with no image data', () => {
|
|
32
|
+
it.each(missing)('returns minimum score', (value) => {
|
|
33
|
+
const score = scoreImageData(value)
|
|
34
|
+
expectMiniumScore(score)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe,
|
|
5
|
+
it,
|
|
6
|
+
} from 'vitest'
|
|
7
|
+
|
|
8
|
+
import { scoreName } from '../name.ts'
|
|
9
|
+
import { expectMaxPossibleScore, expectMiniumScore } from './testHelpers.ts'
|
|
10
|
+
|
|
11
|
+
const valid = ['Foo Friends #3042', 'π₯ Fire & Such']
|
|
12
|
+
const invalid = [{}]
|
|
13
|
+
const missing = ['', undefined, null]
|
|
14
|
+
|
|
15
|
+
describe('scoreName', () => {
|
|
16
|
+
describe('with valid name', () => {
|
|
17
|
+
it.each(valid)('returns max possible score', (value) => {
|
|
18
|
+
const score = scoreName(value)
|
|
19
|
+
expectMaxPossibleScore(score)
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
describe('with missing or invalid name', () => {
|
|
23
|
+
it.each([...missing, ...invalid])('returns minimum score', (value) => {
|
|
24
|
+
const score = scoreName(value)
|
|
25
|
+
expectMiniumScore(score)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe,
|
|
5
|
+
it,
|
|
6
|
+
} from 'vitest'
|
|
7
|
+
|
|
8
|
+
import { scoreYoutubeUrl } from '../youtubeUrl.ts'
|
|
9
|
+
import {
|
|
10
|
+
expectLoweredScore, expectMaxPossibleScore, expectMiniumScore, expectNoScore,
|
|
11
|
+
} from './testHelpers.ts'
|
|
12
|
+
|
|
13
|
+
const secure = ['https://lostpoets.xyz/']
|
|
14
|
+
|
|
15
|
+
const insecure = ['http://lvcidia.xyz/']
|
|
16
|
+
const invalid = ['', 'not a url', {}]
|
|
17
|
+
const missing = [undefined, null]
|
|
18
|
+
|
|
19
|
+
describe('scoreYoutubeUrl', () => {
|
|
20
|
+
describe('with secure url', () => {
|
|
21
|
+
it.each(secure)('returns max possible score', (value) => {
|
|
22
|
+
const score = scoreYoutubeUrl(value)
|
|
23
|
+
expectMaxPossibleScore(score)
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
describe('with valid url', () => {
|
|
27
|
+
it.each(insecure)('returns lowered score', (value) => {
|
|
28
|
+
const score = scoreYoutubeUrl(value)
|
|
29
|
+
expectLoweredScore(score)
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
describe('with invalid url', () => {
|
|
33
|
+
it.each(invalid)('returns minium score', (value) => {
|
|
34
|
+
const score = scoreYoutubeUrl(value)
|
|
35
|
+
expectMiniumScore(score)
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
describe('with no url', () => {
|
|
39
|
+
it.each(missing)('returns no score', (value) => {
|
|
40
|
+
const score = scoreYoutubeUrl(value)
|
|
41
|
+
expectNoScore(score)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/* eslint-disable @stylistic/max-len */
|
|
2
|
+
import '@xylabs/vitest-extended'
|
|
3
|
+
|
|
4
|
+
import type { NftInfoFields } from '@xyo-network/crypto-nft-payload-plugin'
|
|
5
|
+
import {
|
|
6
|
+
describe, expect, it,
|
|
7
|
+
} from 'vitest'
|
|
8
|
+
|
|
9
|
+
import { analyzeNft } from '../analyzeNft.ts'
|
|
10
|
+
|
|
11
|
+
const nfts: NftInfoFields[] = [
|
|
12
|
+
{
|
|
13
|
+
address: '0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85',
|
|
14
|
+
chainId: 1,
|
|
15
|
+
metadata: {
|
|
16
|
+
attributes: [
|
|
17
|
+
{
|
|
18
|
+
display_type: 'date',
|
|
19
|
+
trait_type: 'Created Date',
|
|
20
|
+
value: null,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
display_type: 'number',
|
|
24
|
+
trait_type: 'Length',
|
|
25
|
+
value: 8,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
display_type: 'date',
|
|
29
|
+
trait_type: 'Registration Date',
|
|
30
|
+
value: 1_640_668_437_000,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
display_type: 'date',
|
|
34
|
+
trait_type: 'Expiration Date',
|
|
35
|
+
value: 1_956_237_957_000,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
background_image: 'https://metadata.ens.domains/mainnet/avatar/π©happens.eth',
|
|
39
|
+
description: 'π©happens.eth, an ENS name.',
|
|
40
|
+
image_url:
|
|
41
|
+
'https://metadata.ens.domains/mainnet/0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85/0x727cff87562fe62e86e8147c31e567da501307ce220e074ec61eb51b6afc8e7a/image',
|
|
42
|
+
is_normalized: true,
|
|
43
|
+
name: 'π©happens.eth',
|
|
44
|
+
name_length: 8,
|
|
45
|
+
url: 'https://app.ens.domains/name/π©happens.eth',
|
|
46
|
+
version: 0,
|
|
47
|
+
},
|
|
48
|
+
supply: '1',
|
|
49
|
+
tokenId: '51784517368512681240378652206490798036726342785538345603107759690694088494714',
|
|
50
|
+
type: 'ERC721',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
address: '0x8eaaabe11896bd09fb852d3a5248004ec44bc793',
|
|
54
|
+
chainId: 1,
|
|
55
|
+
metadata: {
|
|
56
|
+
animation_url:
|
|
57
|
+
'https://refractions.azureedge.net/refractions/metadata/refractions/0000000000000000000000000000000000000000000000000000000000000007/prism.mp4',
|
|
58
|
+
attributes: [
|
|
59
|
+
{
|
|
60
|
+
display_type: 'date',
|
|
61
|
+
trait_type: 'Publish Date',
|
|
62
|
+
value: 1_643_274_000,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
trait_type: 'Edition',
|
|
66
|
+
value: 'Bridge',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
trait_type: 'Inventory',
|
|
70
|
+
value: 'Artifacts',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
description:
|
|
74
|
+
'ππππ [Prism](https://refractions.xyz/prism) The Lighthouse \r\n Airdropped to all [BAYC](https://opensea.io/collection/boredapeyachtclub) and [CryptoFish](https://cryptofish.io/) members by [Captain Murray #9671](https://opensea.io/muratsayginer). \r\n \r\n **Introducing [refractions.xyz](https://refractions.xyz/)** / _This is a new yet familiar territory. A dimension where metaphors rule physics and fiction builds realityβ¦_ \r\n \r\n [#apefollowape](https://twitter.com/muratsayginer)',
|
|
75
|
+
external_url: 'https://refractions.xyz/',
|
|
76
|
+
image:
|
|
77
|
+
'https://refractions.azureedge.net/refractions/metadata/refractions/0000000000000000000000000000000000000000000000000000000000000007/prism_placeholder.gif',
|
|
78
|
+
name: 'Prism',
|
|
79
|
+
},
|
|
80
|
+
supply: '1',
|
|
81
|
+
tokenId: '7',
|
|
82
|
+
type: 'ERC1155',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
address: '0x0369edc7646948e2f36f9f85baf297bb99843054',
|
|
86
|
+
chainId: 1,
|
|
87
|
+
metadata: {
|
|
88
|
+
attributes: [
|
|
89
|
+
{
|
|
90
|
+
trait_type: 'Artist',
|
|
91
|
+
value: 'MrSnowy10',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
trait_type: 'Item Eaten',
|
|
95
|
+
value: 'Radioactive Waste',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
trait_type: 'Type',
|
|
99
|
+
value: 'Avatar',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
trait_type: 'Species',
|
|
103
|
+
value: 'Pinco',
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
created_by: 'MrSnowy10',
|
|
107
|
+
description: 'Alien Pinco\n\nPinco "Alien"',
|
|
108
|
+
external_url: 'https://www.thepincovillage.com/',
|
|
109
|
+
image: 'https://arweave.net/0-R-VFX6VxkK4Cr70gnq50A2Rd6qXge0sHAQqGPqI4E',
|
|
110
|
+
image_details: {
|
|
111
|
+
bytes: 2_459_769,
|
|
112
|
+
format: 'PNG',
|
|
113
|
+
height: 1500,
|
|
114
|
+
sha256: 'f5cc400e767fe9a5c75c32745faf472ca35863b0ff48662923d7f3c9bae13edd',
|
|
115
|
+
width: 1200,
|
|
116
|
+
},
|
|
117
|
+
image_url: 'https://arweave.net/0-R-VFX6VxkK4Cr70gnq50A2Rd6qXge0sHAQqGPqI4E',
|
|
118
|
+
name: 'Pinco "Alien"',
|
|
119
|
+
},
|
|
120
|
+
supply: '1',
|
|
121
|
+
tokenId: '27',
|
|
122
|
+
type: 'ERC1155',
|
|
123
|
+
},
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
describe('evaluateNft', () => {
|
|
127
|
+
it.each(nfts)('evaluates the NFT', async (nft) => {
|
|
128
|
+
const rating = await analyzeNft(nft)
|
|
129
|
+
expect(rating).toBeObject()
|
|
130
|
+
for (let [key, score] of Object.entries(rating)) {
|
|
131
|
+
expect(key).toBeString()
|
|
132
|
+
const [total, possible] = score
|
|
133
|
+
expect(total).toBeNumber()
|
|
134
|
+
expect(total).not.toBeNegative()
|
|
135
|
+
expect(possible).toBeNumber()
|
|
136
|
+
expect(possible).not.toBeNegative()
|
|
137
|
+
expect(total).toBeLessThanOrEqual(possible)
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import { readFile } from 'node:fs/promises'
|
|
4
|
+
import Path from 'node:path'
|
|
5
|
+
|
|
6
|
+
import type { NftInfo } from '@xyo-network/crypto-nft-payload-plugin'
|
|
7
|
+
import { isNftInfo } from '@xyo-network/crypto-nft-payload-plugin'
|
|
8
|
+
import { PayloadWrapper } from '@xyo-network/payload-wrapper'
|
|
9
|
+
import {
|
|
10
|
+
beforeAll,
|
|
11
|
+
describe, expect, test,
|
|
12
|
+
} from 'vitest'
|
|
13
|
+
|
|
14
|
+
import { isNftScore, NftScoreDiviner } from '../Diviner.ts'
|
|
15
|
+
|
|
16
|
+
describe('NftScoreDiviner', () => {
|
|
17
|
+
let data: NftInfo[]
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
const filePath = Path.join(__dirname, 'testData.json')
|
|
20
|
+
const fileContents = await readFile(filePath, 'utf8')
|
|
21
|
+
const nfts = JSON.parse(fileContents)
|
|
22
|
+
expect(nfts).toBeArray()
|
|
23
|
+
if (Array.isArray(nfts)) {
|
|
24
|
+
data = nfts.filter(isNftInfo)
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
test('divine', async () => {
|
|
28
|
+
const diviner = await NftScoreDiviner.create({ account: 'random' })
|
|
29
|
+
const scores = (await diviner.divine(data)).filter(isNftScore)
|
|
30
|
+
expect(scores).toBeArrayOfSize(data.length)
|
|
31
|
+
for (const [i, score] of scores.entries()) {
|
|
32
|
+
const wrapped = PayloadWrapper.wrap(score)
|
|
33
|
+
expect(await wrapped.getValid()).toBe(true)
|
|
34
|
+
const payload = wrapped.payload
|
|
35
|
+
expect(payload?.sources).toBeArrayOfSize(1)
|
|
36
|
+
expect(payload?.sources?.[0]).toBeString()
|
|
37
|
+
const sourceHash = await PayloadWrapper.wrap(data[i]).dataHash()
|
|
38
|
+
expect(payload?.sources?.[0]).toBe(sourceHash)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import { PayloadSetPluginResolver } from '@xyo-network/payloadset-plugin'
|
|
4
|
+
import {
|
|
5
|
+
describe, expect,
|
|
6
|
+
test,
|
|
7
|
+
} from 'vitest'
|
|
8
|
+
|
|
9
|
+
import { NftScoreDivinerPlugin } from '../Plugin.ts'
|
|
10
|
+
|
|
11
|
+
describe('NftScoreDivinerPlugin', () => {
|
|
12
|
+
test('Add to Resolver', async () => {
|
|
13
|
+
const plugin = NftScoreDivinerPlugin()
|
|
14
|
+
const resolver = await new PayloadSetPluginResolver().register(plugin)
|
|
15
|
+
expect(resolver.resolve(plugin.set)).toBeDefined()
|
|
16
|
+
})
|
|
17
|
+
})
|