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

Support Settings (a.k.a. Input JSON Description) in @nomiclabs/hardhat-vyper #4872

Merged
merged 14 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/fluffy-cups-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nomiclabs/hardhat-vyper": patch
---

Added support for vyper settings 'evmVersion' and 'optimize'
15 changes: 13 additions & 2 deletions packages/hardhat-vyper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,23 @@ module.exports = {
};
```

You can also configure multiple versions of the Vyper compiler:
You can also configure multiple versions of the Vyper compiler, as well as the compiler settings evmVersion (available from Vyper 0.3.8) and optimize (available from Vyper 0.3.10)

```js
module.exports = {
vyper: {
compilers: [{ version: "0.2.1" }, { version: "0.3.0" }],
compilers: [
{
version: "0.2.1",
},
{
version: "0.3.10",
settings: {
evmVersion: "paris",
optimize: "gas",
},
},
],
},
};
```
Expand Down
16 changes: 14 additions & 2 deletions packages/hardhat-vyper/src/compiler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { exec } from "child_process";
import { VyperSettings } from "./types";

export class Compiler {
constructor(private _pathToVyper: string) {}
Expand All @@ -7,10 +8,21 @@ export class Compiler {
*
* @param inputPaths array of paths to contracts to be compiled
Copy link
Member

Choose a reason for hiding this comment

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

We should add the new parameters.

*/
public async compile(inputPaths: string[]) {
public async compile(inputPaths: string[], settings: VyperSettings = {}) {
const output: string = await new Promise((resolve, reject) => {
let settingsStr =
settings.evmVersion !== undefined
? `--evm-version ${settings.evmVersion} `
: "";
settingsStr +=
settings.optimize !== undefined
? `--optimize ${String(settings.optimize)} `
ChristopherDedominici marked this conversation as resolved.
Show resolved Hide resolved
: "";

const process = exec(
`${this._pathToVyper} -f combined_json ${inputPaths.join(" ")}`,
`${this._pathToVyper} ${settingsStr} -f combined_json ${inputPaths.join(
" "
)}`,
{
maxBuffer: 1024 * 1024 * 500,
},
Expand Down
32 changes: 26 additions & 6 deletions packages/hardhat-vyper/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Artifacts as ArtifactsImpl } from "hardhat/internal/artifacts";
import type { Artifacts } from "hardhat/types/artifacts";
import type { VyperFilesCache as VyperFilesCacheT } from "./cache";
import type { VyperOutput, VyperBuild } from "./types";
import type { VyperOutput, VyperBuild, VyperSettings } from "./types";
import type { ResolvedFile } from "./resolver";

import * as os from "os";
Expand Down Expand Up @@ -188,13 +188,18 @@ subtask(TASK_COMPILE_VYPER_RUN_BINARY)
async ({
inputPaths,
vyperPath,
settings,
}: {
inputPaths: string[];
vyperPath: string;
settings?: VyperSettings;
}): Promise<VyperOutput> => {
const compiler = new Compiler(vyperPath);

const { version, ...contracts } = await compiler.compile(inputPaths);
const { version, ...contracts } = await compiler.compile(
inputPaths,
settings
);

return {
version,
Expand Down Expand Up @@ -249,14 +254,24 @@ subtask(TASK_COMPILE_VYPER)
({ version }) => version
);

const versionsToSettings = Object.fromEntries(
config.vyper.compilers.map(({ version, settings }) => [
version,
settings,
])
);

const versionGroups: Record<string, ResolvedFile[]> = {};
const unmatchedFiles: ResolvedFile[] = [];

for (const file of resolvedFiles) {
const hasChanged = vyperFilesCache.hasFileChanged(
file.absolutePath,
file.contentHash,
{ version: file.content.versionPragma }
{
version: file.content.versionPragma,
settings: versionsToSettings[file.content.versionPragma] ?? {},
ChristopherDedominici marked this conversation as resolved.
Show resolved Hide resolved
}
);

if (!hasChanged) continue;
Expand All @@ -266,8 +281,7 @@ subtask(TASK_COMPILE_VYPER)
file.content.versionPragma
);

// check if there are files that don't match any configured compiler
// version
// check if there are files that don't match any configured compiler version
if (maxSatisfyingVersion === null) {
unmatchedFiles.push(file);
continue;
Expand Down Expand Up @@ -298,6 +312,8 @@ ${list}`
}

