diff --git a/.gitignore b/.gitignore index 934269e..d53078b 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ build/ *.pem # debug +tmp/ npm-debug.log* yarn-debug.log* yarn-error.log* diff --git a/bin/gemforge.js b/bin/gemforge.js new file mode 100755 index 0000000..e13a030 --- /dev/null +++ b/bin/gemforge.js @@ -0,0 +1,15 @@ +#!/usr/bin/env node +import { spawnSync } from "node:child_process" +import { fileURLToPath } from 'node:url' +import { dirname, resolve } from "node:path" + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +// find argument string +const index = process.argv.findIndex((arg) => arg === __filename) +const args = process.argv.slice(index + 1) + +// Say our original entrance script is `app.js` +const cmd = `node --no-warnings ${resolve(__dirname, "../build/src/cli.js")}` +spawnSync(cmd, args, { stdio: "inherit", shell: true }) \ No newline at end of file diff --git a/package.json b/package.json index ad641cf..89ed55d 100644 --- a/package.json +++ b/package.json @@ -3,24 +3,18 @@ "version": "1.0.0", "description": "CLI for designing, building, deploying and upgrading Diamond Standard contracts on EVM chains.", "bin": { - "gemforge": "./build/cli.js" - }, - "exports": { - ".": { - "types": "./build/index.d.ts", - "import": { - "development": "./src", - "default": "./build/index.js" - } - } + "gemforge": "./bin/gemforge.js" }, + "type": "module", + "types": "./build/src/index.d.ts", + "exports": "./build/src/index.js", "author": "Ramesh Nair ", "license": "MIT", "scripts": { "prepare": "husky install && npx husky add .husky/commit-msg 'npx commitlint --edit $1'", "prettify": "prettier --write .", - "dev": "tsc -b -w", - "build": "tsc -b" + "dev": "tsc -b -w tsconfig.json", + "build": "rm -fr build/* && tsc -p tsconfig.json" }, "devDependencies": { "@commitlint/cli": "^17.2.0", @@ -41,5 +35,10 @@ "extends": [ "@commitlint/config-conventional" ] + }, + "dependencies": { + "chalk": "^5.3.0", + "commander": "^11.0.0", + "execa": "^7.2.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 97bc10b..47ff93a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,17 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +dependencies: + chalk: + specifier: ^5.3.0 + version: 5.3.0 + commander: + specifier: ^11.0.0 + version: 11.0.0 + execa: + specifier: ^7.2.0 + version: 7.2.0 + devDependencies: '@commitlint/cli': specifier: ^17.2.0 @@ -464,6 +475,11 @@ packages: supports-color: 7.2.0 dev: true + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: false + /cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -494,6 +510,11 @@ packages: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true + /commander@11.0.0: + resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==} + engines: {node: '>=16'} + dev: false + /compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} dependencies: @@ -567,7 +588,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /dargs@7.0.0: resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} @@ -771,6 +791,21 @@ packages: strip-final-newline: 2.0.0 dev: true + /execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + dev: false + /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true @@ -849,7 +884,6 @@ packages: /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - dev: true /git-raw-commits@2.0.11: resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} @@ -941,6 +975,11 @@ packages: engines: {node: '>=10.17.0'} dev: true + /human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + dev: false + /husky@8.0.2: resolution: {integrity: sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg==} engines: {node: '>=14'} @@ -1032,6 +1071,11 @@ packages: engines: {node: '>=8'} dev: true + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: false + /is-text-path@1.0.1: resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} engines: {node: '>=0.10.0'} @@ -1041,7 +1085,6 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true /jiti@1.19.1: resolution: {integrity: sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==} @@ -1203,13 +1246,17 @@ packages: /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} dev: true + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: false + /min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -1268,6 +1315,13 @@ packages: path-key: 3.1.1 dev: true + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: false + /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -1281,6 +1335,13 @@ packages: mimic-fn: 2.1.0 dev: true + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: false + /optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -1356,7 +1417,11 @@ packages: /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: false /path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -1521,16 +1586,13 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 - dev: true /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - dev: true /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true /spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -1587,6 +1649,11 @@ packages: engines: {node: '>=6'} dev: true + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: false + /strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -1744,7 +1811,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true /wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} diff --git a/src/cli.ts b/src/cli.ts index e69de29..4153041 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -0,0 +1,17 @@ +import { Command } from 'commander' + +import packageJson from '../package.json' assert { "type": "json" } +import { command as init } from './cli/init.js' +import { command as build } from './cli/build.js' + +const { version } = packageJson + +const cli = new Command() + +cli + .version(version) + .addCommand(init()) + .addCommand(build()) + +cli.parseAsync(process.argv) + diff --git a/src/cli/build.ts b/src/cli/build.ts new file mode 100644 index 0000000..37d6efc --- /dev/null +++ b/src/cli/build.ts @@ -0,0 +1,11 @@ +import { Command } from 'commander' + +export const command = () => + new Command('init') + .description('Build a project.') + .option('-f, --folder ', 'folder to run the build in', '.') + .option('-c, --config ', 'gemforge config file', 'gemforge.config.js') + .action(async (folder: string) => { + console.log(typeof folder, folder) + throw new Error('test') + }) diff --git a/src/cli/common.ts b/src/cli/common.ts new file mode 100644 index 0000000..65c3b29 --- /dev/null +++ b/src/cli/common.ts @@ -0,0 +1,34 @@ +import { $ } from 'execa' +import chalk from 'chalk' +import path, { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +export const $$ = $({ stdio: 'inherit' }) + +export const getContext = (args: Record) => { + const context = { + folder: args.folder, + } + + if (context.folder != '.') { + log(`Using folder ${context.folder}`) + context.folder = path.resolve(process.cwd(), context.folder) + } else { + context.folder = process.cwd() + } + + return context +} + +export const template = (file: string) => { + return path.resolve(__dirname, '../templates', file) +} + +export const log = (message: string) => console.log(chalk.gray(message)) +export const info = (message: string) => console.log(chalk.whiteBright(message)) +export const success = (message: string) => console.log(chalk.greenBright(message)) +export const error = (message: string) => console.log(chalk.redBright(message)) +export const warn = (message: string) => console.log(chalk.yellowBright(message)) diff --git a/src/cli/init.ts b/src/cli/init.ts new file mode 100644 index 0000000..65d2076 --- /dev/null +++ b/src/cli/init.ts @@ -0,0 +1,14 @@ +import { Command } from 'commander' +import { $$, getContext, info, template } from './common.js' + +export const command = () => + new Command('init') + .description('Initialize a new project, generating all necessary scaffolding.') + .option('-f, --folder ', 'folder to create the scaffold in', '.') + .action(async (args) => { + const ctx = getContext(args) + + // write config file + info('Writing config file...') + await $$`cp ${template('gemforge.config.js')} ${ctx.folder}/gemforge.config.js` + }) diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/templates/Proxy.sol b/src/templates/Proxy.sol new file mode 100644 index 0000000..4db41ff --- /dev/null +++ b/src/templates/Proxy.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +pragma solidity >={SOLC_VERSION}; + +import { Diamond } from 'lib/diamond-2-hardhat/contracts/Diamond.sol'; +import { LibDiamond } from 'lib/diamond-2-hardhat/contracts/libraries/LibDiamond.sol'; +import { DiamondCutFacet } from 'lib/diamond-2-hardhat/contracts/facets/DiamondCutFacet.sol'; +import { DiamondLoupeFacet } from 'lib/diamond-2-hardhat/contracts/facets/DiamondLoupeFacet.sol'; +import { OwnershipFacet } from 'lib/diamond-2-hardhat/contracts/facets/OwnershipFacet.sol'; +import { IDiamondCut } from 'lib/diamond-2-hardhat/contracts/interfaces/IDiamondCut.sol'; +import { IDiamondLoupe } from 'lib/diamond-2-hardhat/contracts/interfaces/IDiamondLoupe.sol'; +import { IERC165 } from 'lib/diamond-2-hardhat/contracts/interfaces/IERC165.sol'; +import { IERC173 } from 'lib/diamond-2-hardhat/contracts/interfaces/IERC173.sol'; +import { DiamondInit } from 'lib/diamond-2-hardhat/contracts/upgradeInitializers/DiamondInit.sol'; +import { AppStorage, LibAppStorage } from '../libraries/LibAppStorage.sol'; + +contract Proxy is Diamond { + constructor(address _contractOwner) payable Diamond(_contractOwner, address(new DiamondCutFacet())) { + // add core facets + _initCoreFacets(); + + // setup ERC165 data + LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); + ds.supportedInterfaces[type(IERC165).interfaceId] = true; + ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true; + ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true; + ds.supportedInterfaces[type(IERC173).interfaceId] = true; + + // setup storage + AppStorage storage s = LibAppStorage.diamondStorage(); + } + + // Internal + + function _initCoreFacets() private { + // add basic facets + IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](2); + + address _diamondLoupeFacet = address(new DiamondLoupeFacet()); + bytes4[] memory f1 = new bytes4[](4); + f1[0] = IDiamondLoupe.facets.selector; + f1[1] = IDiamondLoupe.facetFunctionSelectors.selector; + f1[2] = IDiamondLoupe.facetAddresses.selector; + f1[3] = IDiamondLoupe.facetAddress.selector; + cut[0] = IDiamondCut.FacetCut({ + facetAddress: _diamondLoupeFacet, + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: f1 + }); + + address _ownershipFacet = address(new OwnershipFacet()); + bytes4[] memory f2 = new bytes4[](2); + f2[0] = IERC173.owner.selector; + f2[1] = IERC173.transferOwnership.selector; + cut[1] = IDiamondCut.FacetCut({ + facetAddress: _ownershipFacet, + action: IDiamondCut.FacetCutAction.Add, + functionSelectors: f2 + }); + + LibDiamond.diamondCut(cut, address(0), ''); + } +} diff --git a/src/templates/gemforge.config.js b/src/templates/gemforge.config.js new file mode 100644 index 0000000..2304ee1 --- /dev/null +++ b/src/templates/gemforge.config.js @@ -0,0 +1,5 @@ +module.exports = { + solc: { + version: '0.8.21', + } +} diff --git a/tsconfig.json b/tsconfig.json index 3a5ac78..0ef1c2d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,23 +1,22 @@ { "compilerOptions": { - "target": "es2020", - "module": "ES2020", - "lib": ["ES2020"], + "module": "ESNext", + "moduleResolution": "NodeNext", + "outDir": "build", + "target": "ESNext", "strict": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "allowJs": true, "skipLibCheck": false, "declaration": true, - "moduleResolution": "NodeNext", "resolveJsonModule": true, "isolatedModules": true, "incremental": true, "paths": { "@/*": ["./src/*"] - }, - "outDir": "build" + } }, - "include": ["src"], + "include": ["src/**/*"], "exclude": ["build", "node_modules"] }