Soldeer as a Package Manager

As explained here, Foundry has been using git submodules to handle dependencies up until now.

The need for a native package manager started to emerge as projects became more complex.

A new approach has been in the making, soldeer.xyz, which is a Solidity native dependency manager built in Rust and open sourced (check the repository https://github.com/mario-eth/soldeer).

Initialize a new project

If you’re using Soldeer for the first time in a new Foundry project, you can use the init command to install a fresh instance of Soldeer, complete with the necessary configurations and the latest version of forge-std.

forge soldeer init

Adding a Dependency

Add a Dependency Stored in the Central Repository

To add a dependency, you can visit soldeer.xyz and search for the dependency you want to add (e.g., openzeppelin 5.0.2).

image

Then just run the forge command:

forge soldeer install @openzeppelin-contracts~5.0.2

This will download the dependency from the central repository and install it into a dependencies directory.

Soldeer can manage two types of dependency configuration: using soldeer.toml or embedded in the foundry.toml. In order to work with Foundry, you have to define the [dependencies] config in the foundry.toml. This will tell the soldeer CLI to define the installed dependencies there. E.g.

# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config

[profile.default]
auto_detect_solc = false 
bytecode_hash = "none" 
fuzz = { runs = 1_000 } 
libs = ["dependencies"] # <= This is important to be added
gas_reports = ["*"] 

[dependencies] # <= Dependencies will be added under this config
"@openzeppelin-contracts" = { version = "5.0.2" }
"@uniswap-universal-router" = { version = "1.6.0" }
"@prb-math" = { version = "4.0.2" }
forge-std = { version = "1.8.1" }

If the central repository does not have a certain dependency, you can install it by providing a zip archive link.

E.g.

forge soldeer install @custom-dependency~1.0.0 https://my-website.com/custom-dependency-1-0-0.zip

The above command will try to download the dependency from the provided link and install it as a normal dependency. For this, you will see in the config an additional field called path.

E.g.

[dependencies]
"@custom-dependency" = { version = "1.0.0", path = "https://my-website.com/custom-dependency-1-0-0.zip" }

Add a Dependency Stored in GIT

If you choose to use Git as a source for your dependencies — though we generally discourage this, since Git isn’t designed to be a dependency manager — you can provide the Git repository link as an additional argument. Soldeer will then automatically handle the installation using a Git subprocess. For example:

forge soldeer install forge-std~1.9.2 https://github.com/foundry-rs/forge-std.git

If you want to use a specific revision, branch, or tag, you can do so by appending the following arguments to the command: --rev/--tag/--branch

e.g.

forge soldeer install forge-std~1.9.2 https://github.com/foundry-rs/forge-std.git --rev 4695fac44b2934aaa6d7150e2eaf0256fdc566a7

Updating Dependencies

Because Soldeer specifies the dependencies in a config file (foundry or soldeer toml), sharing a dependency configuration within the team is much easier.

For example, having this Foundry config file in a git repository, one can pull the repository and then run forge soldeer update. This command will automatically install all the dependencies specified under the [dependencies] tag.

# Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config

[profile.default]
auto_detect_solc = false 
bytecode_hash = "none" 
fuzz = { runs = 1_000 } 
libs = ["dependencies"] # <= This is important to be added
gas_reports = ["*"]

[dependencies] # <= Dependencies will be added under this config
"@openzeppelin-contracts" = { version = "5.0.2" }
"@uniswap-universal-router" = { version = "1.6.0" }
"@prb-math" = { version = "4.0.2" }
forge-std = { version = "1.8.1" }

Removing Dependencies

You can use forge soldeer uninstall DEPENDENCY.

Example: forge soldeer uninstall @openzeppelin-contracts. This will action will remove:

  • the config entry
  • the dependencies artifacts
  • the soldeer.lock entry
  • the remappings entry (txt or config remapping)

Additionally you can manually remove a dependency by just removing the artifacts: dependency files, config entry, remappings entry.

Remappings

The remappings are now fully configurable, the config TOML file (foundry.toml) accepts a [soldeer] field with the following options

[soldeer]
# whether soldeer manages remappings
remappings_generate = true

# whether soldeer re-generates all remappings when installing, updating or uninstalling deps
remappings_regenerate = false

# whether to suffix the remapping with the version: `name-a.b.c`
remappings_version = true

# a prefix to add to the remappings ("@" would give `@name`)
remappings_prefix = ""

# where to store the remappings ("txt" for `remappings.txt` or "config" for `foundry.toml`)
# ignored when `soldeer.toml` is used as config (uses `remappings.txt`)
remappings_location = "txt"

Installing dependencies of dependencies aka sub-dependencies

Whenever you install a dependency, that dependency might have other dependencies that need to be installed as well. Currently, you can handle this by either specifying the recursive_deps field as a configuration entry in the config file or by passing the --recursive-deps argument when running the install or update command. This will ensure that all necessary sub-dependencies are automatically pulled in. e.g.

[soldeer]
recursive_deps = true

Pushing a New Version to the Central Repository

Soldeer acts like npmjs/crates.io, encouraging all developers to publish their projects to the central repository.

To do that, you have to go to soldeer.xyz, create an account, verify it, then

image

Just add a new project

image

After the project is created, you can go into your project source and:

  • Create a .soldeerignore file that acts as a .gitignore to exclude files that aren’t needed. The .gitignore file is also respected.
  • Run forge soldeer login to log into your account.
  • Run forge soldeer push my-project~1.0.0 in your terminal in the directory that you want to push to the central repository associated with the project my-project at version 1.0.0.

If you want to push a specific directory and not the current directory your terminal is in, you can use forge soldeer push my-project~1.0.0 /path/to/directory.

Warning ⚠️

You are at risk to push sensitive files to the central repository that then can be seen by everyone. Make sure to exclude sensitive files in the .soldeerignore file. Furthermore, we’ve implemented a warning that it will be triggered if you try to push a project that contains any .dot files/directories. If you want to skip this warning, you can just use

forge soldeer push my-project~1.0.0 --skip-warnings true

Dry-run

In case you want to simulate what would happen if you push a version, you can use the --dry-run flag. This will create a zip file that you can inspect before pushing it to the central repository.

forge soldeer push my-project~1.0.0 --dry-run true

Warning ⚠️

  • Once a project is created, it cannot be deleted.
  • Once a version is pushed, it cannot be deleted.
  • You cannot push the same version twice.
  • The project name in the command that you run in the terminal must match the project name that you created on the Soldeer website.
  • We encourage everyone to use version pinning when importing them into the contracts, this will help with securing your code by knowing exactly what version of a dependency you are using. Furthermore, it will help security researchers in their work.
  • Make sure you delete this zip file before pushing the version if you run dry-run. e.g. instead of using import '@openzeppelin-contracts/token/ERC20.sol' you should do import '@openzeppelin-contracts-5.0.2/token/ERC20.sol'

What happens if a certain package is not present in the central repository?

  • If a certain package is not present in the central repository, you can open an issue in the Soldeer Repository and the team will look into adding it.
  • If you have a package that you want to use and it is not present in the central repository, you can push it to the central repository by following the steps above.