Initial commit

This commit is contained in:
Raphael
2021-06-04 16:17:21 +02:00
commit ec556d09f4
36 changed files with 16274 additions and 0 deletions

3
.commitlintrc.js Normal file
View File

@@ -0,0 +1,3 @@
module.exports = {
extends: ["@commitlint/config-conventional"],
};

3
.czrc Normal file
View File

@@ -0,0 +1,3 @@
{
"path": "cz-conventional-changelog"
}

16
.editorconfig Normal file
View File

@@ -0,0 +1,16 @@
# EditorConfig http://EditorConfig.org
# top-most EditorConfig file
root = true
# All files
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.sol]
indent_size = 4

2
.env.example Normal file
View File

@@ -0,0 +1,2 @@
INFURA_API_KEY=zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
MNEMONIC=here is where your twelve words mnemonic should be put my friend

13
.eslintignore Normal file
View File

@@ -0,0 +1,13 @@
# folders
artifacts/
build/
cache/
coverage/
dist/
lib/
node_modules/
typechain/
# files
.solcover.js
coverage.json

18
.eslintrc.yaml Normal file
View File

@@ -0,0 +1,18 @@
extends:
- "eslint:recommended"
- "plugin:@typescript-eslint/eslint-recommended"
- "plugin:@typescript-eslint/recommended"
- "plugin:@typescript-eslint/recommended-requiring-type-checking"
- "prettier"
parser: "@typescript-eslint/parser"
parserOptions:
project: "tsconfig.json"
plugins:
- "@typescript-eslint"
root: true
rules:
"@typescript-eslint/no-inferrable-types": "off"
"@typescript-eslint/no-unused-vars":
- error
- argsIgnorePattern: _
varsIgnorePattern: _

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.sol linguist-language=Solidity

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
custom: ["https://gitcoin.co/grants/1657/paulrberg-open-source-engineering"]

