|
1 year ago | |
---|---|---|
.circleci | 1 year ago | |
contracts | 1 year ago | |
migrations | 1 year ago | |
test | 1 year ago | |
.eslintrc.js | 1 year ago | |
.gitattributes | 1 year ago | |
.gitignore | 1 year ago | |
.solcover.js | 1 year ago | |
.solhint.json | 1 year ago | |
LICENSE | 1 year ago | |
README.md | 1 year ago | |
package-lock.json | 1 year ago | |
package.json | 1 year ago | |
truffle-config.js | 1 year ago |
The ultimate environment for Solidity development. TypeScript smart contract testing, code coverage tools, linting, and more included! Simply setup, and get to writing code quickly, with all the tools you need for a fully integrated project at your disposal.
Why does this exist? After setting up a few different repositories with similar configurations, I realized how much effort I’d be spending setting up all of these tools over again. Initially, the barrier was creating a set up where TypeScript could be used to test contracts. Eventually, as I started piling up tools for code coverage, linting, and all of the configuration files needed, from TypeScript’s compiler to both ESLint and solhint, it became clear that getting this all in a consistent setup which can be easily replicated would be the best course of action to save time in future setups.
This is supposed to ease the following integration barriers:
ganache-cli
. https://github.com/trufflesuite/ganache-cliTo set this up in your own repo without going through the step-by-step process below, this section is for you!
To start, if you haven’t already, setup your project as a git repository and an npm package:
git init
npm init
After running through the prompts, install all of the following packages:
npm i --save-dev truffle ganache-cli typescript chai @types/chai @types/mocha @types/node @machinomy/types-truffle @machinomy/types-truffle-contract @machinomy/types-truffle-resolver truffle-contract truffle-resolver solhint eslint eslint-config-google @typescript-eslint/parser @typescript-eslint/eslint-plugin solidity-coverage
Initialize Truffle:
truffle init
Finally, add the following to your files:
package.json
// ...
"scripts": {
"test": "rm -rf ./test/bin/ && tsc --project ./test/tsconfig.json && truffle test ./test/bin/*",
"compile": "npx truffle compile",
"chain": "ganache-cli",
"coverage": "npx solidity-coverage",
"lint": "npm run lint:sol && npm run lint:ts",
"lint:fix": "npm run lint:ts:fix",
"lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"",
"lint:ts": "eslint ./test/src/**/*.ts",
"lint:ts:fix": "eslint --fix ./test/src/**/*.ts"
// ..
.circleci/config.yml
Copy: https://github.com/Luiserebii/Maximum-Solidity-Environment/blob/master/.circleci/config.yml
.eslintrc.js
Copy: https://github.com/Luiserebii/Maximum-Solidity-Environment/blob/master/.eslintrc.js
.gitattributes
Copy: https://github.com/Luiserebii/Maximum-Solidity-Environment/blob/master/.gitattributes
.gitignore
Copy: https://github.com/Luiserebii/Maximum-Solidity-Environment/blob/master/.gitignore
.solcover.js
Copy: https://github.com/Luiserebii/Maximum-Solidity-Environment/blob/master/.solcover.js
.solhint.json
Copy: https://github.com/Luiserebii/Maximum-Solidity-Environment/blob/master/.solhint.json
truffle-config.js
Copy: https://github.com/Luiserebii/Maximum-Solidity-Environment/blob/master/truffle-config.js
At this point, all that is necessary is to signup on CircleCI, Codecov, and LGTM, and set them up with the repo.
Another option for using this is cloning the repo and replacing the repo-specific values with your own. One motivation might be, if you’re having trouble setting up the repo in any of the other ways for any reason, this repo should be guaranteed to have running tests and compiling contracts, so you can work by modifying a successful setup.
To accomplish this, clone the repo:
git clone https://github.com/Luiserebii/Maximum-Solidity-Environment
Install all dependencies:
npm i
And finally, within package.json, replace the following values with your own:
package.json
{
"name": "maximum-solidiy-environment",
"version": "1.0.0",
"description": "The ultimate environment for Solidity development. The final choice. The end.",
// ...
"repository": {
"type": "git",
"url": "git+https://github.com/Luiserebii/Maximum-Solidity-Environment.git"
},
"author": "Luiserebii",
"license": "MIT",
"bugs": {
"url": "https://github.com/Luiserebii/Maximum-Solidity-Environment/issues"
},
"homepage": "https://github.com/Luiserebii/Maximum-Solidity-Environment#readme",
// ...
}
Like in the Quick Setup, at this point, all that is necessary is to signup on CircleCI, Codecov, and LGTM, and set them up with the repo.
cd
into it.git init
npm init
npm i --save-dev truffle
truffle init
.gitignore
, and ignore:
.swo
and .swp
by vim)node_modules/
(dependency installations)build/
(contract build folder generated by Truffle during compilation)npm i --save-dev ganache-cli
truffle-config.js
, uncomment the development
object underneath networks
:truffle-config.js
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
compile
and chain
scripts within package.json
. After doing so, your file should look something like this:package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"compile": "npx truffle compile",
"chain": "ganache-cli",
}
compile
: A convenience script for compiling our contracts. Nice to have, as we take the opportunity to pass any options we want through here.chain
: Run a development blockchain.npm run [scriptname]
. Therefore, to run the two scripts we just added:npm run compile
, andnpm run chain
test
script generated by npm alone for now, and will be editing it later.npm i --save-dev typescript
src
and bin
folders within the test
directory created by Truffle.
src
: The folder where we will place our TypeScript test files. These Typscript files (.ts
) will be compiled into JavaScript and placed in bin
.bin
: This folder is reserved for built Javascript test files compiled by TypeScript, which will be consumed by Truffle for testing.bin
will contain compiled files, let’s add it to the .gitignore
as well!error TS2304: Cannot find name 'assert'.
, so we will need to import chai into our tests and declare assert, and thus, install it.npm i --save-dev chai
npm i --save-dev @types/chai @types/mocha @types/node @machinomy/types-truffle @machinomy/types-truffle-contract @machinomy/types-truffle-resolver
npm i --save-dev truffle-contract truffle-resolver
tsconfig.json file
to pass to the Typescript compiler (tsc
). This will allow us to specify options, such as which folder to compile, and where to send the compiled files to, among others. The following commands will generate this for us. Switch into the test directory, and initialize it through tsc
:
cd test
tsc --init
tsconfig.json
according to our setup:"ES2017"
"outDir"
and set it to "./bin"
"compilerOptions"
:tsconfig.json
// ...
"types": [
"@types/node",
"@types/mocha",
"@types/chai",
"@machinomy/types-truffle",
"@machinomy/types-truffle-contract",
"@machinomy/types-truffle-resolver"
],
// ...
tsconfig.json
{
"compilerOptions": {
"target": "ES2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"outDir": "./bin", /* Redirect output structure to the directory. */
"strict": true, /* Enable all strict type-checking options. */
"types": [
"@types/node",
"@types/mocha",
"@types/chai",
"@machinomy/types-truffle",
"@machinomy/types-truffle-contract",
"@machinomy/types-truffle-resolver"
],
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
}
}
package.json
"scripts": {
"test": "rm -rf ./test/bin && tsc --project ./test/tsconfig.json && truffle test ./test/bin/*"
// ...
}
solhint
. Install via:
npm i --save-dev solhint
.solhint.json
. This is a pretty good configuration that can be used:.solhint.json
{
"extends": "solhint:recommended",
"rules": {
"indent": ["error", 4],
"func-order": "off",
"bracket-align": "off",
"compiler-fixed": "off",
"no-simple-event-func-name": "off",
"separate-by-one-line-in-contract": "off",
"two-lines-top-level-separator": "off",
"mark-callable-contracts": "off",
"compiler-version": ["error", "^0.5.0"]
}
}
lint:sol
:package.json
"scripts": {
// ...
"lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"",
// ...
}
npm i --save-dev eslint
npm i --save-dev eslint-config-google
npm i --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
.eslintrc.js
. The following is a pretty good configuration that can be used:.eslintrc.js
module.exports = {
'env': {
'es6': true,
"node": true,
"mocha": true,
},
'extends': [
'google',
],
'globals': {
'Atomics': 'readonly',
'SharedArrayBuffer': 'readonly',
},
'parser': '@typescript-eslint/parser',
'parserOptions': {
'ecmaVersion': 2018
},
'rules': {
'max-len': ['error', 120, 2],
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }]
},
"plugins": [
"@typescript-eslint"
]
};
lint
will handle both the solhint and ESLint linting. Since no option for fixing linting errors found by solhint exists, lint:fix
will just reference lint:ts:fix
for the time being:package.json
"scripts": {
// ...
"lint": "npm run lint:sol && npm run lint:ts",
"lint:fix": "npm run lint:ts:fix",
"lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"",
"lint:ts": "eslint ./test/src/**/*.ts",
"lint:ts:fix": "eslint --fix ./test/src/**/*.ts"
}
solidity-coverage
:
npm i --save-dev solidity-coverage
package.json
"scripts": {
// ...
"coverage": "npx solidity-coverage",
// ...
}
.solcover.js
. We’ll be asking it to use npm t
for the testing command (shorthand for npm test
), and to skip Migrations.sol
:.solcover.js
module.exports = {
testCommand: 'npm t',
skipFiles: [
'Migrations.sol',
]
};
.circleci
folder at the root of the directory:
mkdir .circleci
config.yml
to it:config.yml
# Recommended documentation on CircleCI and node applications: https://circleci.com/docs/2.0/language-javascript/
version: 2
# 2.1 does not yet support local run
# unless with workaround. For simplicity just use it.
# https://github.com/CircleCI-Public/circleci-cli/issues/79
aliases:
# Alias for any default options
- &defaults
docker:
- image: circleci/node:10.12 # Docker Node 10.12 image.
# Alias for installing npm, if necessary (hence the name)
- &npm_install_if_necessary
run:
name: Install npm dependencies
command: |
# If no node_modules directory
if [ ! -d node_modules ]; then
# Perform a clean npm install
npm ci
fi
# Alias for caching - caches node dependencies with a cache key
# template for an environment variable,
# see circleci.com/docs/2.0/caching/
- &cache_key_node_modules
key: v1-node_modules-{{ checksum "package-lock.json" }}
jobs:
dependencies:
<<: *defaults # << is used to inject an alias
steps:
- checkout
- restore_cache: # special step to restore the dependency cache
<<: *cache_key_node_modules
- *npm_install_if_necessary
- save_cache: # special step to save the dependency cache
paths:
- node_modules
<<: *cache_key_node_modules # Insert our alias to specify cache key to save to
lint:
<<: *defaults
steps:
- checkout
- restore_cache:
<<: *cache_key_node_modules
- *npm_install_if_necessary
- run:
name: Linter
command: npm run lint
test:
<<: *defaults
steps:
- checkout
- restore_cache:
<<: *cache_key_node_modules
- *npm_install_if_necessary
- run:
name: Run development blockchain
command: npm run chain
background: true
- run:
name: Unit tests
command: npm test
coverage:
<<: *defaults
steps:
- checkout
- restore_cache:
<<: *cache_key_node_modules
- *npm_install_if_necessary
- run:
name: Unit tests with coverage report
command: npm run coverage
workflows:
version: 2
everything:
jobs: # For reference, all jobs run in parallel
- dependencies
- lint:
requires:
- dependencies # Wait for dependencies jobs to finish before running
- test:
requires:
- dependencies
- coverage:
requires:
- dependencies
master
as your default branch, this would be:
git push origin master
.circleci/config.yml
file, and edit it as so:config.yml
coverage:
<<: *defaults
steps:
- checkout
- restore_cache:
<<: *cache_key_node_modules
- *npm_install_if_necessary
- run:
name: Install Codecov
command: sudo npm install -g codecov
- run:
name: Unit tests with coverage report
command: npm run coverage
- run:
name: Run Codecov
command: codecov
Install Codecov
and Run Codecov
scripts respectively..gitattributes
. This will allow GitHub to recognize your .sol files as Solidity
*.sol linguist-language=Solidity
This section will focus on using the setup of the environment. Ideally, you should get a solid grasp on how to work from here onwards.
Solidity smart contracts (appended with .sol
) should be written within the contracts/
directory. Contract tests, written in TypeScript (appended with .ts
) should be kept within the test/src/
directory.
Compiling contracts is simple, just run npm run compile
.
To test your contracts, you will need to open a test chain in a different terminal. Run npm run chain
in one, and npm t
in the other. The test script connects to the locally running instance of Ganache, which is why this is necessary.
In order to make sure your code is linted, all you have to do is run npm run lint
. If you need to be more specific:
npm run lint:sol
npm run lint:ts
Sometimes, errors/warnings from ESLint can be fixed automatically by the tool. To do so, run npm run lint:fix
, or npm run lint:ts:fix
if you want to be specific. Both will do the same, as there is no solhint --fix
at the moment.
To check for code coverage, simply run npm run coverage
.
This should be more or less evident by package.json
, but a brief overview might help clarify things. With an understanding of the development flow with this environment, using this should be simple.
If it helps, this was all run on Node v10.15.3 and npm 6.4.1. Further versioning information can be found in the package-lock.json
.
I’d like to give credits to the OpenZeppelin Contracts repo, much of the setup of these files are largely inspired by them. Many of the modifications made were to accommodate TypeScript integration.