Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Advanced Sample Project that uses TypeScript #1770

Merged
merged 21 commits into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1038d20
Some corrections to the (non-TS) Adv. Sample Proj.
feuGeneA Aug 6, 2021
1c332be
Add Advanced Sample Project with TypeScript
feuGeneA Aug 6, 2021
aaa1976
add typechain to recommended gitignore
feuGeneA Aug 10, 2021
771bc11
Convert that last require to an import
feuGeneA Aug 10, 2021
ac5bb98
README mention skipping typechecking with ts-node
feuGeneA Aug 10, 2021
3c48f86
Added .env & coverage to recommended_gitignore.txt
feuGeneA Aug 11, 2021
3d05a25
Merge branch 'multiple-sample-projects' into sample-ts-project
feuGeneA Aug 12, 2021
aad5237
Work around sc-forks/solidity-coverage#652
feuGeneA Aug 12, 2021
6a6945d
Merge branch 'master' into sample-ts-project
fvictorio Aug 13, 2021
af2959a
Correct js/ts mixup in README
feuGeneA Aug 18, 2021
7f00860
Correct another js/ts mixup in README
feuGeneA Aug 18, 2021
4101c42
Rename .env.template to .env.example
feuGeneA Aug 18, 2021
721ba0d
Merge branch 'master' into sample-ts-project
fvictorio Aug 27, 2021
e29e14c
Add changeset
fvictorio Aug 27, 2021
87cef63
Remove workaround for solidity-coverage issue
feuGeneA Sep 21, 2021
1cc3ba3
eslintrc: rm hre global for hardhat config
feuGeneA Sep 21, 2021
e38f67b
eslintrc: declutter via .npmignore
feuGeneA Sep 21, 2021
d88015c
scripts: process.exitCode=1, not process.exit(1)
feuGeneA Sep 21, 2021
91d1203
Improve handling of PRIVATE_KEY env var
feuGeneA Sep 21, 2021
6b80fd0
Merge branch 'master' into sample-ts-project
feuGeneA Sep 22, 2021
a00345c
Create khaki-actors-give.md
alcuadrado Sep 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "advanced-typescript-project",
"version": "1.0.0",
"license": "MIT",
"dependencies": {}
}
34 changes: 34 additions & 0 deletions packages/e2e/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,39 @@ describe("e2e tests", function () {
});
}
});

describe("advanced TypeScript sample project", function () {
useFixture("advanced-ts-sample-project");

before(function () {
shell.exec(`${hardhatBinary}`, {
env: {
...process.env,
HARDHAT_CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_WITH_DEFAULTS:
"true",
},
});
});

for (const suggestedCommand of [
// This list should be kept reasonably in sync with
// hardhat-core/sample-projects/advanced-ts/README.txt
`${hardhatBinary} compile`,
`${hardhatBinary} test`,
`${hardhatBinary} run scripts/deploy.ts`,
"TS_NODE_FILES=true ts-node scripts/deploy.ts",
"REPORT_GAS=true npx hardhat test",
`${hardhatBinary} coverage`,
"npx eslint '**/*.{ts,js}'",
"npx eslint '**/*.{ts,js}' --fix",
"npx prettier '**/*.{json,sol,md}' --check",
"npx solhint 'contracts/**/*.sol'",
"npx solhint 'contracts/**/*.sol' --fix",
]) {
it(`should permit successful execution of the suggested command "${suggestedCommand}"`, async function () {
shell.exec(suggestedCommand);
});
}
});
});
});
3 changes: 3 additions & 0 deletions packages/hardhat-core/recommended-gitignore.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
node_modules
.env
coverage
coverage.json
typechain