24
.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# folders
.coverage_artifacts/
.coverage_cache/
.coverage_contracts/
.yarn/*
!.yarn/releases
!.yarn/plugins
artifacts/
build/
cache/
coverage/
dist/
lib/
node_modules/
typechain/
# files
*.env
*.log
*.tsbuildinfo
coverage.json
npm-debug.log*
yarn-debug.log*
yarn-error.log*

1
.husky/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_

4
.husky/commit-msg Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn dlx commitlint -e

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn dlx lint-staged

5
.lintstagedrc Normal file
View File

@@ -0,0 +1,5 @@
{
"*.{js,json,md,sol,ts}": [
"prettier --config ./.prettierrc --write"
]
}

12
.prettierignore Normal file
View File

@@ -0,0 +1,12 @@
# folders
artifacts/
build/
cache/
coverage/
dist/
lib/
node_modules/
typechain/
# files
coverage.json

17
.prettierrc Normal file
View File

@@ -0,0 +1,17 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"endOfLine":"auto",
"printWidth": 120,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "all",
"overrides": [
{
"files": "*.sol",
"options": {
"tabWidth": 4
}
}
]
}

23
.solcover.js Normal file
View File

@@ -0,0 +1,23 @@
const shell = require("shelljs");
// The environment variables are loaded in hardhat.config.ts
const mnemonic = process.env.MNEMONIC;
if (!mnemonic) {
throw new Error("Please set your MNEMONIC in a .env file");
}
module.exports = {
istanbulReporter: ["html", "lcov"],
onCompileComplete: async function (_config) {
await run("typechain");
},
onIstanbulComplete: async function (_config) {
// We need to do this because solcover generates bespoke artifacts.
shell.rm("-rf", "./artifacts");
shell.rm("-rf", "./typechain");
},
providerOptions: {
mnemonic,
},
skipFiles: ["mocks", "test"],
};

20
.solhint.json Normal file
View File

@@ -0,0 +1,20 @@
{
"extends": "solhint:recommended",
"plugins": ["prettier"],
"rules": {
"code-complexity": ["error", 7],
"compiler-version": ["error", ">=0.8.0"],
"const-name-snakecase": "off",
"constructor-syntax": "error",
"func-visibility": ["error", { "ignoreConstructors": true }],
"max-line-length": ["error", 120],
"not-rely-on-time": "off",
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
],
"reason-string": ["warn", { "maxLength": 64 }]
}
}

5
.solhintignore Normal file
View File

@@ -0,0 +1,5 @@
# folders
.yarn/
build/
dist/
node_modules/

File diff suppressed because one or more lines are too long

55
.yarn/releases/yarn-berry.cjs vendored Executable file

File diff suppressed because one or more lines are too long

7
.yarnrc.yml Normal file
View File

@@ -0,0 +1,7 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-berry.cjs

7
LICENSE.md Normal file
View File

@@ -0,0 +1,7 @@
# WTFPL
by Paul Razvan Berg (@PaulRBerg)
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

117
README.md Normal file
View File

@@ -0,0 +1,117 @@
# Solidity Template
My favourite setup for writing Solidity smart contracts.
- [Hardhat](https://github.com/nomiclabs/hardhat): compile and run the smart contracts on a local development network
- [TypeChain](https://github.com/ethereum-ts/TypeChain): generate TypeScript types for smart contracts
- [Ethers](https://github.com/ethers-io/ethers.js/): renowned Ethereum library and wallet implementation
- [Waffle](https://github.com/EthWorks/Waffle): tooling for writing comprehensive smart contract tests
- [Solhint](https://github.com/protofire/solhint): linter
- [Solcover](https://github.com/sc-forks/solidity-coverage): code coverage
- [Prettier Plugin Solidity](https://github.com/prettier-solidity/prettier-plugin-solidity): code formatter
This is a GitHub template, which means you can reuse it as many times as you want. You can do that by clicking the "Use this
template" button at the top of the page.
## Usage
### Pre Requisites
Before running any command, make sure to install dependencies:
```sh
$ yarn install
```
### Compile
Compile the smart contracts with Hardhat:
```sh
$ yarn compile
```
### TypeChain
Compile the smart contracts and generate TypeChain artifacts:
```sh
$ yarn typechain
```
### Lint Solidity
Lint the Solidity code:
```sh
$ yarn lint:sol
```
### Lint TypeScript
Lint the TypeScript code:
```sh
$ yarn lint:ts
```
### Test
Run the Mocha tests:
```sh
$ yarn test
```
### Coverage
Generate the code coverage report:
```sh
$ yarn coverage
```
### Report Gas
See the gas usage per unit test and average gas per method call:
```sh
$ REPORT_GAS=true yarn test
```
### Clean
Delete the smart contract artifacts, the coverage reports and the Hardhat cache:
```sh
$ yarn clean
```
### Deploy
Deploy the contracts to Hardhat Network:
```sh
$ yarn deploy
```
Deploy the contracts to a specific network, such as the Ropsten testnet:
```sh
$ yarn deploy:network ropsten
```
## Syntax Highlighting
If you use VSCode, you can enjoy syntax highlighting for your Solidity code via the
[vscode-solidity](https://github.com/juanfranblanco/vscode-solidity) extension. The recommended approach to set the
compiler version is to add the following fields to your VSCode user settings:
```json
{
"solidity.compileUsingRemoteVersion": "v0.8.4+commit.c7e474f2",
"solidity.defaultCompiler": "remote"
}
```
Where of course `v0.8.4+commit.c7e474f2` can be replaced with any other version.

28
contracts/Greeter.sol Normal file
View File

@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import "hardhat/console.sol";
error GreeterError();
contract Greeter {
string public greeting;
constructor(string memory _greeting) {
console.log("Deploying a Greeter with greeting:", _greeting);
greeting = _greeting;
}
function greet() public view returns (string memory) {
return greeting;
}
function setGreeting(string memory _greeting) public {
console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
greeting = _greeting;
}
function throwError() external pure {
revert GreeterError();
}
}

100
hardhat.config.ts Normal file
View File

@@ -0,0 +1,100 @@
import "@nomiclabs/hardhat-waffle";
import "@typechain/hardhat";
import "hardhat-gas-reporter";
import "solidity-coverage";
import "./tasks/accounts";
import "./tasks/clean";
import { resolve } from "path";
import { config as dotenvConfig } from "dotenv";
import { HardhatUserConfig } from "hardhat/config";
import { NetworkUserConfig } from "hardhat/types";
dotenvConfig({ path: resolve(__dirname, "./.env") });
const chainIds = {
ganache: 1337,
goerli: 5,
hardhat: 31337,
kovan: 42,
mainnet: 1,
rinkeby: 4,
ropsten: 3,
};
// Ensure that we have all the environment variables we need.
const mnemonic = process.env.MNEMONIC;
if (!mnemonic) {
throw new Error("Please set your MNEMONIC in a .env file");
}
const infuraApiKey = process.env.INFURA_API_KEY;
if (!infuraApiKey) {
throw new Error("Please set your INFURA_API_KEY in a .env file");
}
function createTestnetConfig(network: keyof typeof chainIds): NetworkUserConfig {
const url: string = `https://${network}.infura.io/v3/${infuraApiKey}`;
return {
accounts: {
count: 10,
initialIndex: 0,
mnemonic,
path: "m/44'/60'/0'/0",
},
chainId: chainIds[network],
url,
};
}
const config: HardhatUserConfig = {
defaultNetwork: "hardhat",
gasReporter: {
currency: "USD",
enabled: process.env.REPORT_GAS ? true : false,
excludeContracts: [],
src: "./contracts",
},
networks: {
hardhat: {
accounts: {
mnemonic,
},
chainId: chainIds.hardhat,
},
goerli: createTestnetConfig("goerli"),
kovan: createTestnetConfig("kovan"),
rinkeby: createTestnetConfig("rinkeby"),
ropsten: createTestnetConfig("ropsten"),
},
paths: {
artifacts: "./artifacts",
cache: "./cache",
sources: "./contracts",
tests: "./test",
},
solidity: {
version: "0.8.4",
settings: {
metadata: {
// Not including the metadata hash
// https://github.com/paulrberg/solidity-template/issues/31
bytecodeHash: "none",
},
// You should disable the optimizer when debugging
// https://hardhat.org/hardhat-network/#solidity-optimizer-support
optimizer: {
enabled: true,
runs: 800,
},
},
},
typechain: {
outDir: "typechain",
target: "ethers-v5",
},
};
export default config;

85
package.json Normal file
View File

@@ -0,0 +1,85 @@
{
"name": "@paulrberg/solidity-template",
"description": "Setup for writing Solidity smart contracts",
"version": "1.0.0",
"author": {
"name": "Paul Razvan Berg",
"url": "https://paulrberg.com"
},
"devDependencies": {
"@codechecks/client": "^0.1.10",
"@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4",
"@ethersproject/abi": "^5.2.0",
"@ethersproject/abstract-signer": "^5.2.0",
"@ethersproject/bignumber": "^5.2.0",
"@ethersproject/bytes": "^5.2.0",
"@ethersproject/contracts": "^5.2.0",
"@ethersproject/providers": "^5.2.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@typechain/ethers-v5": "^7.0.0",
"@typechain/hardhat": "^2.0.1",
"@types/chai": "^4.2.18",
"@types/fs-extra": "^9.0.11",
"@types/mocha": "^8.2.2",
"@types/node": "^15.6.1",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/parser": "^4.25.0",
"chai": "^4.3.4",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"dotenv": "^10.0.0",
"eslint": "^7.27.0",
"eslint-config-prettier": "^8.3.0",
"ethereum-waffle": "^3.3.0",
"ethers": "^5.2.0",
"fs-extra": "^10.0.0",
"hardhat": "^2.3.0",
"hardhat-gas-reporter": "^1.0.4",
"husky": "^6.0.0",
"lint-staged": "^11.0.0",
"mocha": "^8.4.0",
"prettier": "^2.3.0",
"prettier-plugin-solidity": "^1.0.0-beta.11",
"shelljs": "^0.8.4",
"solhint": "^3.3.6",
"solhint-plugin-prettier": "^0.0.5",
"solidity-coverage": "^0.7.16",
"ts-generator": "^0.1.1",
"ts-node": "^10.0.0",
"typechain": "^5.0.0",
"typescript": "~4.2.4"
},
"files": [
"/contracts"
],
"keywords": [
"blockchain",
"ethereum",
"hardhat",
"smart-contracts",
"solidity"
],
"license": "WTFPL",
"private": true,
"resolutions": {
"@ethersproject/wallet": "^5.2.0"
},
"scripts": {
"clean": "hardhat clean",
"commit": "git-cz",
"compile": "hardhat compile",
"coverage": "hardhat coverage --solcoverjs ./.solcover.js --temp artifacts --testfiles \"./test/**/*.ts\"",
"deploy": "hardhat run scripts/deploy.ts",
"deploy:network": "hardhat run scripts/deploy.ts --network",
"lint": "yarn run lint:sol && yarn run lint:ts && yarn run prettier:check",
"lint:sol": "solhint --config ./.solhint.json --max-warnings 0 \"contracts/**/*.sol\"",
"lint:ts": "eslint --config ./.eslintrc.yaml --ignore-path ./.eslintignore --ext .js,.ts .",
"postinstall": "husky install",
"prettier": "prettier --config ./.prettierrc --write \"**/*.{js,json,md,sol,ts}\"",
"prettier:check": "prettier --check --config ./.prettierrc \"**/*.{js,json,md,sol,ts}\"",
"test": "hardhat test",
"typechain": "hardhat typechain"
}
}

