diff --git a/package-lock.json b/package-lock.json index 97ad226..e52fc3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,8 @@ "license": "MIT", "dependencies": { "ajv": "^8.17.1", - "ajv-formats": "^3.0.1" + "ajv-formats": "^3.0.1", + "image-size": "^1.1.1" } }, "node_modules/ajv": { @@ -54,11 +55,38 @@ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" }, + "node_modules/image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dependencies": { + "inherits": "~2.0.3" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", diff --git a/package.json b/package.json index f624dd7..f22b371 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Vara Network token list", "main": "scripts/verifier.js", "scripts": { - "start": "node scripts/verifier.js" + "check": "node scripts/verifier.js" }, "keywords": [ "vara network", @@ -15,6 +15,7 @@ "license": "MIT", "dependencies": { "ajv": "^8.17.1", - "ajv-formats": "^3.0.1" + "ajv-formats": "^3.0.1", + "image-size": "^1.1.1" } } diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..fb4f9c6 --- /dev/null +++ b/readme.md @@ -0,0 +1,146 @@ +# Vara Network token list + +## Merge Request Requirements + +### Overview + +This document outlines the requirements for submitting a merge request related to the `Vara Network token list` project. +Ensure that all changes adhere to the specified guidelines to maintain consistency and integrity in the token data and +related assets. + +### File Naming and Uniqueness + +- **File Names**: All files must have unique names within their respective directories. File names are case-insensitive. + +### JSON File Requirements (`data` Directory) + +1. **File Naming**: + - The JSON file name must match the `contractAddress` property of the token and be in lowercase. + +2. **File Extension**: + - The file extension must be `.json`. + +3. **Schema Validation**: + - The JSON file must conform to the schema defined in `scripts/token-schema.json`. Below is the required schema: + + ```json + { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Token Schema", + "description": "Schema for validating token information in a blockchain context.", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the token." + }, + "ticker": { + "type": "string", + "description": "The short name of the token." + }, + "title": { + "type": "string", + "description": "The full name of the network or token." + }, + "iconFileName": { + "type": "string", + "description": "The icon file name for the token." + }, + "infoURL": { + "type": "string", + "format": "uri", + "description": "URL for additional information about the token." + }, + "contractAddress": { + "type": "string", + "description": "The smart contract address of the token." + }, + "type": { + "type": "string", + "enum": [ + "fungible", + "nft" + ], + "description": "The type of token (fungible or NFT)." + }, + "decimals": { + "type": "integer", + "description": "The number of decimal places." + }, + "description": { + "type": "string", + "description": "A description of the token.", + "default": "" + }, + "website": { + "type": "string", + "format": "uri", + "description": "Link to the project's website.", + "default": "" + }, + "whitepaper": { + "type": "string", + "format": "uri", + "description": "Link to the project's whitepaper.", + "default": "" + }, + "initialSupply": { + "type": "number", + "description": "The initial supply of tokens.", + "default": 0 + }, + "totalSupply": { + "type": "number", + "description": "The total supply of tokens.", + "default": 0 + }, + "maxSupply": { + "type": "number", + "description": "The maximum supply of tokens.", + "default": 0 + } + }, + "required": [ + "name", + "ticker", + "title", + "iconFileName", + "infoURL", + "contractAddress", + "type", + "decimals" + ], + "additionalProperties": false + } + ``` + +### PNG File Requirements (`icons` Directory) + +1. **File Naming**: + - The PNG file name must match the `contractAddress` property of the token and be in lowercase. + +2. **File Extension**: + - The file extension must be `.png`. + +3. **File Usage**: + - Each PNG file should be used in exactly one JSON file. However, it must be used in at least one JSON file. + +4. **File Dimensions**: + - The PNG file must be square and have dimensions of at least 128x128 pixels (but no more than 1024x1024 pixels). + +5. **File Format**: + - The file must be in PNG format. + +### Creating a Merge Request + +### Creating a Merge Request + +1. **Verify Functionality**: + - Before creating a merge request, ensure that everything works correctly by running the following command: + ```sh + npm run check + ``` + - Make sure there are no errors and all functionalities are working as expected. + +2. **Submit Merge Request**: + - Once you have verified that everything is working correctly, you can proceed to create a merge request. \ No newline at end of file diff --git a/scripts/verifier.js b/scripts/verifier.js index 4604e30..c90b102 100644 --- a/scripts/verifier.js +++ b/scripts/verifier.js @@ -2,15 +2,29 @@ const Ajv = require('ajv'); const addFormats = require('ajv-formats'); const fs = require('fs'); const path = require('path'); +const sizeOf = require('image-size'); const schema = require('./token-schema.json'); -function validateJsonFile(filePath) { +const ajv = new Ajv(); +addFormats(ajv); +const validate = ajv.compile(schema); + +const dataDirectoryPath = path.resolve(process.cwd(), 'data'); +const iconsDirectoryPath = path.resolve(dataDirectoryPath, 'icons'); + +function validateJsonFile(filePath, iconUsage) { try { const data = JSON.parse(fs.readFileSync(filePath, 'utf8')); const valid = validate(data); if (valid) { console.log(`File ${path.basename(filePath)} is valid.`); + const iconFileName = data.iconFileName.toLowerCase(); + if (iconUsage[iconFileName]) { + iconUsage[iconFileName]++; + } else { + iconUsage[iconFileName] = 1; + } } else { console.log(`File ${path.basename(filePath)} is invalid:`, validate.errors); } @@ -19,27 +33,56 @@ function validateJsonFile(filePath) { } } -function validateAllJsonFilesInDirectory(directoryPath) { +function validatePngFile(filePath, iconUsage) { + if (path.extname(filePath) !== '.png') { + throw new Error(`File ${path.basename(filePath)} is invalid: Image must be in PNG format.`); + } + + const dimensions = sizeOf(filePath); + const fileName = path.basename(filePath).toLowerCase(); + const MIN_SIZE = 128; + const MAX_SIZE = 1024; + if (dimensions.width !== dimensions.height || dimensions.width < MIN_SIZE || dimensions.width > MAX_SIZE) { + throw new Error(`File ${fileName} is invalid: Image must be square and at least ${MIN_SIZE}x${MIN_SIZE} pixels, Your image is ${dimensions.width}x${dimensions.height} pixels.`); + } else if (!iconUsage[fileName] || iconUsage[fileName] > 1) { + throw new Error(`File ${fileName} is invalid: Icon must be used in exactly one JSON file.`); + } else { + console.log(`File ${fileName} is valid.`); + } +} + +function validateAllFilesInDirectory(directoryPath, extension, validationFunction, iconUsage) { fs.readdir(directoryPath, (err, files) => { if (err) { console.error('Error reading directory:', err); return; } + const fileNames = new Set(); files.forEach(file => { const filePath = path.join(directoryPath, file); - if (path.extname(file) === '.json') { - validateJsonFile(filePath); + const fileName = path.basename(file, extension).toLowerCase(); + + if (fileNames.has(fileName)) { + console.log(`File ${file} is invalid: Duplicate file name (case insensitive).`); + } else { + fileNames.add(fileName); + if (path.extname(file) === extension) { + validationFunction(filePath, iconUsage); + } } }); }); } -const ajv = new Ajv(); -addFormats(ajv); +function validateJsonFiles(iconUsage) { + validateAllFilesInDirectory(dataDirectoryPath, '.json', validateJsonFile, iconUsage); +} -const validate = ajv.compile(schema); +function validatePngFiles(iconUsage) { + validateAllFilesInDirectory(iconsDirectoryPath, '.png', validatePngFile, iconUsage); +} -const dataDirectoryPath = path.resolve(process.cwd(), 'data'); - -validateAllJsonFilesInDirectory(dataDirectoryPath); +const iconUsage = {}; +validateJsonFiles(iconUsage); +validatePngFiles(iconUsage); \ No newline at end of file