for (const [vyperVersion, files] of Object.entries(versionGroups)) {
const settings = versionsToSettings[vyperVersion] ?? {};

const vyperBuild: VyperBuild = await run(TASK_COMPILE_VYPER_GET_BUILD, {
quiet,
vyperVersion,
Expand All @@ -312,6 +328,7 @@ ${list}`
{
inputPaths: files.map(({ absolutePath }) => absolutePath),
vyperPath: vyperBuild.compilerPath,
settings,
}
);

Expand All @@ -329,7 +346,10 @@ ${list}`
lastModificationDate: file.lastModificationDate.valueOf(),
contentHash: file.contentHash,
sourceName: file.sourceName,
vyperConfig: { version },
vyperConfig: {
version,
settings,
},
versionPragma: file.content.versionPragma,
artifacts: [artifact.contractName],
});
Expand Down
6 changes: 6 additions & 0 deletions packages/hardhat-vyper/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
export type VyperUserConfig = string | VyperConfig | MultiVyperConfig;

export interface VyperSettings {
evmVersion?: string;
optimize?: "gas" | "codesize" | "none";
ChristopherDedominici marked this conversation as resolved.
Show resolved Hide resolved
}

export interface VyperConfig {
version: string;
settings?: VyperSettings;
}

export interface MultiVyperConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @version 0.3.7

@external
def test() -> int128:
return 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require("../../../src/index");

module.exports = {
vyper: {
compilers: [
{
version: "0.3.7",
settings: {
evmVersion: "paris",
optimize: "gas",
},
},
],
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @version 0.3.8

@external
def test() -> int128:
return 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @version 0.3.10

@external
def test() -> int128:
return 42
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
require("../../../src/index");

module.exports = {
vyper: {
compilers: [
{
version: "0.3.10",
settings: {
evmVersion: "paris",
optimize: "gas",
},
},
{
version: "0.3.8",
settings: {
evmVersion: "shanghai",
},
},
],
},
};
81 changes: 81 additions & 0 deletions packages/hardhat-vyper/test/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path from "path";

import { TASK_COMPILE } from "hardhat/builtin-tasks/task-names";

import fs from "node:fs";
import { VYPER_FILES_CACHE_FILENAME } from "../src/constants";
import {
useEnvironment,
Expand Down Expand Up @@ -44,6 +45,86 @@ describe("Vyper plugin", function () {
});
});

describe("vyper settings", function () {
describe("compilation with different settings", function () {
useFixtureProject("compilation-with-vyper-settings");
useEnvironment();

it("should compile and emit artifacts", async function () {
await this.env.run(TASK_COMPILE);

assertFileExists(path.join("artifacts", "contracts", "A.vy", "A.json"));
assertFileExists(path.join("artifacts", "contracts", "B.vy", "B.json"));
});
});

describe("compilation with wrong settings", function () {
useFixtureProject("compilation-with-vyper-settings-failure");
useEnvironment();

it("should fail the compilation, invalid settings", async function () {
// compiler version is set to 0.3.7, which does not support the settings 'evmVersion' and 'optimize'
await expect(this.env.run(TASK_COMPILE)).to.be.rejectedWith(
Error,
/--evm-version paris --optimize gas/
);
});
});
});

describe("caching mechanism", function () {
describe("caching mechanism without vyper settings", function () {
useFixtureProject("compilation-single-file");
useEnvironment();

it("should not re-compile the contract because of the cache", async function () {
await this.env.run(TASK_COMPILE);

const stats1 = fs.statSync(
path.join("artifacts", "contracts", "A.vy", "A.json")
);

// it should not compile again so the contract should not be modified
await this.env.run(TASK_COMPILE);

const stats2 = fs.statSync(
path.join("artifacts", "contracts", "A.vy", "A.json")
);

assert.equal(stats1.mtimeMs, stats2.mtimeMs);
});
});

describe("caching mechanism with vyper settings", function () {
useFixtureProject("compilation-with-vyper-settings");
useEnvironment();

it("should not re-compile the contract because of the cache", async function () {
await this.env.run(TASK_COMPILE);

const stats1A = fs.statSync(
path.join("artifacts", "contracts", "A.vy", "A.json")
);
const stats1B = fs.statSync(
path.join("artifacts", "contracts", "B.vy", "B.json")
);

// it should not compile again so the contracts should not be modified
await this.env.run(TASK_COMPILE);

const stats2A = fs.statSync(
path.join("artifacts", "contracts", "A.vy", "A.json")
);
const stats2B = fs.statSync(
path.join("artifacts", "contracts", "B.vy", "B.json")
);

assert.equal(stats1A.mtimeMs, stats2A.mtimeMs);
assert.equal(stats1B.mtimeMs, stats2B.mtimeMs);
});
});
});

describe("old versions of vyper", function () {
useFixtureProject("old-vyper-versions");

Expand Down
Loading