#Hardhat files
cache
Expand Down
41 changes: 41 additions & 0 deletions packages/hardhat-core/sample-projects/advanced-ts/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module.exports = {
env: {
browser: false,
es2021: true,
mocha: true,
node: true,
},
plugins: ["@typescript-eslint"],
extends: [
"standard",
"plugin:prettier/recommended",
"plugin:node/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 12,
},
overrides: [
{
files: ["scripts/**"],
rules: { "no-process-exit": "off" },
},
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved
{
files: ["hardhat.config.ts"],
globals: { hre: true },
},
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved
{
files: ["hardhat.config.ts", "scripts/**", "test/**"],
rules: {
"node/no-unpublished-import": "off",
"node/no-unpublished-require": "off",
},
},
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved
],
rules: {
"node/no-unsupported-features/es-syntax": [
"error",
{ ignores: ["modules"] },
],
},
};
46 changes: 46 additions & 0 deletions packages/hardhat-core/sample-projects/advanced-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Advanced Sample Hardhat Project

This project demonstrates an advanced Hardhat use case, integrating other tools commonly used alongside Hardhat in the ecosystem.

The project comes with a sample contract, a test for that contract, a sample script that deploys that contract, and an example of a task implementation, which simply lists the available accounts. It also comes with a variety of other tools, preconfigured to work with the project code.

Try running some of the following tasks:

```shell
npx hardhat accounts
npx hardhat compile
npx hardhat clean
npx hardhat test
npx hardhat node
npx hardhat help
REPORT_GAS=true npx hardhat test
npx hardhat coverage
npx hardhat run scripts/deploy.js
TS_NODE_FILES=true npx ts-node scripts/deploy.js
feuGeneA marked this conversation as resolved.
Show resolved Hide resolved
npx eslint '**/*.{js,ts}'
npx eslint '**/*.{js,ts}' --fix
npx prettier '**/*.{json,sol,md}' --check
npx prettier '**/*.{json,sol,md}' --write
npx solhint 'contracts/**/*.sol'
npx solhint 'contracts/**/*.sol' --fix
```

# Etherscan verification

To try out Etherscan verification, you first need to deploy a contract to an Ethereum network that's supported by Etherscan, such as Ropsten.

In this project, copy the .env.template file to a file named .env, and then edit it to fill in the details. Enter your Etherscan API key, your Ropsten node URL (eg from Alchemy), and the private key of the account which will send the deployment transaction. With a valid .env file in place, first deploy your contract:

```shell
hardhat run --network ropsten scripts/sample-script.js
feuGeneA marked this conversation as resolved.
Show resolved Hide resolved
```

Then, copy the deployment address and paste it in to replace `DEPLOYED_CONTRACT_ADDRESS` in this command:

```shell
npx hardhat verify --network ropsten DEPLOYED_CONTRACT_ADDRESS "Hello, Hardhat!"
```

# Performance optimizations

