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

build: use simultaneously compilation #342

Closed
wants to merge 3 commits into from
Closed
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
3 changes: 0 additions & 3 deletions .github/workflows/ci-deep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ jobs:
uses: "sablier-labs/reusable-workflows/.github/workflows/forge-test.yml@main"
with:
foundry-fuzz-runs: ${{ fromJSON(inputs.integrationFuzzRuns || '100000') }}
foundry-profile: "test-optimized"
match-path: "tests/integration/**/*.sol"
name: "Integration tests"

Expand All @@ -47,7 +46,6 @@ jobs:
with:
foundry-invariant-depth: ${{ fromJSON(inputs.invariantDepth || '200') }}
foundry-invariant-runs: ${{ fromJSON(inputs.invariantRuns || '50000') }}
foundry-profile: "test-optimized"
match-path: "tests/invariant/**/*.sol"
name: "Invariant tests"

Expand All @@ -58,7 +56,6 @@ jobs:
uses: "sablier-labs/reusable-workflows/.github/workflows/forge-test.yml@main"
with:
foundry-fuzz-runs: ${{ fromJSON(inputs.forkFuzzRuns || '20') }}
foundry-profile: "test-optimized"
match-path: "tests/fork/**/*.sol"
name: "Fork tests"

Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ jobs:
uses: "sablier-labs/reusable-workflows/.github/workflows/forge-test.yml@main"
with:
foundry-fuzz-runs: 10000
foundry-profile: "test-optimized"
match-path: "tests/integration/**/*.sol"
name: "Integration tests"

Expand All @@ -40,7 +39,6 @@ jobs:
with:
foundry-invariant-depth: 100
foundry-invariant-runs: 1000
foundry-profile: "test-optimized"
match-path: "tests/invariant/**/*.sol"
name: "Invariant tests"

Expand All @@ -51,7 +49,6 @@ jobs:
uses: "sablier-labs/reusable-workflows/.github/workflows/forge-test.yml@main"
with:
foundry-fuzz-runs: 20
foundry-profile: "test-optimized"
match-path: "tests/fork/**/*.sol"
name: "Fork tests"

Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ coverage
docs
node_modules
out
out-optimized
out-svg

# files
*.env
Expand Down
2 changes: 0 additions & 2 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ coverage
docs
node_modules
out
out-optimized
out-svg

# files
*.env
Expand Down
26 changes: 13 additions & 13 deletions benchmark/results/SablierFlow.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

| Function | Gas Usage |
| ----------------------------- | --------- |
| `adjustRatePerSecond` | 44193 |
| `create` | 113703 |
| `deposit` | 32997 |
| `depositViaBroker` | 22754 |
| `pause` | 7544 |
| `refund` | 22842 |
| `refundMax` | 23840 |
| `restart` | 7058 |
| `void (solvent stream)` | 9982 |
| `void (insolvent stream)` | 37482 |
| `withdraw (insolvent stream)` | 57711 |
| `withdraw (solvent stream)` | 38178 |
| `withdrawMax` | 52010 |
| `adjustRatePerSecond` | 45796 |
| `create` | 113271 |
| `deposit` | 31738 |
| `depositViaBroker` | 20634 |
| `pause` | 7220 |
| `refund` | 22059 |
| `refundMax` | 23128 |
| `restart` | 6660 |
| `void (solvent stream)` | 9658 |
| `void (insolvent stream)` | 37158 |
| `withdraw (insolvent stream)` | 56990 |
| `withdraw (solvent stream)` | 37457 |
| `withdrawMax` | 51364 |
29 changes: 14 additions & 15 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,26 @@
bytecode_hash = "none"
evm_version = "shanghai"
fs_permissions = [
{ access = "read", path = "./out-optimized" },
{ access = "read", path = "./out" },
{ access = "read", path = "package.json" },
{ access = "read-write", path = "./benchmark/results"},
{ access = "read-write", path = "./script"}
]
gas_limit = 9223372036854775807
gas_reports = ["SablierFlow"]
optimizer = true
optimizer_runs = 1000
optimizer_runs = 10_000
out = "out"
script = "script"
sender = "0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38"
solc = "0.8.26"
src = "src"
test = "tests"
via_ir = true

