From 8f412beaa56d87c2be95b01ab4134a1fa18a4495 Mon Sep 17 00:00:00 2001 From: gnkz Date: Wed, 12 Apr 2023 14:09:31 -0400 Subject: [PATCH 1/5] feat: Add `toString` method to commands --- src/_modules/Commands.sol | 17 +++++++++++++++++ test/_modules/Commands.t.sol | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/src/_modules/Commands.sol b/src/_modules/Commands.sol index d74f09e4..7a90c61a 100644 --- a/src/_modules/Commands.sol +++ b/src/_modules/Commands.sol @@ -119,6 +119,22 @@ library commands { return self.args(_args.toDynamic()); } + function toString(Command memory self) internal pure returns (string memory) { + string memory output; + + uint256 length = self.inputs.length; + + for (uint256 i; i < length; ++i) { + output = string.concat(output, self.inputs[i]); + + if (i < length - 1) { + output = string.concat(output, " "); + } + } + + return output; + } + /// @dev Runs a command using the specified `Command` struct as parameters and returns the result. /// @param self The `Command` struct that holds the parameters of the command. /// @return The result of the command as a bytes array. @@ -213,6 +229,7 @@ library commands { return inputs.toDynamic().run(); } + // TODO: We probably want to move this to a different module at some point function toDynamic(string[1] memory inputs) internal pure returns (string[] memory _inputs) { _inputs = new string[](1); diff --git a/test/_modules/Commands.t.sol b/test/_modules/Commands.t.sol index 020c34b0..a4fdf660 100644 --- a/test/_modules/Commands.t.sol +++ b/test/_modules/Commands.t.sol @@ -18,4 +18,10 @@ contract CommandTest is Test { expect(string(output)).toEqual(inputs[1]); } + + function testCommandToString() external { + Command memory ping = commands.create("ping").args(["-c", "1", "nomoi.xyz"]); + + expect(ping.toString()).toEqual("ping -c 1 nomoi.xyz"); + } } From bc9a003a4dc50efbfcfef88fbfae37db2d4f0cdc Mon Sep 17 00:00:00 2001 From: gnkz Date: Wed, 12 Apr 2023 14:11:01 -0400 Subject: [PATCH 2/5] chore: fmt --- src/_modules/Commands.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/_modules/Commands.sol b/src/_modules/Commands.sol index 7a90c61a..9fe67612 100644 --- a/src/_modules/Commands.sol +++ b/src/_modules/Commands.sol @@ -229,7 +229,6 @@ library commands { return inputs.toDynamic().run(); } - // TODO: We probably want to move this to a different module at some point function toDynamic(string[1] memory inputs) internal pure returns (string[] memory _inputs) { _inputs = new string[](1); From 9d6474820bd7ff8b0c94b648e88a58c82e3627f8 Mon Sep 17 00:00:00 2001 From: gnkz Date: Mon, 5 Jun 2023 12:56:18 -0400 Subject: [PATCH 3/5] fix: use an address >= 10 for setCode test --- test/_modules/Accounts.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/_modules/Accounts.t.sol b/test/_modules/Accounts.t.sol index 6b9b4d6c..93f19311 100644 --- a/test/_modules/Accounts.t.sol +++ b/test/_modules/Accounts.t.sol @@ -28,7 +28,7 @@ contract AccountsTest is Test { } function testItCanSetTheCode() external { - address addr = address(1); + address addr = address(1337); Sender sender = new Sender(); From 7ac22d51ab3af2ff316c28eab743c4632e2e0da5 Mon Sep 17 00:00:00 2001 From: gnkz Date: Mon, 5 Jun 2023 15:09:44 -0400 Subject: [PATCH 4/5] chore: add docs to `Command.toString` function --- src/_modules/Commands.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/_modules/Commands.sol b/src/_modules/Commands.sol index ab0efbe9..577457ed 100644 --- a/src/_modules/Commands.sol +++ b/src/_modules/Commands.sol @@ -119,6 +119,9 @@ library commands { return self.args(_toDynamic(_args)); } + /// @dev Transforms a command to its string representation. + /// @param self The command struct that will be transformed to a string. + /// @return The string representation of the command. function toString(Command memory self) internal pure returns (string memory) { string memory output; From 56c39c17d771a478577acee26903fe5234465dca Mon Sep 17 00:00:00 2001 From: Vicente Dragicevic Date: Wed, 7 Jun 2023 17:37:19 -0400 Subject: [PATCH 5/5] Add missing fmt and utils modules --- docs/book.toml | 6 +++--- docs/src/README.md | 3 --- docs/src/SUMMARY.md | 3 +++ docs/src/guide/installation.md | 2 +- docs/src/modules/fmt.md | 24 ++++++++++++++++++++++ docs/src/modules/strings.md | 4 ++++ docs/src/modules/utils.md | 20 ++++++++++++++++++ docs/src/reference/modules/fmt.md | 29 +++++++++++++++++++++++++++ docs/src/reference/modules/strings.md | 18 ++++++++++------- 9 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 docs/src/modules/fmt.md create mode 100644 docs/src/modules/utils.md create mode 100644 docs/src/reference/modules/fmt.md diff --git a/docs/book.toml b/docs/book.toml index 58d2a283..4913a6f7 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -1,11 +1,11 @@ [book] -authors = ['nomoi.xyz'] src = 'src' -title = 'Vulcan Book' +title = '' [output.html] +no-section-label = true additional-js = ['solidity.min.js'] additional-css = ['book.css'] -git-repository-url = 'https://github.com/nomoixyz/vulcan' +git-repository-url = 'https://github.com/foundry-rs/foundry' [output.html.fold] enable = true diff --git a/docs/src/README.md b/docs/src/README.md index facf2273..0d9b4ff2 100644 --- a/docs/src/README.md +++ b/docs/src/README.md @@ -10,9 +10,6 @@ Initially, Vulcan will provide functionality similar to what is already included Over time, Vulcan will grow to include more functionality and utilities, eventually becoming a feature-rich development framework. -> **Warning** -> This library should be treated as highly experimental, its API WILL change, and there might be bugs in it. Don't use in production yet. - ## Why Vulcan? Our goal is to provide: diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 53c5111e..d3e79c2f 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -14,11 +14,13 @@ - [Context](./modules/context.md) - [Env](./modules/env.md) - [Events](./modules/events.md) + - [Format](./modules/fmt.md) - [Forks](./modules/forks.md) - [Fs](./modules/fs.md) - [Huff](./modules/huff.md) - [Json](./modules/json.md) - [Strings](./modules/strings.md) + - [Utils](./modules/utils.md) - [Watchers](./modules/watchers.md) # Reference @@ -30,6 +32,7 @@ - [Context](./reference/modules/context.md) - [Env](./reference/modules/env.md) - [Events](./reference/modules/events.md) + - [Format](./reference/modules/fmt.md) - [Forks](./reference/modules/forks.md) - [Fs](./reference/modules/fs.md) - [Huff](./reference/modules/huff.md) diff --git a/docs/src/guide/installation.md b/docs/src/guide/installation.md index 3897762f..7e930118 100644 --- a/docs/src/guide/installation.md +++ b/docs/src/guide/installation.md @@ -2,5 +2,5 @@ In an existing Foundry project, use `forge install`: ``` -$ forge install nomoixyz/vulcan@alpha-1 +$ forge install nomoixyz/vulcan@0.1.0 ``` diff --git a/docs/src/modules/fmt.md b/docs/src/modules/fmt.md new file mode 100644 index 00000000..ebfb5ebf --- /dev/null +++ b/docs/src/modules/fmt.md @@ -0,0 +1,24 @@ +# Format + +The format function defined under the `fmt` module enables you to format strings dynamically by using a template string and the ABI encoded arguments: + +```solidity +import {Test, expect, console, fmt} from "vulcan/test.sol"; + +contract TestMyContract is Test { + function testFormat() external { + string memory template = "{address} hello {string} world {bool}"; + + // You can also use abbreviations: "{a} hello {s} world {b}"; + template = "{a} hello {s} world {b}"; + + string memory result = fmt.format(template, abi.encode(address(123), "foo", true)); + expect(result).toEqual("0x000000000000000000000000000000000000007B hello foo world true"); + + // For numerical values, you can even specify the number of decimals to format with + expect(fmt.format("{uint:d18}", abi.encode(1e17))).toEqual("0.1"); + } +} +``` + +This example demonstrates the use of a string template with placeholders and a custom formatting function to generate a formatted string. It is a simple and efficient way to manage dynamic values on strings. \ No newline at end of file diff --git a/docs/src/modules/strings.md b/docs/src/modules/strings.md index c62ebfb3..09d71a20 100644 --- a/docs/src/modules/strings.md +++ b/docs/src/modules/strings.md @@ -27,6 +27,10 @@ contract TestMyContract is Test { // Parse an address string to an `address` expect("0x13DFD56424777BAC80070a98Cf83DD82246c2bC0".parseAddress()).toEqual(0x13DFD56424777BAC80070a98Cf83DD82246c2bC0); + + // Format - see the Format module for more details + uint256 amount = 1e17; + expect("Token amount: {u:d18}".format(abi.encode(amount))).toEqual("Token amount: 0.1"); } } ``` diff --git a/docs/src/modules/utils.md b/docs/src/modules/utils.md new file mode 100644 index 00000000..e987813e --- /dev/null +++ b/docs/src/modules/utils.md @@ -0,0 +1,20 @@ +# Utils + +This module provides a set of utility functions that make use of other modules in Vulcan. + +```solidity +import {Test, expect, println, format} from "vulcan/test.sol"; + +contract TestMyContract is Test { + function testUtils() external { + // Print a string + println("Hello world!"); + + // Print a formatted string - see Format module for more details + println("Hello {string}!", abi.encode("world")); + + // Format a string + expect(format("Hello {string}!", abi.encode("world"))).toEqual("Hello world!"); + } +} +``` \ No newline at end of file diff --git a/docs/src/reference/modules/fmt.md b/docs/src/reference/modules/fmt.md new file mode 100644 index 00000000..6e29bd48 --- /dev/null +++ b/docs/src/reference/modules/fmt.md @@ -0,0 +1,29 @@ +# Format + +#### **`format(string template, bytes args) → (string)`** + +## Template Reference + +The template is a string that can contain placeholders for the arguments. The placeholders are defined by curly braces `{}` and can be of the following types: + +- **Address**: `{address}` or `{a}` +- **Bytes32**: `{bytes32}` or `{b32}` +- **String**: `{string}` or `{s}` +- **Bytes**: `{bytes}` or `{b}` +- **Uint**: `{uint}` or `{u}` +- **Int**: `{int}` or `{i}` +- **Boolean**: `{bool}` + +### Modifiers + +Some placeholder types can be followed by a colon `:` and a format specifier. The format specifier is a single character that defines how the argument should be formatted, and can also contain additional arguments. The following format specifiers are supported: + +#### Decimals (`dX`) + +Supported types: `uint`, `int` + +Formats the provided value to a string representation that uses the specified amount of decimals. One of the main use cases is to format token amounts. + +Examples: +- `{uint:d18}` formats the value `1e18` to `"1.0"` +- `{uint:d2}` formats the value `10` to `"0.1"` diff --git a/docs/src/reference/modules/strings.md b/docs/src/reference/modules/strings.md index 9e762708..2672144d 100644 --- a/docs/src/reference/modules/strings.md +++ b/docs/src/reference/modules/strings.md @@ -1,30 +1,34 @@ # Strings -#### **`toString(address value) → (string )`** +#### **`format(string template, bytes args) public → (string)`** + +See [**Format API reference**](./fmt.md) for more details. + +#### **`toString(address value) → (string)`** Transforms an address to a string. -#### **`toString(bytes value) → (string )`** +#### **`toString(bytes value) → (string)`** Transforms a byte array to a string. -#### **`toString(bytes32 value) → (string )`** +#### **`toString(bytes32 value) → (string)`** Transforms a bytes32 to a string. -#### **`toString(bool value) → (string )`** +#### **`toString(bool value) → (string)`** Transforms a boolean to a string. -#### **`toString(uint256 value) → (string )`** +#### **`toString(uint256 value) → (string)`** Transforms an uint256 to a string. -#### **`toString(int256 value) → (string )`** +#### **`toString(int256 value) → (string)`** Transforms an int256 to a string. -#### **`parseBytes(string value) → (bytes )`** +#### **`parseBytes(string value) → (bytes)`** Parses a byte array string.