- Overview
- Step 1: Deploy Contract
- Step 2: Verify Deployment
- Step 3: Verify Source Code
- Step 4: Update Config JSON
- Multi-Chain Deployment
- Impact on Other Contracts
Deploys a new Factory proxy and implementation pair using DeployFactory.s.sol.
Before you begin: Complete all environment and prerequisite setup in README.md - Environment and Prerequisites, then return here.
Key difference from other deployments: Factory is a base-layer contract, but no other contract stores the Factory address as a runtime dependency. A new Factory can be deployed at any time via direct CREATE — deterministic addressing is not required.
Dependencies managed by this deployment:
| Method | Parameter Registry Key | Updated Contract | Update Function |
|---|---|---|---|
| — | — | — | — |
Factory has no dependency updates — no contracts store the Factory address at runtime. The Factory is only used at deploy time by other contracts' deployment scripts. After deployment, update config/<environment>.json so future scripts use the new Factory.
Parameter Registry: No parameter registry updates are required. The two Factory-related keys (xmtp.factory.paused and xmtp.factory.migrator) are optional governance parameters that apply per-Factory and start at their zero-value defaults (unpaused, no migrator).
Deploy the new Factory:
forge script script/single-deployments/any-chain/DeployFactory.s.sol --rpc-url base_sepolia --slow --sig "deployContract()" --broadcastThis:
- Deploys a new Factory implementation (
new Factory(parameterRegistry)) - Deploys a new Factory proxy (
new Proxy(implementation)) - Initializes the proxy (creates the
Initializablecontract) - Validates that
parameterRegistryandinitializableImplementationare set correctly - Writes the new proxy address to
environments/<environment>.jsonassettlementChainFactoryorappChainFactory(depending on which chain you're connected to)
Run the verification script (read-only, no broadcast needed):
forge script script/single-deployments/any-chain/DeployFactory.s.sol --rpc-url base_sepolia --sig "verifyDeployment()"This reads the Factory address from environments/<environment>.json and checks:
- Code exists at the factory address
parameterRegistrymatches the expected valueinitializableImplementationis non-zero (initialized)- Prints
contractName,version, andpausedstatus
Additionally, verify with direct cast calls:
# Check version
cast call <new-factory-proxy> "version()(string)" --rpc-url $RPC_URL
# Check parameterRegistry
cast call <new-factory-proxy> "parameterRegistry()(address)" --rpc-url $RPC_URL
# Check initializableImplementation
cast call <new-factory-proxy> "initializableImplementation()(address)" --rpc-url $RPC_URL
# Check not paused
cast call <new-factory-proxy> "paused()(bool)" --rpc-url $RPC_URLVerify both the Factory implementation and the Proxy on the block explorer. Replace <chain-id>, <parameter-registry>, <implementation>, and <proxy> with the values printed by deployContract().
Factory implementation (constructor takes parameterRegistry):
forge verify-contract \
--chain-id <chain-id> \
--watch \
--constructor-args $(cast abi-encode "constructor(address)" <parameter-registry-address>) \
<factory-implementation-address> \
src/any-chain/Factory.sol:FactoryProxy (constructor takes the implementation address):
forge verify-contract \
--chain-id <chain-id> \
--watch \
--constructor-args $(cast abi-encode "constructor(address)" <parameter-registry-address>) \
<proxy-implementation-address> \
src/any-chain/Proxy.sol:ProxyAfter successful deployment on all chains (settlement + app), update config/<environment>.json:
{
"factory": "<new-factory-proxy>",
"factoryImplementation": "<new-factory-implementation>",
"initializableImplementation": "<new-initializable-implementation>",
...
}These values were printed by deployContract(). This step is critical — all future contract deployments via the single-deployment or upgrade scripts read factory from this config.
Factory exists on both the settlement chain and the app chain. Deploy separately on each:
-
Settlement chain:
forge script DeployFactoryScript --rpc-url $SETTLEMENT_RPC_URL --slow --sig "deployContract()" --broadcast
Updates
settlementChainFactoryinenvironments/<environment>.json. -
App chain:
forge script DeployFactoryScript --rpc-url $APP_CHAIN_RPC_URL --slow --sig "deployContract()" --broadcast
Updates
appChainFactoryinenvironments/<environment>.json.
Multiple environments sharing a chain: If multiple environments (e.g. testnet-dev, testnet-staging, testnet) share the same settlement chain, you only deploy once per chain. The on-chain Factory is the same; just update each environment's JSON files.
No impact. Contracts already deployed through the old Factory continue to work. They do not reference the Factory at runtime.
All proxy addresses deployed through the new Factory will differ from those deployed through the old Factory (because the new Factory has a different initializableImplementation, which changes the proxy init code hash). When deploying new contracts via the single-deployment scripts, re-run predictAddresses() to get the correct new addresses with the updated factory in config.