[profile.default.fuzz]
max_test_rejects = 1_000_000 # Number of times `vm.assume` can fail
runs = 10000
runs = 10_000

[profile.default.invariant]
call_override = false # Override unsafe external calls to perform reentrancy checks
Expand All @@ -31,13 +32,23 @@
runs = 1000
shrink_run_limit = 0 # Disable shrinking of a failed sequence

additional_compiler_profiles = [
{ name = "tests/", via_ir = false }
]

compilation_restrictions = [
{ paths = "tests/mocks", via_ir = true },
{ paths = "tests/**", via_ir = false }
]

# Run only the code inside benchmark directory
[profile.benchmark]
test = "benchmark"

# Speed up compilation and tests during development
[profile.lite]
optimizer = false
via_ir = false

[profile.lite.invariant]
depth = 50
Expand All @@ -46,18 +57,6 @@
[profile.lite.fuzz]
runs = 20

# Compile only the production code and the test mocks with via IR and 10,000 optimizer runs
[profile.optimized]
optimizer = true
optimizer_runs = 10_000
out = "out-optimized"
test = "tests/mocks"
via_ir = true

# Test the optimized contracts without re-compiling them
[profile.test-optimized]
src = "tests"

[doc]
ignore = ["**/*.t.sol"]
out = "docs"
Expand Down
8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,9 @@
"access": "restricted"
},
"scripts": {
"benchmark": "bun run build:optimized && FOUNDRY_PROFILE=benchmark forge test --mt testGas && bun run prettier:write",
"benchmark": "bun run build && FOUNDRY_PROFILE=benchmark forge test --mt testGas && bun run prettier:write",
"build": "forge build",
"build:optimized": "FOUNDRY_PROFILE=optimized forge build",
"clean": "rm -rf artifacts broadcast cache docs out out-optimized out-svg",
"clean": "rm -rf artifacts broadcast cache docs out",
"lint": "bun run lint:sol && bun run prettier:check",
"lint:fix": "bun run lint:sol:fix && forge fmt",
"lint:sol": "forge fmt --check && bun solhint \"{benchmark,precompiles,script,src,tests}/**/*.sol\"",
Expand All @@ -70,7 +69,6 @@
"prettier:check": "prettier --check \"**/*.{json,md,svg,yml}\"",
"prettier:write": "prettier --write \"**/*.{json,md,svg,yml}\"",
"test": "forge test",
"test:lite": "FOUNDRY_PROFILE=lite forge test --nmt \"testFork\"",
"test:optimized": "bun run build:optimized && FOUNDRY_PROFILE=test-optimized forge test"
"test:lite": "FOUNDRY_PROFILE=lite forge test --nmt \"testFork\" --nmc \"Precompiles_Test\""
}
}
4 changes: 2 additions & 2 deletions precompiles/Precompiles.sol

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions shell/prepare-artifacts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,26 @@ mkdir $artifacts \
"$artifacts/libraries"

# Generate the artifacts with Forge
FOUNDRY_PROFILE=optimized forge build
forge build

# Copy the production artifacts
cp out-optimized/SablierFlow.sol/SablierFlow.json $artifacts
cp out-optimized/FlowNFTDescriptor.sol/FlowNFTDescriptor.json $artifacts
cp out/SablierFlow.sol/SablierFlow.json $artifacts
cp out/FlowNFTDescriptor.sol/FlowNFTDescriptor.json $artifacts

interfaces=./artifacts/interfaces
cp out-optimized/ISablierFlow.sol/ISablierFlow.json $interfaces
cp out-optimized/ISablierFlowBase.sol/ISablierFlowBase.json $interfaces
cp out-optimized/IFlowNFTDescriptor.sol/IFlowNFTDescriptor.json $interfaces
cp out/ISablierFlow.sol/ISablierFlow.json $interfaces
cp out/ISablierFlowBase.sol/ISablierFlowBase.json $interfaces
cp out/IFlowNFTDescriptor.sol/IFlowNFTDescriptor.json $interfaces

erc20=./artifacts/interfaces/erc20
cp out-optimized/IERC20.sol/IERC20.json $erc20
cp out/IERC20.sol/IERC20.json $erc20

