Skip to content

Commit

Permalink
feat(all): project initiation and architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
dmokel committed Feb 13, 2023
1 parent afbd4c8 commit d3f733f
Show file tree
Hide file tree
Showing 19 changed files with 231 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Rarity

An algorithm to calculate rarity of NFT(how special it is), based on Jaccard Distance.
20 changes: 20 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "gorarity",
"version": "1.0.0",
"description": "An algorithm to calculate rarity of NFT(how special it is), based on Jaccard Distance.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/NFTGo/GoRarity.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/NFTGo/GoRarity/issues"
},
"homepage": "https://github.com/NFTGo/GoRarity#readme"
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./models";
export * from "./rarity-ranker";
26 changes: 26 additions & 0 deletions src/models/collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Token } from "./token";
import { StringAttribute } from "./token-metadata";
import { TokenStandard } from "./token-standard";

export type CollectionAttribute = {
attribute: StringAttribute,
totalTokens: number,
}

export class Collection {
name: string;
tokens: Token[];

constructor(dict: { name: string, tokens: Token[] }) {
this.name = dict.name;
this.tokens = dict.tokens;
}

tokenStandards(): TokenStandard[] {
const tokenStandards = new Set<TokenStandard>();
this.tokens.forEach(item => {
tokenStandards.add(item.tokenStandard);
})
return Array.from(tokenStandards.values())
}
}
6 changes: 6 additions & 0 deletions src/models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from "./collection";
export * from "./token";
export * from "./token-identifier";
export * from "./token-metadata";
export * from "./token-rarity";
export * from "./token-standard";
24 changes: 24 additions & 0 deletions src/models/token-identifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export class EVMContractTokenIdentifier {
public contractAddress: string;
public tokenId: number;

constructor(contractAddress: string, tokenId: number) {
this.contractAddress = contractAddress;
this.tokenId = tokenId;
}

get() {
return `Contract(${this.contractAddress}) #${this.tokenId}`;
}

static fromDict(dict: { contractAddress: string, tokenId: number }) {
return new EVMContractTokenIdentifier(dict.contractAddress, dict.tokenId);
}

toDict() {
return {
"contractAddress": this.contractAddress,
"tokenId": this.tokenId,
};
}
}
34 changes: 34 additions & 0 deletions src/models/token-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { normalizeAttributeString } from "./utils/attribute-utils";

type AttributeName = string;
type AttributeValue = string;

export class StringAttribute {
public name: AttributeName;
public value: AttributeValue;

constructor(name: AttributeName, value: AttributeValue) {
this.name = normalizeAttributeString(name);
this.value = normalizeAttributeString(value);
}
}

export class TokenMetadata {
public stringAttributes: Map<AttributeName, StringAttribute> | undefined = new Map();

constructor(
stringAttributes: Map<AttributeName, StringAttribute>,
) {
if (!stringAttributes) { throw new Error('null stringAttributes') }
this.stringAttributes = TokenMetadata.normalizeAttributes<StringAttribute>(stringAttributes);
}

private static normalizeAttributes<T>(attributes: Map<AttributeName, T>) {
const normalizedAttributes = new Map<AttributeName, T>();
attributes.forEach((attribute, attributeName) => {
const normalizedAttributeName = normalizeAttributeString(attributeName);
normalizedAttributes.set(normalizedAttributeName, attribute)
});
return normalizedAttributes
}
}
4 changes: 4 additions & 0 deletions src/models/token-ranking-features.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type TokenRankingFeatures = {
// count of unique attributes in the token
uniqueAttributeCount: number,
}
9 changes: 9 additions & 0 deletions src/models/token-rarity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Token } from "./token"
import { TokenRankingFeatures } from "./token-ranking-features"

export type TokenRarity = {
score: number,
tokenFeatures: TokenRankingFeatures,
token: Token,
rank: number,
}
4 changes: 4 additions & 0 deletions src/models/token-standard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum TokenStandard {
ERC721 = "erc721",
ERC1155 = "erc1155",
}
15 changes: 15 additions & 0 deletions src/models/token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { EVMContractTokenIdentifier } from "./token-identifier";
import { TokenMetadata } from "./token-metadata";
import { TokenStandard } from "./token-standard";

export class Token {
public tokenIdentifier: EVMContractTokenIdentifier;
public tokenStandard: TokenStandard;
public metadata: TokenMetadata;

constructor(tokenIdentifier: EVMContractTokenIdentifier, tokenStandard: TokenStandard, metadata: TokenMetadata) {
this.tokenIdentifier = tokenIdentifier;
this.tokenStandard = tokenStandard;
this.metadata = metadata;
}
}
3 changes: 3 additions & 0 deletions src/models/utils/attribute-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function normalizeAttributeString(value: string): string {
return value.toLowerCase().trim();
}
26 changes: 26 additions & 0 deletions src/rarity-ranker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Collection, TokenRarity } from "./models";
import { Scorer } from "./scoring";

export class RarityRanker {
private static defaultScorer = new Scorer();

constructor() { }

static rankCollection(collection: Collection, score: Scorer = RarityRanker.defaultScorer): TokenRarity[] {
if (!collection || !collection.tokens || collection.tokens.length === 0) return [];

const tokens = collection.tokens;
const scores = score.scoreTokens(collection, tokens);
if (scores.length !== tokens.length) { throw new Error(`dimension of scores doesn't match dimension of tokens`) }

const tokenRarities: TokenRarity[] = []

// TODO

return RarityRanker.setRarityRanks(tokenRarities);
}

static setRarityRanks(tokenRarities: TokenRarity[]): TokenRarity[] {
return [];
}
}
1 change: 1 addition & 0 deletions src/scoring/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./jaccard-distance-scoring-handler";
9 changes: 9 additions & 0 deletions src/scoring/handlers/jaccard-distance-scoring-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Collection, Token } from "../../models";
import { IScoringHandler } from "../scoring-handler";

export class JaccardDistanceScoringHandler implements IScoringHandler {

scoreTokens(collection: Collection, tokens: Token[]): number[] {
return [];
}
}
2 changes: 2 additions & 0 deletions src/scoring/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./handlers";
export * from "./scorer";
26 changes: 26 additions & 0 deletions src/scoring/scorer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Collection, Token, TokenStandard } from "../models";
import { JaccardDistanceScoringHandler } from "./handlers";
import { IScoringHandler } from "./scoring-handler";

export class Scorer {

private handler: IScoringHandler;

constructor() {
this.handler = new JaccardDistanceScoringHandler();
}

validateCollection(collection: Collection) {
const allowedStandards = [TokenStandard.ERC721, TokenStandard.ERC1155];

// TODO validate collection with the rules
//
collection.tokenStandards().every(val => allowedStandards.includes(val))
}

scoreTokens(collection: Collection, tokens: Token[]): number[] {

this.validateCollection(collection);
return this.handler.scoreTokens(collection, tokens);
}
}
5 changes: 5 additions & 0 deletions src/scoring/scoring-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Collection, Token } from "../models";

export interface IScoringHandler {
scoreTokens(collection: Collection, tokens: Token[]): number[],
}
14 changes: 14 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es2019",
"module": "commonjs",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"composite": true
}
}

0 comments on commit d3f733f

Please sign in to comment.