For faster runs of your tests and scripts, consider skipping ts-node's type checking by setting the environment variable `TS_NODE_TRANSPILE_ONLY` to `1` in hardhat's environment. For more details see [the documentation](https://hardhat.org/guides/typescript.html#performance-optimizations).
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as dotenv from "dotenv";

import { HardhatUserConfig, task } from "hardhat/config";
import "@nomiclabs/hardhat-etherscan";
import "@nomiclabs/hardhat-waffle";
import "@typechain/hardhat";
import "hardhat-gas-reporter";
import "solidity-coverage";

dotenv.config();

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();

for (const account of accounts) {
console.log(account.address);
}
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

const config: HardhatUserConfig = {
solidity: "0.8.4",
networks: {
hardhat: {
initialBaseFeePerGas: 0, // workaround from https://github.com/sc-forks/solidity-coverage/issues/652#issuecomment-896330136 . Remove when that issue is closed.
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved
},
ropsten: {
url: process.env.ROPSTEN_URL || "",
accounts: [process.env.PRIVATE_KEY || `0x${"0".repeat(40)}`],
alcuadrado marked this conversation as resolved.
Show resolved Hide resolved
},
},
gasReporter: {
enabled: process.env.REPORT_GAS !== undefined,
currency: "USD",
Copy link
Contributor

@cgewecke cgewecke Aug 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@feuGeneA

Hi, wrt your issue at hardhat-gas-reporter 72. Was not able to reproduce (using a valid coinmarketcap api. key). I know you tried that but ... maybe there was a copy-paste error or something?

Price data seems to be working ok for latest hardhat in this recent CI run for the plugin.

The config used for that test is:

gasReporter: {
    coinmarketcap: process.env.CMC_API_KEY
 }

[EDIT - I think the price problem is a race-condition issue that likely only affects tiny projects (like a sample). More detail about this in hh-gas-reporter 72 comment]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cgewecke, is there a way to disable this feature and just show the prices in eth? While i find it really cool, it would be nice if we don't ask the users to provide another api key in these sample projects.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes good idea,. Someone else has requested that here as well ... (cross-linking: cgewecke/eth-gas-reporter#219)

},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
};

export default config;
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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 `npx hardhat run <script>` you'll find the Hardhat
// Runtime Environment's members available in the global scope.
import { ethers } from "hardhat";

async function main() {
// Hardhat always runs the compile task when running scripts with its command
// line interface.
//
// If this script is run directly using `node` you may want to call compile
// manually to make sure everything is compiled
// await hre.run('compile');

// We get the contract to deploy
const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("Hello, Hardhat!");

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) => {
console.error(error);
process.exit(1);
});
19 changes: 19 additions & 0 deletions packages/hardhat-core/sample-projects/advanced-ts/test/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { expect } from "chai";
import { ethers } from "hardhat";

