Crates.io | forge-deploy |
lib.rs | forge-deploy |
version | 0.2.0 |
source | src |
created_at | 2023-04-20 10:15:18.8702 |
updated_at | 2023-05-19 16:21:05.530015 |
description | A cli that goes along a set of soldity contract to keep track of deployments by name in forge. |
homepage | https://github.com/wighawag/forge-deploy |
repository | https://github.com/wighawag/forge-deploy |
max_upload_size | |
id | 844261 |
size | 654,730 |
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 providing the minimal set of function to provide an elegant deployment system for foundry.
https://github.com/wighawag/template-foundry
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
There are 2 way to get started, one without npm and one with npm
have a forge project with npm and cd into it
mkdir my-project;
cd my-project;
forge init;
npm init
add the forge-deploy package
npm i -D forge-deploy
This will install the forge-deploy binary automatically
add to .gitignore the generated files
cat >> .gitignore <<EOF
# forge-deploy
/generated
/deployments/localhost
/deployments/31337
EOF
you also need to allow forge to read and write on certain paths by editing foundry.toml:
cat >> foundry.toml <<EOF
fs_permissions = [
{ access = "read", path = "./deployments"},
{ access = "read", path = "./out"},
]
EOF
generate the type-safe deployment functions
add some scripts in the package.json
{
"scripts": {
"compile": "forge-deploy gen-deployer && forge build",
"deploy": "forge script script/Counter.s.sol --rpc-url $RPC_URL --broadcast --private-key $DEPLOYER_PRIVATE_KEY -v && forge-deploy sync;"
}
}
Note how we execute forge-deploy sync
directly after the script executiom. this is how forge-deploy will keep track of new deployments.
add a deploy script
add the file script/Deploy.s.sol
with 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");
}
}
The deploy function will be called and as the script extends DeployScript (which itself extends Script from forge-std) you ll have access to the deployer variable.
This variable mostly expose save and get functions. Deploy functionality is actually implemented in library like the one provided here: "DeployerFunctions.g.sol", which is actually generated code from the command above: forge-deploy gen-deployer;
You can now execute the script via forge script
npm run deploy
Note that with anvil (localhost network), you need to set the DEPLOYMENT_CONTEXT env variable for forge-deploy to save the deployment
DEPLOYMENT_CONTEXT=localhost npm run deploy
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
have a forge project and cd into it
mkdir my-project;
cd my-project;
forge init;
add the forge-deploy package
forge install wighawag/forge-deploy@v0.0.34;
build the cli directly from lib/forge-deploy
cd lib/forge-deploy;
cargo build --release;
cp target/release/forge-deploy ../../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:
./forge-deploy <command>
You could also download the binaries (if you dont want to use cargo): https://github.com/wighawag/forge-deploy/releases
add to .gitignore the generated file + the binary we just installed
cat >> .gitignore <<EOF
# forge-deploy
/generated
/deployments/localhost
/deployments/31337
# forge-deploy cli binary
/forge-deploy
EOF
you also need to allow forge to read and write on certain paths by editing foundry.toml:
cat >> foundry.toml <<EOF
fs_permissions = [
{ access = "read", path = "./deployments"},
{ access = "read", path = "./out"}
]
EOF
generate the type-safe deployment functions
./forge-deploy gen-deployer;
add a deploy script
add the file script/Deploy.s.sol
with 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");
}
}
The deploy function will be called and as the script extends DeployScript (which itself extends Script from forge-std) you ll have access to the deployer variable.
This variable mostly expose save and get functions. Deploy functionality is actually implemented in library like the one provided here: "DeployerFunctions.g.sol", which is actually generated code from the command above: ./forge-deploy gen-deployer;
You can now execute the script via forge script
Note that you need to execute ./forge-deploy sync
directly afterward
For example:
forge script script/Counter.s.sol --rpc-url $RPC_URL --broadcast --private-key $DEPLOYER_PRIVATE_KEY -v && ./forge-deploy sync;
with anvil and default account
DEPLOYMENT_CONTEXT=localhost forge script script/Counter.s.sol --rpc-url http://localhost:8545 --broadcast --private-key ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -v && ./forge-deploy sync;
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
Get anvil started somewhere:
anvil;
then copy and execute this and see the result
mkdir my-forge-deploy-project;
cd my-forge-deploy-project;
forge init;
forge install wighawag/forge-deploy@v0.0.34;
cd lib/forge-deploy;
cargo build --release;
cp target/release/forge-deploy ../../forge-deploy;
cd ../..;
cat >> foundry.toml <<EOF
fs_permissions = [
{ access = "read", path = "./deployments"},
{ access = "read", path = "./out"}
]
EOF
cat >> .gitignore <<EOF
# forge-deploy
/generated
/deployments/localhost
/deployments/31337
EOF
./forge-deploy gen-deployer;
cat > script/Deploy.s.sol <<EOF
// 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");
}
}
EOF
DEPLOYMENT_CONTEXT=localhost forge script script/Deploy.s.sol --rpc-url http://localhost:8545 --broadcast --private-key ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -v && ./forge-deploy sync;
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
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
)
);
}
}