diff --git a/.npmignore b/.npmignore index e69de29b..cbf98b9a 100644 --- a/.npmignore +++ b/.npmignore @@ -0,0 +1 @@ +src/commands \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..5acb4c91 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +*.hbs \ No newline at end of file diff --git a/README.md b/README.md index db0edb54..09e48025 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ If you want your protocol to work well across all of web3, contribute to **EVM C ### Generating a new contextualization -Run this command `npm run create:contextualizer -- --name [name of protocol]` +Run this command `npm run create:contextualizer [name of protocol]` This will generate a new file called `protocol/[name of protocol].ts` and a test file called `protocol/[name of protocol].spec.ts`. diff --git a/package-lock.json b/package-lock.json index a11619c9..194702ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,10 @@ "version": "0.0.8", "license": "UNLICENSED", "dependencies": { - "ethers": "^5.6.4" + "commander": "^11.1.0", + "ethers": "^5.6.4", + "handlebars": "^4.7.8", + "path": "^0.12.7" }, "devDependencies": { "@types/jest": "^29.5.8", @@ -2501,6 +2504,14 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "engines": { + "node": ">=16" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3492,6 +3503,26 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4737,6 +4768,14 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -4749,6 +4788,11 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4922,6 +4966,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5056,6 +5109,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -5299,7 +5360,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5628,6 +5688,18 @@ "node": ">=14.17" } }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -5682,6 +5754,19 @@ "punycode": "^2.1.0" } }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, "node_modules/v8-to-istanbul": { "version": "9.1.3", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", @@ -5720,6 +5805,11 @@ "node": ">= 8" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 6a832cf3..cf59e76f 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "prepublishOnly": "npm run build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "grab-transaction": "node ./scripts/grab-transaction.js" + "grab-transaction": "node ./scripts/grab-transaction.js", + "create:contextualizer": "npm run build && node ./dist/commands/main.js create-contextualizer" }, "repository": { "type": "git", @@ -54,6 +55,9 @@ "testEnvironment": "node" }, "dependencies": { - "ethers": "^5.6.4" + "commander": "^11.1.0", + "ethers": "^5.6.4", + "handlebars": "^4.7.8", + "path": "^0.12.7" } } diff --git a/src/commands/createContextualizer.ts b/src/commands/createContextualizer.ts new file mode 100644 index 00000000..c8787953 --- /dev/null +++ b/src/commands/createContextualizer.ts @@ -0,0 +1,78 @@ +import * as fs from 'fs'; +import * as Handlebars from 'handlebars'; +import * as path from 'path'; +import { program } from './main'; + +export function registerCreateContextualizerCommand() { + program + .command('create-contextualizer') + .description('Create a new contextualizer') + .argument('', 'name of contextualizer') + .action((name, options) => { + const srcDir = path.join(__dirname, '..', '..', 'src'); + const contextualizerTemplateFilePath = path.join( + srcDir, + 'template', + 'contextualizer.template.hbs', + ); + const contextualizerSpecTemplateFilePath = path.join( + srcDir, + 'template', + 'contextualizer.spec.template.hbs', + ); + const newContextualizerFilePath = path.join( + srcDir, + 'protocol', + `${name}.ts`, + ); + const newContextualizerSpecFilePath = path.join( + srcDir, + 'protocol', + `${name}.spec.ts`, + ); + + try { + console.log(`Creating a new contextualizer: ${name}`); + + const contextualizerSource = fs.readFileSync( + contextualizerTemplateFilePath, + 'utf8', + ); + const contextualizerSpecSource = fs.readFileSync( + contextualizerSpecTemplateFilePath, + 'utf8', + ); + const contextualizerTemplate = Handlebars.compile(contextualizerSource); + const contextualizerSpecTemplate = Handlebars.compile( + contextualizerSpecSource, + ); + // Data to replace variables + const data = { + lowercaseName: name, + camelCaseName: capitalize(name), + }; + // Replace with actual contextualizer name + const contextualizerContent = contextualizerTemplate(data); + const contextualizerSpecContent = contextualizerSpecTemplate(data); + // Write the modified contents to the new contextualizer file + fs.writeFileSync(newContextualizerFilePath, contextualizerContent); + fs.writeFileSync( + newContextualizerSpecFilePath, + contextualizerSpecContent, + ); + + console.log( + `Successfully created a new contextualizer: ${newContextualizerFilePath}`, + ); + + process.exit(0); // Successful exit + } catch (error) { + console.error('Error during file operation:', error); + process.exit(1); // Exit with error + } + }); +} + +function capitalize(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1); +} diff --git a/src/commands/main.ts b/src/commands/main.ts new file mode 100644 index 00000000..f1d4db29 --- /dev/null +++ b/src/commands/main.ts @@ -0,0 +1,12 @@ +const { Command } = require('commander'); +import { registerCreateContextualizerCommand } from './createContextualizer'; +export const program = new Command(); + +program + .name('Onceupon command') + .description('CLI to some contextualizer utils') + .version('0.1.0'); + +registerCreateContextualizerCommand(); + +program.parse(process.argv); diff --git a/src/template/contextualizer.spec.template.hbs b/src/template/contextualizer.spec.template.hbs new file mode 100644 index 00000000..793867b3 --- /dev/null +++ b/src/template/contextualizer.spec.template.hbs @@ -0,0 +1,10 @@ +import { Transaction } from '../types'; +import { detect{{camelCaseName}}, generate{{camelCaseName}}Context } from './{{lowercaseName}}'; + +describe('{{camelCaseName}}', () => { + it('Should detect {{camelCaseName}} transaction', () => { + }); + + it('Should generate {{camelCaseName}} context', () => { + }); +}); diff --git a/src/template/contextualizer.template.hbs b/src/template/contextualizer.template.hbs new file mode 100644 index 00000000..5c669def --- /dev/null +++ b/src/template/contextualizer.template.hbs @@ -0,0 +1,58 @@ +import { Transaction } from '../types'; + +export const {{lowercaseName}}Contextualizer = (transaction: Transaction): Transaction => { + const is{{camelCaseName}} = detect{{camelCaseName}}(transaction); + if (!is{{camelCaseName}}) return transaction; + + return generate{{camelCaseName}}Context(transaction); +}; + +export const detect{{camelCaseName}} = (transaction: Transaction): boolean => { + /** implement your detection logic */ + if (!transaction.value) { + return false; + } + + return true; +}; + +// Contextualize for mined txs +export const generate{{camelCaseName}}Context = (transaction: Transaction): Transaction => { + /** implement your context generation logic */ + transaction.context = { + variables: { + subject: { + type: '', + value: '', + }, + asset1: { + type: '', + value: '', + }, + asset2: { + type: '', + value: '', + }, + userCount: { + type: 'emphasis', + value: '70', + }, + }, + summaries: { + category: '', // e.g. FUNGIBLE_TOKEN + en: { + title: '', // e.g. ERC20 Swap + variables: { + contextAction: { + type: 'contextAction', + value: '<ACTION_TEXT>', // text should be lowercase. e.g. purchased + }, + }, + default: + '[[subject]] [[contextAction]] [[asset1]] for [[asset2]] [[userCount]] users', + }, + }, + }; + + return transaction; +};