describe("Greeter", function () {
it("Should return the new greeting once it's changed", async function () {
const Greeter = await ethers.getContractFactory("Greeter");
const greeter = await Greeter.deploy("Hello, world!");
await greeter.deployed();

expect(await greeter.greet()).to.equal("Hello, world!");

const setGreetingTx = await greeter.setGreeting("Hola, mundo!");

// wait until the transaction is mined
await setGreetingTx.wait();

expect(await greeter.greet()).to.equal("Hola, mundo!");
});
});
12 changes: 12 additions & 0 deletions packages/hardhat-core/sample-projects/advanced-ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
"declaration": true
},
"include": ["./scripts", "./test", "./typechain"],
"files": ["./hardhat.config.ts"]
}
3 changes: 3 additions & 0 deletions packages/hardhat-core/src/internal/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ async function main() {
process.env.HARDHAT_CREATE_BASIC_SAMPLE_PROJECT_WITH_DEFAULTS !==
undefined ||
process.env.HARDHAT_CREATE_ADVANCED_SAMPLE_PROJECT_WITH_DEFAULTS !==
undefined ||
process.env
.HARDHAT_CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_WITH_DEFAULTS !==
undefined)
) {
await createProject();
Expand Down
54 changes: 50 additions & 4 deletions packages/hardhat-core/src/internal/cli/project-creation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import { emoji } from "./emoji";
enum Action {
CREATE_BASIC_SAMPLE_PROJECT_ACTION = "Create a basic sample project",
CREATE_ADVANCED_SAMPLE_PROJECT_ACTION = "Create an advanced sample project",
CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_ACTION = "Create an advanced sample project that uses TypeScript",
CREATE_EMPTY_HARDHAT_CONFIG_ACTION = "Create an empty hardhat.config.js",
QUIT_ACTION = "Quit",
}

type SampleProjectTypeCreationAction =
| Action.CREATE_BASIC_SAMPLE_PROJECT_ACTION
| Action.CREATE_ADVANCED_SAMPLE_PROJECT_ACTION;
| Action.CREATE_ADVANCED_SAMPLE_PROJECT_ACTION
| Action.CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_ACTION;

interface Dependencies {
[name: string]: string;
Expand Down Expand Up @@ -60,11 +62,26 @@ const ADVANCED_SAMPLE_PROJECT_DEPENDENCIES: Dependencies = {
"solidity-coverage": "^0.7.16",
};

const ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_DEPENDENCIES: Dependencies = {
...ADVANCED_SAMPLE_PROJECT_DEPENDENCIES,
"@typechain/ethers-v5": "^7.0.1",
"@typechain/hardhat": "^2.3.0",
"@typescript-eslint/eslint-plugin": "^4.29.1",
"@typescript-eslint/parser": "^4.29.1",
"@types/chai": "^4.2.21",
"@types/node": "^16.4.13",
"@types/mocha": "^9.0.0",
"ts-node": "^10.1.0",
typechain: "^5.1.2", // a workaround. see https://github.com/nomiclabs/hardhat/issues/1672#issuecomment-894497156
typescript: "^4.3.5",
};

const SAMPLE_PROJECT_DEPENDENCIES: {
[K in SampleProjectTypeCreationAction]: Dependencies;
} = {
[Action.CREATE_BASIC_SAMPLE_PROJECT_ACTION]: BASIC_SAMPLE_PROJECT_DEPENDENCIES,
[Action.CREATE_ADVANCED_SAMPLE_PROJECT_ACTION]: ADVANCED_SAMPLE_PROJECT_DEPENDENCIES,
[Action.CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_ACTION]: ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_DEPENDENCIES,
};

const TELEMETRY_CONSENT_TIMEOUT = 10000;
Expand Down Expand Up @@ -128,23 +145,42 @@ async function copySampleProject(
) {
const packageRoot = getPackageRoot();

// first copy the basic project, then, if the advanced project is what was
// requested, overlay the advanced files on top of the basic ones.
// first copy the basic project, then, if an advanced project is what was
// requested, overlay the advanced files on top of the basic ones. then, if
// the advanced TypeScript project is what was requested, overlay those files
// on top of the advanced ones.

await fsExtra.ensureDir(projectRoot);
await fsExtra.copy(
path.join(packageRoot, "sample-projects", "basic"),
projectRoot
);

if (projectType === Action.CREATE_ADVANCED_SAMPLE_PROJECT_ACTION) {
if (
projectType === Action.CREATE_ADVANCED_SAMPLE_PROJECT_ACTION ||
projectType === Action.CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_ACTION
) {
await fsExtra.copy(
path.join(packageRoot, "sample-projects", "advanced"),
projectRoot
);
await fsExtra.remove(path.join(projectRoot, "scripts", "sample-script.js"));
}

if (projectType === Action.CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_ACTION) {
await fsExtra.copy(
path.join(packageRoot, "sample-projects", "advanced-ts"),
projectRoot
);
for (const jsFile of [
"hardhat.config.js",
path.join("scripts", "deploy.js"),
path.join("test", "sample-test.js"),
]) {
await fsExtra.remove(jsFile);
}
}

// This is just in case we have been using the sample project for dev/testing
await removeTempFilesIfPresent(projectRoot);

Expand Down Expand Up @@ -203,7 +239,14 @@ async function getAction(): Promise<Action> {
undefined
) {
return Action.CREATE_ADVANCED_SAMPLE_PROJECT_ACTION;
} else if (
process.env
.HARDHAT_CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_WITH_DEFAULTS !==
undefined
) {
return Action.CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_ACTION;
}

const { default: enquirer } = await import("enquirer");
try {
const actionResponse = await enquirer.prompt<{ action: string }>([
Expand Down Expand Up @@ -291,6 +334,9 @@ export async function createProject() {
process.env.HARDHAT_CREATE_BASIC_SAMPLE_PROJECT_WITH_DEFAULTS !==
undefined ||
process.env.HARDHAT_CREATE_ADVANCED_SAMPLE_PROJECT_WITH_DEFAULTS !==
undefined ||
process.env
.HARDHAT_CREATE_ADVANCED_TYPESCRIPT_SAMPLE_PROJECT_WITH_DEFAULTS !==
undefined;

if (useDefaultPromptResponses) {
Expand Down