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 Fe support #106

Merged
merged 12 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 6 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ jobs:
with:
version: nightly

- name: Install Fe
run: |
curl -L https://github.com/ethereum/fe/releases/download/v0.24.0/fe_amd64 -o /usr/local/bin/fe -s
chmod +x /usr/local/bin/fe

- name: Run Forge build
run: |
forge --version
Expand All @@ -52,4 +57,4 @@ jobs:
run: forge --version

- name: Check formatting
run: forge fmt --check
run: forge fmt --check
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ out/
.env

.DS_Store

# Test stuff
test/fixtures/fe
23 changes: 23 additions & 0 deletions docs/src/modules/fe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Fe

Provides [Fe](https://fe-lang.org/) compiler support. The `ffi` setting must be enabled on `foundry.toml` for this module
to work.

```solidity
import { Test, fe } from "vulcan/test.sol";

contract TestMyContract is Test {
function testCompile() external {
fe.create().setFilePath("./test/mocks/guest_book.fe").setOutputDir("./test/fixtures/fe/output").setOverwrite(
true
).build();

string memory result = fs.readFile("./test/fixtures/fe/output/GuestBook/GuestBook.bin");

expect(bytes(result).length).toBeGreaterThan(0);
}

}
```
[**Fe API reference**](../reference/modules/fe.md)

47 changes: 47 additions & 0 deletions docs/src/reference/fe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Fe

#### **`create() → (Fe)`**

Creates a new `Fe` struct with the following defaults.

```solidity
Fe({
compilerPath: "fe",
filePath: "",
emitOptions: "",
outputDir: "",
overwrite: false
});
```

> Notice: The `filePath` is the only required field, if it is empty, `.build` and `.toCommand`
> will revert.

#### **`build(Fe self) → (bytes)`**

Builds a binary file from a `.fe` file.

#### **`toCommand(Fe self) → (Command)`**

Converts the `Fe` struct into a `Command` struct.

#### **`setCompilerPath(Fe self, string compilerPath) → (Fe)`**

Overwrites the compiler path.

#### **`setFilePath(Fe self, string filePath) → (Fe)`**

Overwrites the file path.

#### **`setOutputDir(Fe self, string outputDir) → (Fe)`**

Overwrites the default artifacts directory.

#### **`setEmitOptions(Fe memory self, string memory emitOptions) → (Fe)`**

Overwrites the default emit options. `abi,bytecode` is the default value. Use `fe build --help` to
get all the other possible values.

#### **`setOverwrite(Fe memory self, bool overwrite) → (Fe)`**

Sets the build command overwrite flag. If `true` the contents of `outputDir` will be overwritten.
3 changes: 2 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ ffi = true
fs_permissions = [
{ access = "read", path = "./out"},
{ access = "read", path = "./test/fixtures/fs/read" },
{ access = "read-write", path = "./test/fixtures/fs/write" }
{ access = "read-write", path = "./test/fixtures/fs/write" },
{ access = "read-write", path = "./test/fixtures/fe/output" }
]

[rpc_endpoints]
Expand Down
90 changes: 90 additions & 0 deletions src/_modules/Fe.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.13 <0.9.0;

import "./Commands.sol";
import "./Strings.sol";

struct Fe {
string compilerPath;
string filePath;
string emitOptions;
string outputDir;
bool overwrite;
}

library fe {
using strings for bytes32;

/// @dev Creates a new `Fe` struct with default values.
function create() internal pure returns (Fe memory) {
return Fe({compilerPath: "fe", filePath: "", emitOptions: "", outputDir: "", overwrite: false});
}

/// @dev Builds a binary file from a `.fe` file.
/// @param self The `Fe` struct to build.
function build(Fe memory self) internal returns (bytes memory) {
bytes memory output = self.toCommand().run();
// TODO: add error handling
return output;
}

/// @dev Transforms a `Fe` struct to a `Command` struct.
/// @param self The `Fe` struct to transform.
function toCommand(Fe memory self) internal pure returns (Command memory) {
Command memory command = commands.create(self.compilerPath);
require(bytes(self.filePath).length > 0, "fe.toCommand: self.filePath not set");

// MUST be last
command = command.arg("build");

if (bytes(self.outputDir).length > 0) command = command.args(["-o", self.outputDir]);
if (bytes(self.emitOptions).length > 0) command = command.args(["-e", self.emitOptions]);
if (self.overwrite) command = command.arg("--overwrite");

command = command.arg(self.filePath);

return command;
}

/// @dev Sets the `fe` compiler path.
/// @param self The `Fe` struct to modify.
/// @param compilerPath The new compiler path.
function setCompilerPath(Fe memory self, string memory compilerPath) internal pure returns (Fe memory) {
self.compilerPath = compilerPath;
return self;
}

/// @dev Sets the `fe` file path to build.
/// @param self The `Fe` struct to modify.
/// @param filePath The path to the `.fe` file to build.
function setFilePath(Fe memory self, string memory filePath) internal pure returns (Fe memory) {
self.filePath = filePath;
return self;
}

/// @dev Sets the `fe` build command emit options.
/// @param self The `Fe` struct to modify.
/// @param emitOptions The build command emit options.
function setEmitOptions(Fe memory self, string memory emitOptions) internal pure returns (Fe memory) {
self.emitOptions = emitOptions;
return self;
}

/// @dev Sets the `fe` build command output directory.
/// @param self The `Fe` struct to modify.
/// @param outputDir The directory where the binary file will be saved.
function setOutputDir(Fe memory self, string memory outputDir) internal pure returns (Fe memory) {
self.outputDir = outputDir;
return self;
}

/// @dev Sets the `fe` build command overwrite flag.
/// @param self The `Fe` struct to modify.
/// @param overwrite If true it will overwrite the `outputDir with the new build binaries.
function setOverwrite(Fe memory self, bool overwrite) internal pure returns (Fe memory) {
self.overwrite = overwrite;
return self;
}
}

using fe for Fe global;
1 change: 1 addition & 0 deletions src/script.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {config, Rpc} from "./_modules/Config.sol";
import {fmt} from "./_modules/Fmt.sol";
import {format} from "./_utils/format.sol";
import {println} from "./_utils/println.sol";
import {fe, Fe} from "./_modules/Fe.sol";

contract Script {
bool public IS_SCRIPT = true;
Expand Down
1 change: 1 addition & 0 deletions src/test.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {strings} from "./_modules/Strings.sol";
import {watchers, Watcher} from "./_modules/Watchers.sol";
import {config, Rpc} from "./_modules/Config.sol";
import {fmt} from "./_modules/Fmt.sol";
import {fe, Fe} from "./_modules/Fe.sol";
import {format} from "./_utils/format.sol";
import {println} from "./_utils/println.sol";

Expand Down
39 changes: 39 additions & 0 deletions test/_modules/Fe.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
pragma solidity >=0.8.13 <0.9.0;

import {Test, expect, commands, Command, fe, Fe, fs} from "../../src/test.sol";

contract FeTest is Test {
function testToCommandAllSet() external {
Command memory command = fe.create().setCompilerPath("difffe").setFilePath("./filePath.fe").setEmitOptions(
"abi"
).setOutputDir("./feoutput").toCommand();

expect(command.inputs.length).toEqual(7);
expect(command.inputs[0]).toEqual("difffe");
expect(command.inputs[1]).toEqual("build");
expect(command.inputs[2]).toEqual("-o");
expect(command.inputs[3]).toEqual("./feoutput");
expect(command.inputs[4]).toEqual("-e");
expect(command.inputs[5]).toEqual("abi");
expect(command.inputs[6]).toEqual("./filePath.fe");
}

function testToCommandMinimumSet() external {
Command memory command = fe.create().setFilePath("./filePath.fe").toCommand();

expect(command.inputs.length).toEqual(3);
expect(command.inputs[0]).toEqual("fe");
expect(command.inputs[1]).toEqual("build");
expect(command.inputs[2]).toEqual("./filePath.fe");
}

function testCompile() external {
fe.create().setFilePath("./test/mocks/guest_book.fe").setOutputDir("./test/fixtures/fe/output").setOverwrite(
true
).build();

string memory result = fs.readFile("./test/fixtures/fe/output/GuestBook/GuestBook.bin");

expect(bytes(result).length).toBeGreaterThan(0);
}
}
11 changes: 11 additions & 0 deletions test/mocks/guest_book.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
contract GuestBook {
messages: Map<address, String<100>>

pub fn sign(mut self, ctx: Context, book_msg: String<100>) {
self.messages[ctx.msg_sender()] = book_msg
}

pub fn get_msg(self, addr: address) -> String<100> {
return self.messages[addr].to_mem()
}
}