erc721=./artifacts/interfaces/erc721
cp out-optimized/IERC721.sol/IERC721.json $erc721
cp out-optimized/IERC721Metadata.sol/IERC721Metadata.json $erc721
cp out/IERC721.sol/IERC721.json $erc721
cp out/IERC721Metadata.sol/IERC721Metadata.json $erc721

libraries=./artifacts/libraries
cp out-optimized/Errors.sol/Errors.json $libraries
cp out/Errors.sol/Errors.json $libraries

# Format the artifacts with Prettier
bun prettier --write ./artifacts
6 changes: 3 additions & 3 deletions shell/update-precompiles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
set -euo pipefail

# Compile the contracts with Forge
FOUNDRY_PROFILE=optimized forge build
forge build

# Retrieve the raw bytecodes, removing the "0x" prefix
flow=$(cat out-optimized/SablierFlow.sol/SablierFlow.json | jq -r '.bytecode.object' | cut -c 3-)
nft_descriptor=$(cat out-optimized/FlowNFTDescriptor.sol/FlowNFTDescriptor.json | jq -r '.bytecode.object' | cut -c 3-)
flow=$(cat out/SablierFlow.sol/SablierFlow.json | jq -r '.bytecode.object' | cut -c 3-)
nft_descriptor=$(cat out/FlowNFTDescriptor.sol/FlowNFTDescriptor.json | jq -r '.bytecode.object' | cut -c 3-)

