This guide explains how to use Foundry and the Makefile for deploying, upgrading, and managing the Liquidity Bridge Contract across different networks.
- Prerequisites
- Environment Setup
- Foundry Basics
- Makefile Overview
- Deployment Commands
- Network Configuration
- Fork Testing
- Safety Features
- Troubleshooting
- Examples
- Foundry: Install from getfoundry.sh
- Git: For version control
- Node.js: For additional dependencies
curl -L https://foundry.paradigm.xyz | bash
foundryupforge --version
cast --version
anvil --versiongit clone <repository-url>
cd liquidity-bridge-contract# Install Foundry dependencies
forge install
# Install Node.js dependencies (if any)
npm installCopy the example environment file and configure it:
cp example.env .envEdit .env with your configuration:
# RPC URLs
MAINNET_RPC_URL=https://public-node.rsk.co
TESTNET_RPC_URL=https://public-node.testnet.rsk.co
REGTEST_RPC_URL=http://localhost:4444
# Private Keys (NEVER commit these!)
MAINNET_SIGNER_PRIVATE_KEY=your_mainnet_private_key
TESTNET_SIGNER_PRIVATE_KEY=your_testnet_private_key
DEV_SIGNER_PRIVATE_KEY=your_dev_private_key
# Existing contract addresses (for upgrades)
EXISTING_PROXY_MAINNET=0x...
EXISTING_ADMIN_MAINNET=0x...
EXISTING_PROXY_TESTNET=0x...
EXISTING_ADMIN_TESTNET=0x...
# Multisig configuration
MULTISIG_ADDRESS_MAINNET=0x...
MULTISIG_OWNER_1_MAINNET=0x...
# ... add more owners as neededCompile all contracts:
forge buildRun tests:
forge test
forge test --gas-report # With gas reportingExecute deployment scripts:
forge script <script_path> --rpc-url <url> --private-key <key> --broadcastVerify contracts on block explorers:
forge verify-contract <address> <contract_name> --chain-id <id> --etherscan-api-key <key>The project uses a custom foundry.toml configuration:
[profile.default]
src = "src"
test = "test"
out = "out"
cache_path = "cache"
solc_version = "0.8.25"
optimizer = true
optimizer_runs = 1
[rpc_endpoints]
rskRegtest = "${REGTEST_RPC_URL}"
rskTestnet = "${TESTNET_RPC_URL}"
rskMainnet = "${MAINNET_RPC_URL}"The Makefile provides a convenient interface for all Foundry operations with built-in safety features.
- Network Support: Mainnet, Testnet, Dev environments
- Fork Testing: Test against forked networks
- Safety Validation: Environment checks and mainnet confirmations
- Simulation vs Deployment: Separate commands for testing and actual deployment
make <target> [NETWORK=<network>] [FORK_BLOCK=<block>] [VERIFY=<true|false>]These commands simulate deployments without broadcasting transactions:
# Deploy LiquidityBridgeContract (simulation)
make deploy-lbc NETWORK=rskTestnet
# Upgrade to V2 (simulation)
make upgrade-lbc NETWORK=rskTestnet
# Transfer ownership (simulation)
make change-owner NETWORK=rskTestnet
# High gas deployment (simulation)
make deploy-lbc-high-gas NETWORK=rskTestnetThese commands broadcast real transactions:
# Deploy LiquidityBridgeContract (actual)
make deploy-lbc-broadcast NETWORK=rskTestnet
# Upgrade to V2 (actual)
make upgrade-lbc-broadcast NETWORK=rskTestnet
# Transfer ownership (actual)
make change-owner-broadcast NETWORK=rskTestnet
# High gas deployment (actual)
make deploy-lbc-high-gas-broadcast NETWORK=rskTestnetThese include additional validation:
# Safe deployment with validation
make safe-deploy-lbc-broadcast NETWORK=rskMainnet
# Safe upgrade with validation
make safe-upgrade-lbc-broadcast NETWORK=rskMainnet
# Safe ownership transfer with validation
make safe-change-owner-broadcast NETWORK=rskMainnet| Network | Chain ID | RPC URL | Description |
|---|---|---|---|
| rskMainnet | 30 | RSK Mainnet | Production network |
| rskTestnet | 31 | RSK Testnet | Test network |
| rskDevelopment | 31 | RSK Testnet | Development addresses (same chain as testnet) |
| rskRegtest | 33 | RSK Regtest | Local regtest node |
# Mainnet operations
make deploy-lbc NETWORK=rskMainnet
make upgrade-lbc-broadcast NETWORK=rskMainnet
# Testnet operations
make deploy-lbc NETWORK=rskTestnet
make change-owner-broadcast NETWORK=rskTestnet
# Dev operations
make dev-deploy
make dev-deploy-broadcastFork testing allows you to test against real network state without using real funds.
# Testnet fork simulation
make testnet-fork-deploy
# Testnet fork actual deployment
make testnet-fork-deploy-broadcast
# Mainnet fork simulation
make mainnet-fork-deploy
# Mainnet fork actual deployment
make mainnet-fork-deploy-broadcast# Specify fork block
make deploy-lbc NETWORK=rskTestnet FORK_BLOCK=6020639
# Use latest block
make deploy-lbc NETWORK=rskMainnet FORK_BLOCK=latestThe Makefile includes built-in safety checks:
# Check environment configuration
make check-env NETWORK=rskTestnet
# Validate deployment prerequisites
make validate-deploy NETWORK=rskMainnetMainnet deployments require explicit confirmation:
# This will prompt for confirmation
make safe-deploy-lbc-broadcast NETWORK=rskMainnet# Default gas limit (10M)
make deploy-lbc NETWORK=rskTestnet
# High gas limit (15M)
make deploy-lbc-high-gas NETWORK=rskTestnet
# Custom gas limit
make deploy-lbc NETWORK=rskTestnet GAS_LIMIT=20000000# Get contract versions
make get-versions
# Get BTC block height
make get-btc-height# Build contracts
make build
# Run tests
make test
# Run tests with coverage
make coverage
# Clean build artifacts
make clean
# Install dependencies
make install
# Update dependencies
make update# Generate documentation
make docs
# Show help
make helpError: invalid value 'auto' for '--gas-price'
Solution: The Makefile now uses --legacy flag and proper gas price handling.
Error: execution reverted: EvmError: OutOfGas
Solution: Use high gas commands:
make deploy-lbc-high-gas NETWORK=rskTestnetError: Failed to get EIP-1559 fees
Solution: The Makefile automatically uses --legacy flag.
Error: No such file or directory: forge-std/Script.sol
Solution: Install dependencies:
make install
# or
forge installError: transaction nonce too low
Solution: This is expected when using a private key that has already been used. Use a fresh private key for testing.
# Check if private key is set
make check-env NETWORK=rskTestnet
# Set in .env file
TESTNET_SIGNER_PRIVATE_KEY=your_private_key# Test RPC connection
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' https://public-node.testnet.rsk.co# 1. Setup environment
cp example.env .env
# Edit .env with your configuration
# 2. Install dependencies
make install
# 3. Build contracts
make build
# 4. Test deployment (simulation)
make deploy-lbc NETWORK=rskTestnet
# 5. Check environment
make check-env NETWORK=rskTestnet
# 6. Actual deployment
make deploy-lbc-broadcast NETWORK=rskTestnet
# 7. Verify deployment
make get-versions# 1. Test on forked testnet
make testnet-fork-deploy
# 2. Test on forked mainnet
make mainnet-fork-deploy
# 3. Deploy on forked testnet
make testnet-fork-deploy-broadcast# 1. Test upgrade (simulation)
make upgrade-lbc NETWORK=rskTestnet
# 2. Check environment
make check-env NETWORK=rskTestnet
# 3. Perform upgrade
make upgrade-lbc-broadcast NETWORK=rskTestnet
# 4. Verify upgrade
make get-versions# 1. Test ownership transfer (simulation)
make change-owner NETWORK=rskTestnet
# 2. Validate multisig configuration
make check-env NETWORK=rskTestnet
# 3. Transfer ownership
make change-owner-broadcast NETWORK=rskTestnet# Always run simulation before actual deployment
make deploy-lbc NETWORK=rskTestnet
make deploy-lbc-broadcast NETWORK=rskTestnet# Test against real network state
make testnet-fork-deploy
make mainnet-fork-deploy# Check configuration before deployment
make check-env NETWORK=rskMainnet# Safe commands include additional validation
make safe-deploy-lbc-broadcast NETWORK=rskMainnet# Use gas reporting
make gas-report
# Use high gas for complex deployments
make deploy-lbc-high-gas NETWORK=rskTestnet# Regular updates
make updateThe project uses Halmos for symbolic testing, which complements existing fuzz and invariant tests. While fuzzing explores random inputs and invariant tests check properties after random sequences of calls, Halmos converts test assertions into SMT constraints and proves they hold for all possible inputs within bounded execution. If Halmos finds no counterexample, the property is mathematically proven correct within the explored bounds.
The property catalog — what each proof actually claims, its assumptions, and why it matters — lives in FORMAL_VERIFICATION.md. Treat that document as the source of truth for the specification; the Solidity in test/formal/ is one encoding of those claims.
The recommended one-command setup creates a project-local virtualenv at .venv/ with the pinned Halmos version:
make python-setup
source .venv/bin/activateThe setup script (scripts/setup-python.sh) prefers uv when available — it can fetch its own Python 3.12 if the system doesn't have one — and falls back to a system python3.12 or python3.11. Re-running is safe; the existing venv is upgraded in place.
The Halmos pin lives at the top of scripts/setup-python.sh (HALMOS_VERSION). When bumping it, update the matching pin in .github/workflows/formal.yml and the lib/halmos-cheatcodes submodule in lockstep (see below).
Note: Do not add a root-level
requirements.txt—crytic/slither-actionauto-installs it in CI inside a Python 3.9 container. Python tooling is not installed bynpm ci; runmake python-setuponce after cloning (or when the Halmos pin changes). Optional git hooks live in.pre-commit-config.yaml; install withpip install pre-commit && pre-commit installif you want them.
The halmos-cheatcodes Foundry library is tracked as a git submodule at lib/halmos-cheatcodes/, pinned to a specific upstream commit of a16z/halmos-cheatcodes. After cloning the repo run:
git submodule update --init --recursive
# or, equivalently:
forge installTo bump the cheatcodes version, update the submodule commit in lockstep with the Halmos Python pin so both halves of the toolchain stay compatible:
cd lib/halmos-cheatcodes
git fetch origin
git checkout <new-commit-or-tag>
cd ../..
git add lib/halmos-cheatcodesFormal tests live in test/formal/ and follow Halmos conventions:
- Function prefix: Use
check_instead oftestortestFuzz_. Halmos treats parameters as symbolic (all possible values), not random. - Base contract: Extend
FormalBase(intest/formal/FormalBase.sol) which provides the deployed CollateralManagement system andSymTesthelpers. - Assertions: Use
assert()for properties Halmos will prove. Usevm.assume()to constrain symbolic inputs. - File naming:
<Contract>.check.t.sol(the.check.distinguishes formal from unit/fuzz tests).
Example:
function check_SlashConservation(
uint256 collateral,
uint256 penaltyFee
) public {
vm.assume(collateral > 0 && collateral <= 100 ether);
vm.assume(penaltyFee > 0 && penaltyFee <= 100 ether);
// ... setup and call ...
assert(rewardDelta + penaltyDelta == effectivePenalty);
}
# Run all formal verification tests
make test-formal
# Or via npm
npm run test:formalHalmos runs are significantly slower than Foundry tests (minutes rather than seconds) because they solve SMT constraints. CI runs formal tests in a dedicated workflow (.github/workflows/formal.yml).
Run formal verification (npm run test:formal) when:
- Arithmetic logic changes (slashing, rewards, collateral accounting)
- Access control or state-transition guards change
- New invariant properties are added
- NEVER commit private keys to version control
- Use environment variables for all private keys
- Consider using hardware wallets for mainnet operations
- Always double-check the network before deployment
- Use simulation commands first
- Use safe commands for mainnet
- Monitor gas prices and limits
- Use appropriate gas limits for complex operations
- Consider gas price fluctuations
- Verify all contracts after deployment
- Use
make get-versionsto confirm deployments - Monitor contract interactions
For issues and questions:
- Check the troubleshooting section
- Review the Foundry documentation
- Check the project's README.md
- Open an issue in the project repository
Remember: Always test thoroughly before deploying to mainnet, and never use real private keys in development environments.