forge-deploy
A cli and associated contracts to keep track of deployments by name and reuse them in solidity.
It tries to keep compatibility with hardhat-deploy as far as possible (work in progress).
forge-deploy aims at provoding the minimal set of function to provide an elegant deployment system for foundry.
Features
- generate type-safe deployment function for forge contracts. no need to pass in string of text and hope the abi encoded args are in the correct order or of the correct type.
- save deployments in json file (based on hardhat-deploy schema)
- modular system based on templates and solidity library
Modularity
The system is modular. The deploy functions provided by default offer a basic set of feature but the system can be extended by custom function easily. See contracts/DefaultDeployerFunction.sol and how this is a simple library that you can provide yourself. The only thing forge-deploy really provide is the specific set of functions in contrats/Deployer.sol to save and get deployments
How to use
-
have a forge project and cd into it
; ; ; -
add the forge-deploy package
; -
build the cli directly from lib/forge-deploy
; ; ;In the last step above, we also copy it in the project folder for easy access;
This way you can then execute it via the following:
-
add to .gitignore the generated file + the binary we just installed
-
generate the type-safe deployment functions
; -
add a deploy script
add the file
script/Deploy.s.solwith this content:// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; import "forge-deploy/DeployScript.sol"; import "generated/deployer/DeployerFunctions.g.sol"; contract Deployments is DeployScript { using DeployerFunctions for Deployer; function deploy() external returns (Counter) { return deployer.deploy_Counter("MyCounter"); } } -
you also need to allow forge to read and write on certain paths by editing foundry.toml:
You might wonder what
contexts.json. This is a configuration file. Its name might change in the future, but as of now, it let you configure context (like localhost, sepolia, mainnet) and specify a list of tags that you can then use in your deploy script to trigger diferent execution path. -
You can now execute the script via forge script
Note that you need to execute
./forge-deploy syncdirectly afterwardFor example:
&& ;with anvil and default account
DEPLOYMENT_CONTEXT=localhost && ;Note that here we specify the DEPLOYMENT_CONTEXT env variable. This is necessary for localhost which use chain id 31337 as by default forge-deploy will not save the deployment on that chainId (same for 1337). This is so it does not interfere with in-memory tests which also use chainId=31337
The DEPLOYMENT_CONTEXT env var also allows you to segregate different deployment context on the same network. If not specified, the context is the chainId
-
If you use just, see example in examples/basic with its own justfile
Quick Start
Get anvil started somewhere:
;
then copy and execute this and see the result
;
;
;
;
;
;
;
;
;
DEPLOYMENT_CONTEXT=localhost && ;
Reusable in tests
One great feature of forge's script that remains in forge-deploy is the ability to use script in tests.
This allow you to have your deployment procedure reusable in tests!
for example, here is a basic test for Counter. Copy the following content in the existing test/Counter.t.sol and run the test to see it in action:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../src/Counter.sol";
import "../script/Deploy.s.sol";
contract CounterTest is Test {
Counter public counter;
function setUp() public {
counter = new Deployments().deploy();
counter.setNumber(0);
}
function testIncrement() public {
counter.increment();
assertEq(counter.number(), 1);
}
function testSetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
}
As usual to run the tests you can do the following:
forge test
More info
Note that the generated solidity is optional.
You can instead simply use the default deploy function
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-deploy/DeployScript.sol";
import "forge-deploy/DefaultDeployerFunction.sol";
import "../src/Counter.sol";
contract Deployments is DeployScript {
using DefaultDeployerFunction for Deployer;
function deploy() external returns (Counter) {
return Counter(
deployer.deploy(
"MyCounter",
"Counter.sol:Counter", // forge's artifact id
"", // no arguments: empty bytes
DeployOptions({
deterministic: 0, // 0 => no deterministic
proxyOnTag: "", // empty string => no proxy
proxyOwner: address(0)
})
)
);
}
}