precompiles_path="precompiles/Precompiles.sol"
if [ ! -f $precompiles_path ]; then
Expand Down
28 changes: 12 additions & 16 deletions tests/Base.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pragma solidity >=0.8.22;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Test } from "forge-std/src/Test.sol";
import { FlowNFTDescriptor } from "src/FlowNFTDescriptor.sol";
import { IFlowNFTDescriptor } from "src/interfaces/IFlowNFTDescriptor.sol";
import { ISablierFlow } from "src/interfaces/ISablierFlow.sol";
import { SablierFlow } from "src/SablierFlow.sol";
import { ERC20MissingReturn } from "./mocks/ERC20MissingReturn.sol";
import { ERC20Mock } from "./mocks/ERC20Mock.sol";
Expand All @@ -30,8 +32,8 @@ abstract contract Base_Test is Assertions, Modifiers, Test {
ERC20Mock internal usdc;
ERC20MissingReturn internal usdt;

SablierFlow internal flow;
FlowNFTDescriptor internal nftDescriptor;
ISablierFlow internal flow;
IFlowNFTDescriptor internal nftDescriptor;

/*//////////////////////////////////////////////////////////////////////////
SET-UP FUNCTION
Expand All @@ -40,15 +42,12 @@ abstract contract Base_Test is Assertions, Modifiers, Test {
function setUp() public virtual {
users.admin = payable(makeAddr("admin"));

if (!isBenchmarkProfile() && !isTestOptimizedProfile()) {
nftDescriptor = new FlowNFTDescriptor();
flow = new SablierFlow(users.admin, nftDescriptor);
} else {
flow = deployOptimizedSablierFlow();
}
// Deploy the Flow contracts.
deployOptimizedSablierFlow();

// Label the flow contract.
vm.label(address(flow), "Flow");
vm.label(address(nftDescriptor), "NFTDescriptor");

// Create new tokens and label them.
createAndLabelTokens();
Expand Down Expand Up @@ -119,14 +118,11 @@ abstract contract Base_Test is Assertions, Modifiers, Test {
return user;
}

/// @dev Deploys {SablierFlow} from an optimized source compiled with `--via-ir`.
function deployOptimizedSablierFlow() internal returns (SablierFlow) {
nftDescriptor = FlowNFTDescriptor(deployCode("out-optimized/FlowNFTDescriptor.sol/FlowNFTDescriptor.json"));

return SablierFlow(
deployCode(
"out-optimized/SablierFlow.sol/SablierFlow.json", abi.encode(users.admin, address(nftDescriptor))
)
/// @dev Deploys {FlowNFTDescriptor} and {SablierFlow} from an optimized source compiled with `--via-ir`.
function deployOptimizedSablierFlow() internal {
nftDescriptor = FlowNFTDescriptor(deployCode("out/FlowNFTDescriptor.sol/FlowNFTDescriptor.json"));
flow = ISablierFlow(
deployCode("out/SablierFlow.sol/SablierFlow.json", abi.encode(users.admin, address(nftDescriptor)))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract SetNFTDescriptor_Integration_Concrete_Test is Shared_Integration_Concre
// It should re-set the NFT descriptor
flow.setNFTDescriptor(nftDescriptor);
vm.expectCall(address(nftDescriptor), abi.encodeCall(FlowNFTDescriptor.tokenURI, (flow, 1)));
flow.tokenURI({ streamId: defaultStreamId });
flow.tokenURI(defaultStreamId);
}

function test_WhenNewAndOldNFTDescriptorsAreNotSame() external whenCallerAdmin {
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/concrete/token-uri/tokenURI.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { Shared_Integration_Concrete_Test } from "../Concrete.t.sol";
contract TokenURI_Integration_Concrete_Test is Shared_Integration_Concrete_Test {
function test_RevertGiven_NFTNotExist() external {
vm.expectRevert(abi.encodeWithSelector(IERC721Errors.ERC721NonexistentToken.selector, nullStreamId));
flow.tokenURI({ streamId: nullStreamId });
flow.tokenURI(nullStreamId);
}

function test_GivenNFTExists() external view {
// It should return the correct token URI
string memory actualURI = flow.tokenURI({ streamId: defaultStreamId });
string memory actualURI = flow.tokenURI(defaultStreamId);
// solhint-disable max-line-length,quotes
string memory expectedURI =
"data:application/json;base64,eyJkZXNjcmlwdGlvbiI6ICJUaGlzIE5GVCByZXByZXNlbnRzIGEgcGF5bWVudCBzdHJlYW0gaW4gU2FibGllciBGbG93IiwiZXh0ZXJuYWxfdXJsIjogImh0dHBzOi8vc2FibGllci5jb20iLCJuYW1lIjogIlNhYmxpZXIgRmxvdyIsImltYWdlIjogImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5QjNhV1IwYUQwaU5UQXdJaUJvWldsbmFIUTlJalV3TUNJZ2MzUjViR1U5SW1KaFkydG5jbTkxYm1RdFkyOXNiM0k2SUNNeE5ERTJNVVk3SWlCNGJXeHVjejBpYUhSMGNEb3ZMM2QzZHk1M015NXZjbWN2TWpBd01DOXpkbWNpSUhacFpYZENiM2c5SWpJd0lDMDBNREFnTWpBd0lERXdNREFpUGp4d1lYUm9JR2xrUFNKTWIyZHZJaUJtYVd4c1BTSWpabVptSWlCbWFXeHNMVzl3WVdOcGRIazlJakVpSUdROUltMHhNek11TlRVNUxERXlOQzR3TXpSakxTNHdNVE1zTWk0ME1USXRNUzR3TlRrc05DNDRORGd0TWk0NU1qTXNOaTQwTURJdE1pNDFOVGdzTVM0NE1Ua3ROUzR4Tmpnc015NDBNemt0Tnk0NE9EZ3NOQzQ1T1RZdE1UUXVORFFzT0M0eU5qSXRNekV1TURRM0xERXlMalUyTlMwME55NDJOelFzTVRJdU5UWTVMVGd1T0RVNExqQXpOaTB4Tnk0NE16Z3RNUzR5TnpJdE1qWXVNekk0TFRNdU5qWXpMVGt1T0RBMkxUSXVOelkyTFRFNUxqQTROeTAzTGpFeE15MHlOeTQxTmpJdE1USXVOemM0TFRFekxqZzBNaTA0TGpBeU5TdzVMalEyT0MweU9DNDJNRFlzTVRZdU1UVXpMVE0xTGpJMk5XZ3dZekl1TURNMUxURXVPRE00TERRdU1qVXlMVE11TlRRMkxEWXVORFl6TFRVdU1qSTBhREJqTmk0ME1qa3ROUzQyTlRVc01UWXVNakU0TFRJdU9ETTFMREl3TGpNMU9DdzBMakUzTERRdU1UUXpMRFV1TURVM0xEZ3VPREUyTERrdU5qUTVMREV6TGpreUxERXpMamN6TkdndU1ETTNZelV1TnpNMkxEWXVORFl4TERFMUxqTTFOeTB5TGpJMU15dzVMak00TFRndU5EZ3NNQ3d3TFRNdU5URTFMVE11TlRFMUxUTXVOVEUxTFRNdU5URTFMVEV4TGpRNUxURXhMalEzT0MwMU1pNDJOVFl0TlRJdU5qWTBMVFkwTGpnek55MDJOQzQ0TXpkc0xqQTBPUzB1TURNM1l5MHhMamN5TlMweExqWXdOaTB5TGpjeE9TMHpMamcwTnkweUxqYzFNUzAyTGpJd05HZ3dZeTB1TURRMkxUSXVNemMxTERFdU1EWXlMVFF1TlRneUxESXVOekkyTFRZdU1qSTVhREJzTGpFNE5TMHVNVFE0YURCakxqQTVPUzB1TURZeUxDNHlNakl0TGpFME9Dd3VNemN0TGpJMU9XZ3dZekl1TURZdE1TNHpOaklzTXk0NU5URXRNaTQyTWpFc05pNHdORFF0TXk0NE5ESkROVGN1TnpZekxUTXVORGN6TERrM0xqYzJMVEl1TXpReExERXlPQzQyTXpjc01UZ3VNek15WXpFMkxqWTNNU3c1TGprME5pMHlOaTR6TkRRc05UUXVPREV6TFRNNExqWTFNU3cwTUM0eE9Ua3ROaTR5T1RrdE5pNHdPVFl0TVRndU1EWXpMVEUzTGpjME15MHhPUzQyTmpndE1UZ3VPREV4TFRZdU1ERTJMVFF1TURRM0xURXpMakEyTVN3MExqYzNOaTAzTGpjMU1pdzVMamMxTVd3Mk9DNHlOVFFzTmpndU16Y3hZekV1TnpJMExERXVOakF4TERJdU56RTBMRE11T0RRc01pNDNNemdzTmk0eE9USmFJaUIwY21GdWMyWnZjbTA5SW5OallXeGxLREV1TlN3Z01TNDFLU0lnTHo0OEwzTjJaejQ9In0=";
Expand Down
11 changes: 3 additions & 8 deletions tests/utils/Precompiles.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,10 @@ contract Precompiles_Test is Base_Test {

Precompiles internal precompiles = new Precompiles();

modifier onlyTestOptimizedProfile() {
if (isTestOptimizedProfile()) {
_;
}
}

function test_DeployFlow() external onlyTestOptimizedProfile {
function test_DeployFlow() external {
deployOptimizedSablierFlow();
address actualSablierFlow = address(precompiles.deploySablierFlow(users.admin, nftDescriptor));
address expectedSablierFlow = address(deployOptimizedSablierFlow());
address expectedSablierFlow = address(flow);
bytes memory expectedSablierFlowCode =
adjustBytecode(expectedSablierFlow.code, expectedSablierFlow, actualSablierFlow);
assertEq(actualSablierFlow.code, expectedSablierFlowCode, "bytecodes mismatch");
Expand Down
12 changes: 0 additions & 12 deletions tests/utils/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,6 @@ abstract contract Utils is CommonBase, Constants, PRBMathUtils {
return amount * scaleFactor;
}

/// @dev Checks if the Foundry profile is "benchmark".
function isBenchmarkProfile() internal view returns (bool) {
string memory profile = vm.envOr({ name: "FOUNDRY_PROFILE", defaultValue: string("default") });
return Strings.equal(profile, "benchmark");
}

/// @dev Checks if the Foundry profile is "test-optimized".
function isTestOptimizedProfile() internal view returns (bool) {
string memory profile = vm.envOr({ name: "FOUNDRY_PROFILE", defaultValue: string("default") });
return Strings.equal(profile, "test-optimized");
}

/// @dev Stops the active prank and sets a new one.
function resetPrank(address msgSender) internal {
vm.stopPrank();
Expand Down
Loading