├── .gitignore ├── .travis.yml ├── .vscodeignore ├── .vscode ├── extensions.json ├── tasks.json ├── settings.json └── launch.json ├── tslint.json ├── tsconfig.json ├── LICENSE ├── package.json ├── src ├── utils │ └── parse.ts └── extension.ts └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "node" 5 | 6 | install: 7 | - npm install 8 | - npm run vscode:prepublish -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "ms-vscode.vscode-typescript-tslint-plugin" 6 | ] 7 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-string-throw": true, 4 | "no-unused-expression": true, 5 | "no-duplicate-variable": true, 6 | "curly": true, 7 | "class-name": true, 8 | "semicolon": [ 9 | true, 10 | "always" 11 | ] 12 | }, 13 | "defaultSeverity": "error" 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [{ 8 | "name": "Run Extension", 9 | "type": "extensionHost", 10 | "request": "launch", 11 | "runtimeExecutable": "${execPath}", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/out/**/*.js" 17 | ], 18 | "preLaunchTask": "npm: watch" 19 | }, 20 | { 21 | "name": "Extension Tests", 22 | "type": "extensionHost", 23 | "request": "launch", 24 | "runtimeExecutable": "${execPath}", 25 | "args": [ 26 | "--extensionDevelopmentPath=${workspaceFolder}", 27 | "--extensionTestsPath=${workspaceFolder}/out/test" 28 | ], 29 | "outFiles": [ 30 | "${workspaceFolder}/out/test/**/*.js" 31 | ], 32 | "preLaunchTask": "npm: watch" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Aniket-Engg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sol-profiler-vscode", 3 | "displayName": "sol-profiler", 4 | "description": "Generate & Store Solidity Smart Contract Methods Profile", 5 | "version": "1.3.2", 6 | "publisher": "Aniket-Engg", 7 | "repository": "https://github.com/Aniket-Engg/sol-profiler-vscode", 8 | "engines": { 9 | "vscode": "^1.26.0" 10 | }, 11 | "categories": [ 12 | "Other" 13 | ], 14 | "activationEvents": [ 15 | "onCommand:extension.sol-profiler.generate" 16 | ], 17 | "main": "./out/extension.js", 18 | "contributes": { 19 | "commands": [ 20 | { 21 | "command": "extension.sol-profiler.generate", 22 | "title": "sol-profiler: generate profile" 23 | } 24 | ] 25 | }, 26 | "scripts": { 27 | "vscode:prepublish": "npm run compile", 28 | "compile": "tsc -p ./", 29 | "watch": "tsc -watch -p ./", 30 | "postinstall": "node ./node_modules/vscode/bin/install", 31 | "test": "tslint --project .", 32 | "lint:fix": "tslint --project . --fix" 33 | }, 34 | "devDependencies": { 35 | "@types/mocha": "^2.2.42", 36 | "@types/node": "^10.12.21", 37 | "tslint": "^5.12.1", 38 | "typescript": "^3.3.1", 39 | "vscode": "^1.1.34" 40 | }, 41 | "dependencies": { 42 | "sol-straightener": "^1.1.1", 43 | "solparse": "^2.2.8", 44 | "table": "^5.2.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/utils/parse.ts: -------------------------------------------------------------------------------- 1 | export var parsePartData = (contract:any, part:any) => { 2 | let contractName = contract.name; 3 | if(contract.type == 'LibraryStatement') { 4 | contractName = contractName + ' (library)'; 5 | } 6 | else if(contract.type == 'InterfaceStatement') { 7 | contractName = contractName + ' (interface)'; 8 | } 9 | 10 | let funcName = null; 11 | if(part.type == 'ConstructorDeclaration') { 12 | funcName = 'constructor'; 13 | } 14 | else if(part.type == 'FunctionDeclaration'){ 15 | funcName = part.name || ''; 16 | } 17 | 18 | let params:any = []; 19 | if(part.params) { 20 | part.params.forEach(function(param: any) { 21 | if(param.storage_location) { 22 | params.push(param.literal.literal + ' ' + param.storage_location); 23 | } 24 | else { 25 | params.push(param.literal.literal); 26 | } 27 | }); 28 | funcName += '(' + params.join(',') + ')'; 29 | } 30 | else { 31 | //Check fallback 32 | if(!funcName && !part.name && !part.params && !part.returnParams) { 33 | funcName = '()' + ' -fallback'; 34 | } 35 | else { 36 | funcName += '()'; 37 | } 38 | } 39 | if(part.is_abstract) { 40 | funcName += ' -abstract'; 41 | } 42 | 43 | // Default is public 44 | let visibility = "public"; 45 | let viewOrPure = ''; 46 | let returns:any = []; 47 | let custom :any = []; 48 | 49 | if(part.modifiers) { 50 | part.modifiers.forEach(function(mod : any) { 51 | switch(mod.name) { 52 | case "public": 53 | break; 54 | case "private": 55 | visibility = "private"; 56 | break; 57 | case "internal": 58 | visibility = "internal"; 59 | break; 60 | case "external": 61 | visibility = "external"; 62 | break; 63 | case "view": 64 | viewOrPure = "view"; 65 | break; 66 | case "pure": 67 | viewOrPure = "pure"; 68 | break; 69 | default: 70 | custom.push(mod.name); 71 | } 72 | }); 73 | } 74 | if(part.returnParams) { 75 | part.returnParams.params.forEach(function(param: any) { 76 | if(param.storage_location) { 77 | returns.push(param.literal.literal + ' ' + param.storage_location); 78 | } 79 | else { 80 | returns.push(param.literal.literal); 81 | } 82 | }); 83 | } 84 | 85 | return { 86 | contractName: contractName, 87 | functionName: funcName, 88 | visibility : visibility, 89 | viewOrPure : viewOrPure, 90 | returns : returns, 91 | modifiers : custom 92 | }; 93 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Travis (.com)](https://img.shields.io/travis/com/Aniket-Engg/sol-profiler-vscode.svg?style=for-the-badge)](https://travis-ci.com/Aniket-Engg/sol-profiler-vscode) 2 | [![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/Aniket-Engg.sol-profiler-vscode.svg?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=Aniket-Engg.sol-profiler-vscode) 3 | [![Visual Studio Marketplace Installs - Azure DevOps Extension](https://img.shields.io/visual-studio-marketplace/azure-devops/installs/total/Aniket-Engg.sol-profiler-vscode.svg?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=Aniket-Engg.sol-profiler-vscode) 4 | [![Visual Studio Marketplace Downloads](https://img.shields.io/visual-studio-marketplace/d/Aniket-Engg.sol-profiler-vscode.svg?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=Aniket-Engg.sol-profiler-vscode) 5 | [![Visual Studio Marketplace Rating (Stars)](https://img.shields.io/visual-studio-marketplace/stars/Aniket-Engg.sol-profiler-vscode.svg?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=Aniket-Engg.sol-profiler-vscode) 6 | [![GitHub](https://img.shields.io/github/license/aniket-engg/sol-profiler-vscode.svg?style=for-the-badge)](https://github.com/Aniket-Engg/sol-profiler-vscode/blob/master/LICENSE) 7 | 8 | # sol-profiler-vscode 9 | 10 | [sol-profiler-vscode](https://marketplace.visualstudio.com/items?itemName=Aniket-Engg.sol-profiler-vscode) extension generates & stores Solidity smart contract methods profile. Contract file should be opened in editor. Generated profile helps to get knowledge of method attributes any time later at one glance. 11 | 12 | **Note:** sol-profiler-vscode extension does not ensure/guarantee any kind of security or correctness of any smart-contract. 13 | 14 | ## Features 15 | 16 | * Generates & stores profile in a folder named `profiles` in a .txt file named with suffix `_Profile` 17 | * Supports file import from relative path and `node_modules`(like openzeppelin-solidity etc.) both 18 | * Explicitly marks `abstract` and `fallback` functions 19 | * Explicitly marks `library` and `interface` contracts 20 | * Since Solidity release [v0.5.0](https://github.com/ethereum/solidity/releases/tag/v0.5.0), Explicit data location for all variables of struct, array or mapping types is now mandatory, so profile also include the storage location of parameters defined explicitly. 21 | 22 | ## How to use 23 | 24 | * Install extension by searching `sol-profiler` in extensions section of VSCode 25 | * Open contract file (`.sol`) in editor whose profile you want to generate 26 | * Open `View -> Command Palette` (Keyboard Shortcut: `CTRL + SHIFT + P`) 27 | * Type `sol-profiler`. You will see a text in dropdown as `sol-profiler: generate profile`. Click on it & you're done. (As result, success/failure message will be shown as notification) 28 | 29 | ## Demo 30 | 31 | ![spvDemo2](https://user-images.githubusercontent.com/30843294/55622296-68c9c580-57bd-11e9-9395-fe104d909d39.gif) 32 | 33 | For CLI version, see https://www.npmjs.com/package/sol-profiler 34 | 35 | ## License 36 | [MIT](https://github.com/Aniket-Engg/sol-profiler-vscode/blob/master/LICENSE) 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | const parser = require("solparse"); 3 | const table = require('table'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const straightener = require('sol-straightener'); 7 | import {parsePartData} from './utils/parse'; 8 | 9 | export function activate(context: vscode.ExtensionContext) { 10 | 11 | let disposable = vscode.commands.registerCommand('extension.sol-profiler.generate', async() => { 12 | let config = { 13 | border: { 14 | topBody: `─`, 15 | topJoin: `┬`, 16 | topLeft: `┌`, 17 | topRight: `┐`, 18 | 19 | bottomBody: `─`, 20 | bottomJoin: `┴`, 21 | bottomLeft: `└`, 22 | bottomRight: `┘`, 23 | 24 | bodyLeft: `│`, 25 | bodyRight: `│`, 26 | bodyJoin: `│`, 27 | 28 | joinBody: `─`, 29 | joinLeft: `├`, 30 | joinRight: `┤`, 31 | joinJoin: `┼` 32 | } 33 | }; 34 | let version; 35 | let tableRows:any = []; 36 | try{ 37 | let filePath = vscode.window.activeTextEditor!.document.fileName; 38 | let pathArray = filePath.split('/'); 39 | let file = pathArray[pathArray.length -1]; 40 | let fileArray = file.split('.'); 41 | if(fileArray[fileArray.length -1] != 'sol') { 42 | throw new Error('Open a solidity(.sol) file in editor.'); 43 | } 44 | 45 | let contractName = file.substr(0, file.length - 4); 46 | const contractSource = await straightener.straighten(filePath); 47 | let parsedData = parser.parse(contractSource); 48 | if(parsedData.body[0].type == 'PragmaStatement') { 49 | let pragmaData = parsedData.body[0]; 50 | version = pragmaData.start_version.operator + pragmaData.start_version.version; 51 | if(pragmaData.end_version) { 52 | version += pragmaData.end_version.operator + pragmaData.end_version.version; 53 | } 54 | } 55 | 56 | tableRows.push(['','File: ' + file + ', Solidity Pragma: ' + version, '','','','']); 57 | 58 | // Adding header row 59 | tableRows.push(['Contract/Library/Interface', 'Function(Params with Storage Location)', 'Visibility', 'View/Pure', 'Returns', 'Modifiers']); 60 | parsedData.body.forEach((contract: any) => { 61 | if(contract.type != 'PragmaStatement'){ 62 | contract.body.forEach(function(part : any) { 63 | if(part.type == 'ConstructorDeclaration' || part.type == 'FunctionDeclaration') { 64 | let {contractName, functionName, visibility, viewOrPure, returns, modifiers} = parsePartData(contract, part); 65 | tableRows.push([contractName, functionName, visibility, viewOrPure, returns, modifiers]); 66 | } 67 | }); 68 | } 69 | }); 70 | var fileData = table.table(tableRows, config); 71 | if(!fs.existsSync(path.join(vscode.workspace.rootPath, '/profiles'))) { 72 | fs.mkdirSync(path.join(vscode.workspace.rootPath, '/profiles')); 73 | } 74 | let profilePath = vscode.workspace.rootPath + '/profiles/' + contractName + "_Profile.txt"; 75 | fs.writeFileSync(profilePath, fileData); 76 | vscode.window.showInformationMessage(`Profile for contract '${contractName}' generated and stored at ${profilePath}.`); 77 | }catch(error){ 78 | vscode.window.showErrorMessage("Error in generating profile: " + error.message); 79 | } 80 | }); 81 | 82 | context.subscriptions.push(disposable); 83 | } 84 | 85 | export function deactivate() {} 86 | --------------------------------------------------------------------------------