deploy_contract_with_forge
Documentation for eth_defi.foundry.forge.deploy_contract_with_forge function.
- deploy_contract_with_forge(web3, project_folder, contract_file, contract_name, deployer, constructor_args=None, etherscan_api_key=None, verifier=None, verifier_url=None, register_for_tracing=True, timeout=240, wait_for_block_confirmations=0, verify_delay=20, verify_retries=9, verbose=False, contract_file_out=None, forge_libraries=None, cache_dir=None, deploy_retries=1)
Deploy and verify smart contract with Forge.
The smart contracts must be developed with Foundry tool chain and its forge command
Uses Forge to verify the contract on Etherscan
For normal use
deploy_contract()is much easier
Example:
guard, tx_hash = deploy_contract_with_forge( web3, CONTRACTS_ROOT / "guard", # Foundry projec path "GuardV0.sol", # src/GuardV0.sol f"GuardV0", # GuardV0 is the smart contract name deployer, # Local account with a private key we use for the deployment etherscan_api_key=etherscan_api_key, # Etherscan API key we use for the verification ) logger.info("GuardV0 is %s deployed at %s", guard.address, tx_hash.hex()) # Test the deployed contract assert guard.functions.getInternalVersion().call() == 1
Assumes standard Foundry project layout with foundry.toml, src and out.
Known forge issues with “contract was not deployed”
Forge’s
forge createhas a known issue where allPendingTransactionErrorvariants (receipt polling timeout, transaction dropped from mempool, RPC failure) are silently converted into a single generic"contract was not deployed"message in crates/forge/src/cmd/create.rs:impl From<PendingTransactionError> for ContractDeploymentError { fn from(_err: PendingTransactionError) -> Self { Self::ContractNotDeployed // original error discarded } }
On load-balanced RPCs (e.g. drpc.live), this is frequently a false positive: the transaction was mined but forge polled a different backend node that didn’t have the receipt yet. Foundry #1362 reported ~80% failure rate on testnets. Forge does not print the transaction hash on failure, so the deployer address + nonce are needed to look up the tx on a block explorer.
This function handles the issue with two mechanisms:
False-positive recovery: after a failure, checks the on-chain nonce and scans recent blocks to detect if the contract was actually deployed (see
_try_recover_deployment()).Retry with nonce re-sync: if recovery fails and
deploy_retries > 1, re-syncs the nonce from the chain and retries the deployment.
Other relevant Foundry issues:
#877 — “forge create sometimes takes two invocations”
#13352 — open issue to improve UX for dropped transactions
#1803 —
--gas-estimate-multipliernot available forforge create(onlyforge script)
RPC URL selection and L2 sequencers
forge createrequires a full RPC that supports both reads (eth_chainId,eth_gasPrice,eth_getTransactionReceipt) and writes (eth_sendRawTransaction). This rules out L2 sequencer endpoints:Arbitrum sequencers (e.g.
arb1-sequencer.arbitrum.io/rpc) are write-only — they only accepteth_sendRawTransaction. Forge will fail immediately because it callseth_chainIdfirst.OP Stack sequencers (Base, Optimism) run a full
op-gethbut may return 403 Forbidden on read calls under load.
For forge deployments, prefer the chain’s official single-endpoint public RPC over a load-balanced aggregator like drpc.live. These route to one backend, avoiding the receipt-polling inconsistency that triggers foundry#1362:
Arbitrum Sepolia:
https://sepolia-rollup.arbitrum.io/rpcBase Sepolia:
https://sepolia.base.orgArbitrum One:
https://arb1.arbitrum.io/rpcBase:
https://mainnet.base.org
See
eth_defi.chain.SEQUENCERSfor the full mapping of chain IDs to sequencer and public RPC URLs.When using
MultiProviderWeb3, this function automatically selects the call provider URL (not themev+broadcast endpoint) viaweb3.provider.call_endpoint_uri.See
Foundry book for more information
eth_defi.deploy.deploy_contract()for simple, non-verified contract deploymentsMEV protection and multiple JSON-RPCs configuration tutorial for MEV protection and multi-provider configuration
- Parameters
web3 (web3.main.Web3) – Web3 instance
deployer (Union[eth_defi.hotwallet.HotWallet, eth_account.signers.local.LocalAccount]) –
Deployer tracked as a hot wallet.
We need to be able to manually track the nonce across multiple contract deployments.
project_folder (pathlib.Path) – Foundry project with foundry.toml in the root.
contract_file (Union[pathlib.Path, str]) –
Contract path relative to the project folder.
E.g. TermsOfService.sol.
contract_name (str) –
The smart contract name within the file.
E.g. TermsOfService.
constructor_args (Optional[list[str]]) –
Other arguments to pass to the contract’s constructor.
Need to be able to stringify these for forge.
etherscan_api_key (Optional[str]) –
API key for Etherscan-compatible verification services.
Required when using
verifier="etherscan"orverifier="oklink".Not needed for Blockscout or Sourcify.
E.g. 3F3H8…..
verifier (Optional[Literal['etherscan', 'blockscout', 'sourcify', 'oklink']]) –
The contract verification provider to use.
Supported values:
"etherscan": Etherscan and compatible explorers (requires API key)"blockscout": Blockscout explorers (requires verifier_url)"sourcify": Sourcify verification (no API key required)"oklink": OKLink explorer (requires API key)
If
Nonebutetherscan_api_keyis provided, defaults to"etherscan"for backward compatibility.verifier_url (Optional[str]) –
Custom verifier URL for Blockscout or other custom verification endpoints.
Required when
verifier="blockscout".Example:
"https://base.blockscout.com/api/"register_for_tracing –
Make the symbolic contract information available on web3 instance.
See
get_contract_registry()wait_for_block_confirmations – Currently not used.
verbose – Try to be extra verbose with Forge output to pin point errors
forge_libraries (Optional[dict[str, str]]) –
Pre-deployed library addresses for
--librariesflag.Maps
"source_path:LibraryName"to deployed address. E.g.{"src/lib/CowSwapLib.sol:CowSwapLib": "0x000..."}.Use
eth_defi.deploy.build_guard_forge_libraries()to build this mapping for guard contracts.cache_dir (Optional[pathlib.Path]) – Isolated directory for forge’s
--cache-pathand--outflags. When set, forge writes compilation cache and ABI artifacts here instead of inproject_folder. This allows multiple concurrent forge processes to share the same source tree without lock contention.deploy_retries (int) –
Number of attempts when forge reports
"contract was not deployed".On unreliable testnet RPCs (e.g. drpc.live load-balanced Base Sepolia), forge may fail to confirm the deploy transaction. Setting this > 1 will re-sync the nonce and retry. Default: 1 (no retries).
Safety: values > 1 are only allowed on testnets (Anvil or chain IDs listed in
eth_defi.chain.TESTNET_CHAIN_IDS). AValueErroris raised if retries are requested on mainnet.contract_file_out (Optional[Union[pathlib.Path, str]]) –
- Raises
In the case we could not deploy the contract.
Running forge failed
Transaction could not be confirmed
- Returns
Contract and deployment tx hash.
- Return type
Tuple[web3.contract.contract.Contract, hexbytes.main.HexBytes]