Skip to content

Commit 309e286

Browse files
committed
validation scripts
1 parent 4d3b854 commit 309e286

6 files changed

+270
-6
lines changed

scripts/validate-metadata.ts

-6
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TokenInfo, TokenList } from '@src/types';
2+
import fs from 'fs';
3+
import path from 'path';
4+
5+
const loadMainTokenList = (): TokenInfo[] => {
6+
const tokenListPath = path.resolve('temp', 'token-list.json');
7+
return (JSON.parse(fs.readFileSync(tokenListPath, 'utf-8')) as TokenList)
8+
.tokens;
9+
};
10+
11+
const checkForDuplicates = (newToken: TokenInfo): boolean => {
12+
const mainTokens = loadMainTokenList();
13+
return mainTokens.some(token => token.address === newToken.address);
14+
};
15+
16+
export { checkForDuplicates };

scripts/validators/token-validator.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Ajv from 'ajv';
2+
import ajvFormats from 'ajv-formats';
3+
4+
const ajv = new Ajv();
5+
ajvFormats(ajv);
6+
7+
const tokenInfoSchema = {
8+
type: 'object',
9+
description: 'Metadata for a single token in a token list',
10+
additionalProperties: false,
11+
properties: {
12+
address: {
13+
type: 'string',
14+
description: 'The checksummed address of the token on the bitfinity',
15+
pattern: '^0x[a-fA-F0-9]{40}$',
16+
examples: ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'],
17+
},
18+
decimals: {
19+
type: 'integer',
20+
description: 'The number of decimals for the token balance',
21+
minimum: 0,
22+
maximum: 255,
23+
examples: [18],
24+
},
25+
name: {
26+
type: 'string',
27+
description: 'The name of the token',
28+
minLength: 0,
29+
maxLengtth: 60,
30+
anyOf: [
31+
{
32+
const: '',
33+
},
34+
{
35+
pattern: '^[ \\S+]+$',
36+
},
37+
],
38+
examples: ['Sonic'],
39+
},
40+
symbol: {
41+
type: 'string',
42+
description: 'The symbol for the token',
43+
minLength: 0,
44+
maxLength: 20,
45+
anyOf: [
46+
{
47+
const: '',
48+
},
49+
{
50+
pattern: '^\\S+$',
51+
},
52+
],
53+
examples: ['SONIC'],
54+
},
55+
logoURI: {
56+
type: 'string',
57+
description:
58+
'A URI to the token logo asset; if not set, interface will attempt to find a logo based on the token address; suggest SVG or PNG of size 64x64',
59+
format: 'uri',
60+
examples: ['ipfs://QmXfzKRvjZz3u5JRgC4v5mGVbm9ahrUiB4DgzHBsnWbTMM'],
61+
},
62+
},
63+
required: ['address', 'decimals', 'name', 'symbol'],
64+
};
65+
66+
// Create the validator
67+
const validateTokenInfo = ajv.compile(tokenInfoSchema);
68+
69+
export { validateTokenInfo };
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://raw.githubusercontent.com/sonicdex/bitfinity-token-list/main/src/tokenlist.schema.json",
4+
"title": "Sonic-Bitfinity Token List",
5+
"description": "Schema for lists of tokens compatible with the Sonic App",
6+
"definitions": {
7+
"Version": {
8+
"type": "object",
9+
"description": "The version of the list, used in change detection",
10+
"examples": [
11+
{
12+
"major": 1,
13+
"minor": 0,
14+
"patch": 0
15+
}
16+
],
17+
"additionalProperties": false,
18+
"properties": {
19+
"major": {
20+
"type": "integer",
21+
"description": "The major version of the list. Must be incremented when tokens are removed from the list or token addresses are changed.",
22+
"minimum": 0,
23+
"examples": [1, 2]
24+
},
25+
"minor": {
26+
"type": "integer",
27+
"description": "The minor version of the list. Must be incremented when tokens are added to the list.",
28+
"minimum": 0,
29+
"examples": [0, 1]
30+
},
31+
"patch": {
32+
"type": "integer",
33+
"description": "The patch version of the list. Must be incremented for any changes to the list.",
34+
"minimum": 0,
35+
"examples": [0, 1]
36+
}
37+
},
38+
"required": ["major", "minor", "patch"]
39+
},
40+
"TokenInfo": {
41+
"type": "object",
42+
"description": "Metadata for a single token in a token list",
43+
"additionalProperties": false,
44+
"properties": {
45+
"address": {
46+
"type": "string",
47+
"description": "The checksummed address of the token on the bitfinity",
48+
"pattern": "^0x[a-fA-F0-9]{40}$",
49+
"examples": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"]
50+
},
51+
"decimals": {
52+
"type": "integer",
53+
"description": "The number of decimals for the token balance",
54+
"minimum": 0,
55+
"maximum": 255,
56+
"examples": [18]
57+
},
58+
"name": {
59+
"type": "string",
60+
"description": "The name of the token",
61+
"minLength": 0,
62+
"maxLength": 60,
63+
"anyOf": [
64+
{
65+
"const": ""
66+
},
67+
{
68+
"pattern": "^[ \\S+]+$"
69+
}
70+
],
71+
"examples": ["Sonic"]
72+
},
73+
"symbol": {
74+
"type": "string",
75+
"description": "The symbol for the token",
76+
"minLength": 0,
77+
"maxLength": 20,
78+
"anyOf": [
79+
{
80+
"const": ""
81+
},
82+
{
83+
"pattern": "^\\S+$"
84+
}
85+
],
86+
"examples": ["SONIC"]
87+
},
88+
"logoURI": {
89+
"type": "string",
90+
"description": "A URI to the token logo asset; if not set, interface will attempt to find a logo based on the token address; suggest SVG or PNG of size 64x64",
91+
"format": "uri",
92+
"examples": ["ipfs://QmXfzKRvjZz3u5JRgC4v5mGVbm9ahrUiB4DgzHBsnWbTMM"]
93+
}
94+
},
95+
"required": ["address", "decimals", "name", "symbol"]
96+
}
97+
},
98+
"type": "object",
99+
"properties": {
100+
"name": {
101+
"type": "string",
102+
"description": "The name of the token list",
103+
"minLength": 1,
104+
"maxLength": 30,
105+
"pattern": "^[\\w ]+$",
106+
"examples": ["My Token List"]
107+
},
108+
"timestamp": {
109+
"type": "string",
110+
"format": "date-time",
111+
"description": "The timestamp of this list version; i.e. when this immutable version of the list was created"
112+
},
113+
"version": {
114+
"$ref": "#/definitions/Version"
115+
},
116+
"tokens": {
117+
"type": "array",
118+
"description": "The list of tokens included in the list",
119+
"items": {
120+
"$ref": "#/definitions/TokenInfo"
121+
},
122+
"minItems": 1,
123+
"maxItems": 10000
124+
}
125+
},
126+
"required": ["name", "timestamp", "version", "tokens"]
127+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import fs from 'fs';
2+
import { validateTokenInfo } from './token-validator';
3+
4+
const validateMetadata = (filePath: string): boolean => {
5+
const metadata = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
6+
7+
const valid = validateTokenInfo(metadata);
8+
9+
if (!valid) {
10+
console.log(`Invalid metadata in file: ${filePath}`);
11+
validateTokenInfo.errors?.forEach(err => {
12+
console.log(`Error in property ${err.instancePath}: ${err.message}`);
13+
});
14+
return false;
15+
}
16+
17+
return true;
18+
};
19+
20+
export { validateMetadata };
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import { validateMetadata } from './validate-metadata';
4+
import { checkForDuplicates } from './check-for-duplicatets';
5+
6+
// Function to get all the changed token metadata files from the PR
7+
const getChangedFiles = (gitDiffOutput: string): string[] => {
8+
const files = gitDiffOutput
9+
.split('\n')
10+
.filter(line => line.includes('src/tokens/'));
11+
return files;
12+
};
13+
14+
// Function to validate each token from the PR
15+
const validatePRTokens = (gitDiffOutput: string): void => {
16+
const changedFiles = getChangedFiles(gitDiffOutput);
17+
18+
changedFiles.forEach(filePath => {
19+
const fullPath = path.resolve(filePath);
20+
const metadataValid = validateMetadata(fullPath);
21+
22+
if (metadataValid) {
23+
const metadata = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
24+
25+
// Check for duplicate in the main list
26+
const isDuplicate = checkForDuplicates(metadata);
27+
if (isDuplicate) {
28+
console.log(
29+
`Duplicate token found: ${metadata.symbol} (${metadata.address})`
30+
);
31+
} else {
32+
console.log(`Valid token: ${metadata.symbol} (${metadata.address})`);
33+
}
34+
}
35+
});
36+
};
37+
38+
export { validatePRTokens };

0 commit comments

Comments
 (0)