23
scripts/deploy.ts Normal file
View File

@@ -0,0 +1,23 @@
import { Contract } from "@ethersproject/contracts";
// We require the Hardhat Runtime Environment explicitly here. This is optional but useful for running the
// script in a standalone fashion through `node <script>`. When running the script with `hardhat run <script>`,
// you'll find the Hardhat Runtime Environment's members available in the global scope.
import { ethers } from "hardhat";
import { Greeter__factory } from "../typechain";
async function main(): Promise<void> {
const Greeter: Greeter__factory = await ethers.getContractFactory("Greeter");
const greeter: Contract = await Greeter.deploy("Hello, Buidler!");
await greeter.deployed();
console.log("Greeter deployed to: ", greeter.address);
}
// We recommend this pattern to be able to use async/await everywhere and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error: Error) => {
console.error(error);
process.exit(1);
});

12
tasks/accounts.ts Normal file
View File

@@ -0,0 +1,12 @@
import { Signer } from "@ethersproject/abstract-signer";
import { task } from "hardhat/config";
import { TASK_ACCOUNTS } from "./task-names";
task(TASK_ACCOUNTS, "Prints the list of accounts", async (_taskArgs, hre) => {
const accounts: Signer[] = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(await account.getAddress());
}
});

12
tasks/clean.ts Normal file
View File

