Dependencies
Forge manages dependencies using git submodules by default, which means that it works with any GitHub repository that contains smart contracts.
Adding a dependency
To add a dependency, run forge install
:
$ forge install vectorized/solady
Installing solady in /tmp/tmp.zsoajxRxB4/hello_foundry/lib/solady (url: Some("https://github.com/vectorized/solady"), tag: None)
Installed solady v0.1.3
This pulls the solady
library, stages the .gitmodules
file in git and makes a commit with the message Installed solady
.
If we now check the lib
folder:
$ tree . -d -L 1
.
├── lib
├── script
├── src
└── test
5 directories
We can see that Forge installed solady
!
By default, forge install
installs the latest master branch version. If you want to install a specific tag or commit, you can do it like so:
$ forge install vectorized/solady@v0.1.3
Remapping dependencies
Forge can remap dependencies to make them easier to import. Forge will automatically try to deduce some remappings for you:
$ forge remappings
forge-std/=lib/forge-std/src/
solady/=lib/solady/src/
These remappings mean:
- To import from
forge-std
we would write:import "forge-std/Contract.sol";
- To import from
solady
we would write:import "solady/Contract.sol";
You can customize these remappings by creating a remappings.txt
file in the root of your project.
Let’s create a remapping called solady-utils
that points to the utils
folder in the solady
repository!
@solady-utils/=lib/solady/src/utils/
You can also set remappings in foundry.toml
.
remappings = [
"@solady-utils/=lib/solady/src/utils/",
]
Now we can import any of the contracts in src/utils
of the solady
repository like so:
import {LibString} from "@solady-utils/LibString.sol";
Remapping conflicts
In some cases, you may encounter dependency conflicts when two or more git submodules include different dependencies with the same namespace. For example, suppose you have installed both org/lib_1
and org/lib_2
, and they each reference their own versions of @openzeppelin
. In such scenarios, forge remappings
generates a single remapping entry for the namespace, which will point to only one of the two @openzeppelin
libraries.
$ forge remappings
@openzeppelin/=lib/lib_1/node_modules/@openzeppelin/
This situation can lead to import issues, causing forge build
to fail or introduce unexpected behavior into your contracts. To resolve this, you can add remapping contexts to your remappings.txt
file. This instructs the compiler to use different remappings in distinct compilation contexts, resolving the conflict. For example, to address the conflict between lib_1
and lib_2
, you would update your remappings.txt as follows:
lib/lib_1/:@openzeppelin/=lib/lib_1/node_modules/@openzeppelin/
lib/lib_2/:@openzeppelin/=lib/lib_2/node_modules/@openzeppelin/
This approach ensures that each dependency is mapped to the appropriate library version, avoiding potential issues. For more information about remapping, please see the Solidity Lang Docs.
Updating dependencies
You can update a specific dependency to the latest commit on the version you have specified using forge update <dep>
. For example, if we wanted to pull the latest commit from our previously installed master-version of solady
, we would run:
$ forge update lib/solady
Alternatively, you can do this for all dependencies at once by just running forge update
.
Removing dependencies
You can remove dependencies using forge remove <deps>...
, where <deps>
is either the full path to the dependency or just the name. For example, to remove solady
both of these commands are equivalent:
$ forge remove solady
# ... is equivalent to ...
$ forge remove lib/solady
Hardhat compatibility
Forge also supports Hardhat-style projects where dependencies are npm packages (stored in node_modules
) and contracts are stored in contracts
as opposed to src
.
To enable Hardhat compatibility mode pass the --hh
flag.