foundry-rs / foundry-rs/foundry
proposal: implement unified interface for invoking different foundry implementations
- Dominant language
- Rust
- Stars
- 10.6k
- Forks
- 2.6k
- Avg merge
- 18h 20m
- Merged PRs (30d)
- 510
Description
### Component
Other (please describe)
### Describe the feature you would like
## Problem
Right now, Foundry provides a wide coverage for Web3 ecosystem, but it comes with a few nuances:
- Some networks, such as ZKsync and Starknet, have their own implementation of Foundry.
- Some networks, such as Optimism and Odyssey, have support upstream managed via CLI flags (e.g. `anvil --optimism`).
This creates a mixed environment for users, where they have to choose binaries or CLI flags, often in different ways, which can be troublesome.
The issue will likely become even more complex with several companies now working on their stacks. I can imagine that eventually features like [OP's supersim](https://docs.optimism.io/builders/app-developers/tools/supersim) and similar ones for ZKsync/Polygon/Arbitrum would be wanted to be available out of the box (especially once interop becomes an industry standard). Based on our interactions with teams building on ZKsync, not being able to easily reuse the same setup is a big adoption barrier, even if it's "kind of" integrated (e.g. even having to pass `--zksync` flag was reported as an inconvenience, and understandably so).
## Solution
I propose creating a unified way for users to explicitly specify what network they want to use in _upstream foundry_, as well as a way for developers to create "hooks" to a particular implementation.
It would mean that users always invoke `forge`/`cast`/`anvil`/etc, but based on some form of configuration (to be covered below) it can modify behavior to match network expectations, e.g.:
- Redirect execution to `foundry-zksync`
- Set `--optimism` flag
- In the future, it can be used to set up plugins for specific chains (relevant to option 3 [mentioned here](https://github.com/foundry-rs/foundry/issues/748)).
Technically, I see several options to achieve this, but they revolve around a single prerequisite:
All the tools from the Foundry suite must load some configuration before actual execution. Let's assume that we have a variable `network_family`, with supported options like `zk_stack`, `op_stack`, `starknet`. Handling it might vary. For example, if we're invoking `forge` with `network_family=zk_stack`, we will simply forward execution to `forge-zksync` binary with the same arguments. If we're invoking `anvil` with `network_family=op_stack`, it will imply the `--optimism` CLI flag.
### Option 1 - `foundry.toml` profiles
Foundry already has a similar mechanism for altering behavior: [profiles](https://book.getfoundry.sh/reference/config/overview). We can add a `network_family` variable there, and then users can reuse the same workflows they have by altering `FOUNDRY_PROFILE` variable.
The drawback I can see here is that users may potentially need up to `N*M` profiles, where `N` stands for the profiles they normally have (default/CI), and `M` stands for the networks they support (l1/op/zksync). On the other hand, it is not unlikely that profiles for different networks will be different anyway, especially for ZKsync and Starknet.
### Option 2 - `FOUNDRY_NETWORK_FAMILY` env variable
In this option, there will be one more variable to choose a network family. For now, looks like we don't need to add anything to `foundry.toml` in this case, but in the future, it can be extended to have something similar to profiles (e.g. `[network_family.zksync]` and `[network_family.optimism]` sections). I'm not sure what the use cases will be there, but probably it may be relevant for interop (e.g. supersim configuration).
The main drawback here is that we now have 2 environment variables, which has higher cognitive complexity.
### Option 3 - Less upstream support
If having logic to handle differences of particular chains feels troublesome, there is a more lightweight approach: we can introduce `binary_mappings` variable, so that e.g. for ZKsync it would be `{ "forge" = "forge-zksync", "cast" = "cast-zksync" }` and for Starknet it would be `snforge`/`sncast`. This variable would simply tell which binary the execution should be forwarded to with the same arguments. This way no "custom" logic is added upstream, though it feels less extensible for networks with upstream support.
---
If (hopefully) we will decide that this proposal makes sense and agree on a particular option, the ZKsync community will be happy to submit PRs for the implementation. We see it as a first step towards [#feat(compatibility): add `zkSync` support](https://github.com/foundry-rs/foundry/issues/7624#top), as well as greater integrity of the ecosystem.
### Additional context
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.