@@ -0,0 +1,12 @@
import fsExtra from "fs-extra";
import { TASK_CLEAN } from "hardhat/builtin-tasks/task-names";
import { task } from "hardhat/config";
task(TASK_CLEAN, "Overrides the standard clean task", async function (_taskArgs, { config }, runSuper) {
await fsExtra.remove("./coverage");
await fsExtra.remove("./coverage.json");
if (config.typechain?.outDir) {
await fsExtra.remove(config.typechain.outDir);
}
await runSuper();
});

1
tasks/task-names.ts Normal file
View File

@@ -0,0 +1 @@
export const TASK_ACCOUNTS: string = "accounts";

10
test/Greeter.behavior.ts Normal file
View File

@@ -0,0 +1,10 @@
import { expect } from "chai";
export function shouldBehaveLikeGreeter(): void {
it("should return the new greeting once it's changed", async function () {
expect(await this.greeter.connect(this.signers.admin).greet()).to.equal("Hello, world!");
await this.greeter.setGreeting("Hola, mundo!");
expect(await this.greeter.connect(this.signers.admin).greet()).to.equal("Hola, mundo!");
});
}

28
test/Greeter.ts Normal file
View File

@@ -0,0 +1,28 @@
import hre from "hardhat";
import { Artifact } from "hardhat/types";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address";
import { Greeter } from "../typechain/Greeter";
import { Signers } from "../types";
import { shouldBehaveLikeGreeter } from "./Greeter.behavior";
const { deployContract } = hre.waffle;
describe("Unit tests", function () {
before(async function () {
this.signers = {} as Signers;
const signers: SignerWithAddress[] = await hre.ethers.getSigners();
this.signers.admin = signers[0];
});
describe("Greeter", function () {
beforeEach(async function () {
const greeting: string = "Hello, world!";
const greeterArtifact: Artifact = await hre.artifacts.readArtifact("Greeter");
this.greeter = <Greeter>await deployContract(this.signers.admin, greeterArtifact, [greeting]);
});
shouldBehaveLikeGreeter();
});
});

26
tsconfig.json Normal file
View File

@@ -0,0 +1,26 @@
{
"compilerOptions": {
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"lib": ["es5", "es6"],
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"outDir": "dist",
"resolveJsonModule": true,
"sourceMap": true,
"strict": true,
"target": "es5"
},
"exclude": ["artifacts", "node_modules"],
"files": ["./hardhat.config.ts"],
"include": [
"artifacts/**/*",
"artifacts/**/*.json",
"scripts/**/*",
"tasks/**/*",
"test/**/*",
"typechain/**/*",
"types/**/*"
]
}

13
types/augmentations.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
// eslint-disable @typescript-eslint/no-explicit-any
import { Fixture } from "ethereum-waffle";
import { Signers } from "./";
import { Greeter } from "../typechain/Greeter";
declare module "mocha" {
export interface Context {
greeter: Greeter;
loadFixture: <T>(fixture: Fixture<T>) => Promise<T>;
signers: Signers;
}
}

5
types/index.ts Normal file
View File

@@ -0,0 +1,5 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address";
export interface Signers {
admin: SignerWithAddress;
}

15496
yarn.lock Normal file

File diff suppressed because it is too large Load Diff