diff --git a/INSTALL.md b/INSTALL.md
index f7a86cc..21875f1 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -22,7 +22,8 @@ The deployment process includes the following steps:
- `make deploy-helpers` deploys all helper contracts defined in `OPS_EVM_HELPER_CONFIGS`,
- `make deploy-leftover-exchanger` deploys LeftoverExchanger,
- `make deploy-fee-collector-factory` deploys FeeCollector and FeeCollectorFactory,
- - `make deploy-new-fee-collector` deploys specific FeeCollectors defined in `OPS_FEE_COLLECTOR_OPERATORS` and `OPS_FEE_COLLECTOR_OPERATOR_NAMES`,
+ - `make deploy-new-fee-collector` deploys a single FeeCollector for one operator (`OPS_FEE_COLLECTOR_OPERATOR_NAME` and `OPS_FEE_COLLECTOR_OPERATOR`),
+ - `make deploy-new-fee-collectors` deploys multiple FeeCollectors defined in `OPS_FEE_COLLECTOR_OPERATORS` and `OPS_FEE_COLLECTOR_OPERATOR_NAMES`,
- `make upgrade-fee-collector` upgrades the FeeCollector implementation.
5. To get deployed contract addresses, use `make get PARAMETER=` where `` is the contract parameter name (e.g., `OPS_EVM_HELPERS_ADDRESS`).
@@ -52,8 +53,21 @@ The deployment process includes the following steps:
### FeeCollector
- `OPS_FEE_COLLECTOR_FACTORY_OWNER_ADDRESS`: The owner address for the Fee Collector Factory.
- `OPS_FEE_COLLECTOR_OWNER_ADDRESS`: The owner address for the Fee Collector.
-- `OPS_FEE_COLLECTOR_OPERATORS`: Comma-separated list of operator addresses for new fee collectors.
-- `OPS_FEE_COLLECTOR_OPERATOR_NAMES`: Comma-separated list of operator names corresponding to the addresses.
+- `OPS_FEE_COLLECTOR_FACTORY_ADDRESS`: The deployed FeeCollectorFactory contract address (required for `deploy-new-fee-collector` and `deploy-new-fee-collectors`).
+
+Single operator (`make deploy-new-fee-collector`):
+- `OPS_FEE_COLLECTOR_OPERATOR_NAME`: Operator name (plain string, e.g. `Safe`).
+- `OPS_FEE_COLLECTOR_OPERATOR`: Operator address (plain string).
+
+After a successful single deploy:
+1. The deploy script appends `OPS_FEE_COLLECTOR_INSTANCE_ADDRESS=0x...` to `.env.outputs`
+2. `process-fee-collector-instance` writes the address to `config/constants.json` as `feeCollector[] = "0x..."` via `upsert-constant`
+
+Batch operators (`make deploy-new-fee-collectors`, also used by `deploy-all`):
+- `OPS_FEE_COLLECTOR_OPERATOR_NAMES`: JSON array of operator names (e.g. `'["Safe","DevPortal"]'`).
+- `OPS_FEE_COLLECTOR_OPERATORS`: JSON object mapping names to addresses (e.g. `'{"Safe":"0x...","DevPortal":"0x..."}'`).
+
+Batch deploy does not write `.env.outputs` or update `feeCollector` in `constants.json`.
Note: For ZKSync (chain ID 324), `OPS_ZKSYNC_MODE` is automatically set to true and `OPS_CREATE3_DEPLOYER_ADDRESS` is not required.
@@ -68,11 +82,12 @@ Here is a list of main Makefile targets you can use by running `make $$tmpfile \
+ && mv $$tmpfile $(FILE_CONSTANTS_JSON); \
+ echo "Updated feeCollectorOperator[$(OPS_CHAIN_ID)][$(OPS_FEE_COLLECTOR_OPERATOR_NAME)] = $(OPS_FEE_COLLECTOR_OPERATOR)"; \
+ }
+
process-leftover-exchanger-owner:
@$(MAKE) OPS_GEN_KEY=leftoverExchangerOwner OPS_GEN_VAL='$(OPS_LEFTOVER_EXCHANGER_OWNER_ADDRESS)' upsert-constant
@@ -194,6 +225,7 @@ deploy-skip-all:
$(FILE_DEPLOY_FEE_COLLECTOR_FACTORY) \
$(FILE_DEPLOY_FEE_COLLECTOR_FACTORY_ZKSYNC) \
$(FILE_DEPLOY_NEW_FEE_COLLECTOR) \
+ $(FILE_DEPLOY_NEW_FEE_COLLECTORS) \
$(FILE_UPGRADE_FEE_COLLECTOR) \
$(FILE_UPGRADE_FEE_COLLECTOR_ZKSYNC); do \
$(MAKE) OPS_CURRENT_DEP_FILE=$$secret deploy-skip; \
@@ -291,10 +323,11 @@ help:
@echo " deploy-helpers Deploy helper contracts"
@echo " deploy-leftover-exchanger Deploy leftover exchanger contract"
@echo " deploy-fee-collector-factory Deploy fee collector factory contract"
- @echo " deploy-new-fee-collector Deploy new fee collector contract"
+ @echo " deploy-new-fee-collector Deploy new fee collector contract for a single operator"
+ @echo " deploy-new-fee-collectors Deploy new fee collector contracts for multiple operators"
@echo " upgrade-fee-collector Upgrade fee collector contract"
@echo " get PARAMETER=... Get deployed contract address"
@echo " launch-hh-node Launch Hardhat node with forked RPC"
@echo " help Show this help message"
-.PHONY: install install-utils install-dependencies clean deploy-all deploy-helpers deploy-leftover-exchanger deploy-fee-collector-factory deploy-new-fee-collector upgrade-fee-collector get get-outputs help validate validate-helpers validate-leftover-exchanger validate-fee-collector-factory validate-new-fee-collector validate-upgrade-fee-collector process-helpers-args process-weth process-create3-deployer process-lop process-fee-collector-factory-owner process-fee-collector-owner process-fee-collector-operator process-leftover-exchanger-owner process-leftover-exchanger-salt process-fee-collector-salt process-fee-collector-factory-salt upsert-constant deploy-skip-all deploy-skip deploy-noskip
+.PHONY: install install-utils install-dependencies clean deploy-all deploy-helpers deploy-leftover-exchanger deploy-fee-collector-factory deploy-new-fee-collector deploy-new-fee-collectors upgrade-fee-collector get get-outputs help validate validate-helpers validate-leftover-exchanger validate-fee-collector-factory validate-new-fee-collector validate-new-fee-collectors validate-upgrade-fee-collector process-helpers-args process-weth process-create3-deployer process-lop process-fee-collector-factory process-fee-collector-factory-owner process-fee-collector-owner process-fee-collector-operator process-fee-collector-operator-single process-leftover-exchanger-owner process-leftover-exchanger-salt process-fee-collector-salt process-fee-collector-factory-salt upsert-constant deploy-skip-all deploy-skip deploy-noskip
diff --git a/config/constants.js b/config/constants.js
index b0755df..09a1145 100644
--- a/config/constants.js
+++ b/config/constants.js
@@ -20,6 +20,7 @@ module.exports = {
LOP: constantsData.lop || {},
CREATE3_DEPLOYER_CONTRACT: constantsData.create3DeployerContract || {},
FEE_COLLECTOR_OPERATOR: constantsData.feeCollectorOperator || {},
+ FEE_COLLECTOR: constantsData.feeCollector || {},
CONSTRUCTOR_ARGS: {
UniV4Helper: constantsData.constructorArgs?.UniV4Helper || {},
UniV4HelperV2: sliceArgs(constantsData.constructorArgs?.UniV4Helper || {}, 2),
diff --git a/config/constants.json b/config/constants.json
index 66b6a78..5cba07a 100644
--- a/config/constants.json
+++ b/config/constants.json
@@ -1,123 +1,22 @@
{
"weth": {
- "31337": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
- "1": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
- "56": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
- "137": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
- "42161": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
- "10": "0x4200000000000000000000000000000000000006",
- "43114": "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7",
- "100": "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d",
- "250": "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83",
- "1313161554": "0xC9BdeEd33CD01541e1eeD10f90519d2C06Fe3feB",
- "8217": "0xe4f05A66Ec68B54A58B17c22107b02e0232cC817",
- "8453": "0x4200000000000000000000000000000000000006",
- "59144": "0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f",
- "146": "0x039e2fB66102314Ce7b64Ce5Ce3E5183bc94aD38",
- "130": "0x4200000000000000000000000000000000000006",
- "324": "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91"
},
"feeCollectorOwner": {
- "1": "0x9F8102b1bB05785BaD2874f2C7B1aaea4c6D976a",
- "56": "0x7a4C2f97069f874A355607eBC52aEfCc4eAc9202",
- "137": "0xA154B43EEa8905Ef684995424fF476656ab50A61",
- "42161": "0x0f6E3fB5D73AFd2e594AC4b962E57E603E650875",
- "10": "0x5B18c756F4D9B54255a17BF120da2cF74743247f",
- "43114": "0x3b26f6325868Ddd8CB223Ac766cE02a2906653A5",
- "100": "0x9e05fA5A389D782C17369a76d8e59A268973275F",
- "250": "0x0dBa0Da8C5642Db20fEAc06b7A6E9e08e6E501C6",
- "1313161554": "0x0e9292Ff8be5bA8075bE05F5F155E10422AE8017",
- "8217": "0xa38038f9Ac2b3A7b4247804A46C787960E160Aed",
- "8453": "0xa4659995DC39d891C1bA9131Aaf5F000E5B57224",
- "59144": "0x9cCf4d6B76976Ab11CF9f9219A38BA28983A9a27",
- "324": "0x5cEf041D1C3198Ce7F9D5E0521867e670da7520e",
- "146": "0x385004992b43F1A73c6Cb2F7D2B88B79a3c0120f",
- "130": "0xc985620F2F18a6560ca68F1f85107674735CF8e7",
- "31337": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
},
"feeCollectorFactory": {
- "1": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "56": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "137": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "42161": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "10": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "43114": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "100": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "250": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "1313161554": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "8217": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "8453": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "59144": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "324": "0x0a479E2ac6d90e15d3c1Fae861b84260D7D4fadb",
- "146": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "130": "0xD25c6f0293d41758552b0B27d6F69353a1134d51",
- "31337": "0xb8b1Dd7c82317485942984775649d506c12c203D"
},
"feeCollectorFactoryOwner": {
- "31337": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
- "1": "0x9F8102b1bB05785BaD2874f2C7B1aaea4c6D976a",
- "56": "0x7a4C2f97069f874A355607eBC52aEfCc4eAc9202",
- "137": "0xA154B43EEa8905Ef684995424fF476656ab50A61",
- "42161": "0x0f6E3fB5D73AFd2e594AC4b962E57E603E650875",
- "10": "0x5B18c756F4D9B54255a17BF120da2cF74743247f",
- "43114": "0x3b26f6325868Ddd8CB223Ac766cE02a2906653A5",
- "100": "0x9e05fA5A389D782C17369a76d8e59A268973275F",
- "250": "0x0dBa0Da8C5642Db20fEAc06b7A6E9e08e6E501C6",
- "1313161554": "0x0e9292Ff8be5bA8075bE05F5F155E10422AE8017",
- "8217": "0xa38038f9Ac2b3A7b4247804A46C787960E160Aed",
- "8453": "0xa4659995DC39d891C1bA9131Aaf5F000E5B57224",
- "59144": "0x9cCf4d6B76976Ab11CF9f9219A38BA28983A9a27",
- "146": "0x385004992b43F1A73c6Cb2F7D2B88B79a3c0120f",
- "130": "0xc985620F2F18a6560ca68F1f85107674735CF8e7",
- "324": "0x5cEf041D1C3198Ce7F9D5E0521867e670da7520e"
},
"lop": {
- "1": "0x111111125421cA6dc452d289314280a0f8842A65",
- "56": "0x111111125421cA6dc452d289314280a0f8842A65",
- "137": "0x111111125421cA6dc452d289314280a0f8842A65",
- "42161": "0x111111125421cA6dc452d289314280a0f8842A65",
- "10": "0x111111125421cA6dc452d289314280a0f8842A65",
- "43114": "0x111111125421cA6dc452d289314280a0f8842A65",
- "100": "0x111111125421cA6dc452d289314280a0f8842A65",
- "250": "0x111111125421cA6dc452d289314280a0f8842A65",
- "1313161554": "0x111111125421cA6dc452d289314280a0f8842A65",
- "8217": "0x111111125421cA6dc452d289314280a0f8842A65",
- "8453": "0x111111125421cA6dc452d289314280a0f8842A65",
- "59144": "0x111111125421cA6dc452d289314280a0f8842A65",
- "324": "0x6fd4383cB451173D5f9304F041C7BCBf27d561fF",
- "146": "0x111111125421cA6dc452d289314280a0f8842A65",
- "130": "0x111111125421cA6dc452d289314280a0f8842A65",
- "31337": "0x111111125421cA6dc452d289314280a0f8842A65"
},
"create3DeployerContract": {
- "1": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "56": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "137": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "42161": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "10": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "43114": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "100": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "250": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "1313161554": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "8217": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "8453": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "59144": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "146": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "130": "0x65B3Db8bAeF0215A1F9B14c506D2a3078b2C84AE",
- "31337": "0x62f4807082fa27E711784C53fE5CBF056E6C11B2"
},
"feeCollectorOperator": {
- "31337": {
- "Safe": "0x0829b195d2d53887cd2316c0acb390ef8fecaef9",
- "DevPortal": "0xA98F85F55F259ef41548251c93409F1D60e804e4"
- }
+ },
+ "feeCollector": {
},
"constructorArgs": {
- "UniV4Helper": {
- "31337": ["0x000000000004444c5dc75cB358380D2e3dE08A90", "0x7ffe42c4a5deea5b0fec41c94c136cf115597227", "0xbd216513d74c8cf14cf4747e6aaa6420ff64ee9e"]
- }
},
"leftoverExchangerOwner": {
- "31337": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
}
}
diff --git a/deploy/deploy-leftover-exchanger.js b/deploy/deploy-leftover-exchanger.js
index 9b3c701..a281160 100644
--- a/deploy/deploy-leftover-exchanger.js
+++ b/deploy/deploy-leftover-exchanger.js
@@ -63,4 +63,4 @@ module.exports = async ({ getNamedAccounts, deployments }) => {
}
};
-module.exports.skip = async () => false;
+module.exports.skip = async () => true;
diff --git a/deploy/deploy-new-fee-collector.js b/deploy/deploy-new-fee-collector.js
index ba13a19..c3ce42c 100644
--- a/deploy/deploy-new-fee-collector.js
+++ b/deploy/deploy-new-fee-collector.js
@@ -1,6 +1,9 @@
+const fs = require('fs');
+const path = require('path');
const hre = require('hardhat');
-const { getChainId, ethers } = hre;
+const { getChainId } = hre;
const constants = require('../config/constants');
+const { deployFeeCollectorForOperator } = require('./helpers/deploy-fee-collector-for-operator');
module.exports = async ({ config }) => {
console.log('running deploy script');
@@ -13,33 +16,18 @@ module.exports = async ({ config }) => {
return;
}
- for (const feeCollectorOperatorName of config.deployOpts.feeCollectorOperatorNames) {
- console.log('Deploying FeeCollector for operator name:', feeCollectorOperatorName);
+ const { feeCollectorOperatorName, feeCollectorOperator } = config.deployOpts;
- if (!constants.FEE_COLLECTOR_OPERATOR?.[chainId]?.[feeCollectorOperatorName]) {
- console.log(`Skipping deployment on chain ${chainId} as no operator is set for name ${feeCollectorOperatorName}`);
- continue;
- }
-
- const salt = ethers.keccak256(ethers.toUtf8Bytes(feeCollectorOperatorName)); // Use correct salt, for instance: from `deploy/upgrade-fee-collector.js`
-
- const feeCollectorFactory = await ethers.getContractAt('FeeCollectorFactory', constants.FEE_COLLECTOR_FACTORY[chainId]);
- await feeCollectorFactory.deployFeeCollector(salt);
- const feeCollectorAddress = await feeCollectorFactory.getFeeCollectorAddress(salt);
- console.log('FeeCollector deployed at', feeCollectorAddress);
+ if (!feeCollectorOperatorName || !feeCollectorOperator) {
+ console.log('Skipping deployment as feeCollectorOperatorName or feeCollectorOperator is not set');
+ return;
+ }
- if (chainId !== '31337' && process.env.OPS_SKIP_VERIFY !== 'true') {
- await hre.run('verify:verify', {
- address: feeCollectorAddress,
- constructorArguments: [constants.FEE_COLLECTOR_FACTORY[chainId], '0x'],
- });
- }
+ const feeCollectorAddress = await deployFeeCollectorForOperator(hre, chainId, feeCollectorOperatorName, feeCollectorOperator);
- const OPERATOR = constants.FEE_COLLECTOR_OPERATOR[chainId][feeCollectorOperatorName]; // Replace with the actual operator address
- const feeCollector = await ethers.getContractAt('FeeCollector', feeCollectorAddress);
- await feeCollector.setOperator(OPERATOR);
- console.log('feeCollectorOperator set to', feeCollectorAddress);
- }
+ const envOutputsPath = path.join(__dirname, '../.env.outputs');
+ fs.appendFileSync(envOutputsPath, `OPS_FEE_COLLECTOR_INSTANCE_ADDRESS=${feeCollectorAddress}\n`);
+ console.log(`Wrote OPS_FEE_COLLECTOR_INSTANCE_ADDRESS=${feeCollectorAddress} to .env.outputs`);
};
module.exports.skip = async () => true;
diff --git a/deploy/deploy-new-fee-collectors.js b/deploy/deploy-new-fee-collectors.js
new file mode 100644
index 0000000..27808ef
--- /dev/null
+++ b/deploy/deploy-new-fee-collectors.js
@@ -0,0 +1,28 @@
+const hre = require('hardhat');
+const { getChainId } = hre;
+const constants = require('../config/constants');
+const { deployFeeCollectorForOperator } = require('./helpers/deploy-fee-collector-for-operator');
+
+module.exports = async ({ config }) => {
+ console.log('running deploy script');
+ const chainId = await getChainId();
+ console.log('network id ', chainId);
+ console.log('deployOpts', config.deployOpts);
+
+ if (!constants.FEE_COLLECTOR_FACTORY[chainId]) {
+ console.log(`Skipping deployment on chain ${chainId} as no FeeCollectorFactory is set`);
+ return;
+ }
+
+ for (const feeCollectorOperatorName of config.deployOpts.feeCollectorOperatorNames) {
+ if (!constants.FEE_COLLECTOR_OPERATOR?.[chainId]?.[feeCollectorOperatorName]) {
+ console.log(`Skipping deployment on chain ${chainId} as no operator is set for name ${feeCollectorOperatorName}`);
+ continue;
+ }
+
+ const operatorAddress = constants.FEE_COLLECTOR_OPERATOR[chainId][feeCollectorOperatorName];
+ await deployFeeCollectorForOperator(hre, chainId, feeCollectorOperatorName, operatorAddress);
+ }
+};
+
+module.exports.skip = async () => true;
diff --git a/deploy/helpers/deploy-fee-collector-for-operator.js b/deploy/helpers/deploy-fee-collector-for-operator.js
new file mode 100644
index 0000000..10d52f8
--- /dev/null
+++ b/deploy/helpers/deploy-fee-collector-for-operator.js
@@ -0,0 +1,30 @@
+const constants = require('../../config/constants');
+
+async function deployFeeCollectorForOperator(hre, chainId, feeCollectorOperatorName, operatorAddress) {
+ const { ethers } = hre;
+
+ console.log('Deploying FeeCollector for operator name:', feeCollectorOperatorName);
+
+ const salt = feeCollectorOperatorName.startsWith('0x')
+ ? feeCollectorOperatorName
+ : ethers.keccak256(ethers.toUtf8Bytes(feeCollectorOperatorName));
+
+ console.log(`Using salt ${salt} for operator ${feeCollectorOperatorName}`);
+
+ const feeCollectorFactory = await ethers.getContractAt('FeeCollectorFactory', constants.FEE_COLLECTOR_FACTORY[chainId]);
+ const deployTx = await feeCollectorFactory.deployFeeCollector(salt);
+ await deployTx.wait();
+
+ const feeCollectorAddress = await feeCollectorFactory.getFeeCollectorAddress(salt);
+ console.log('FeeCollector deployed at', feeCollectorAddress);
+
+ const feeCollector = await ethers.getContractAt('FeeCollector', feeCollectorAddress);
+ tx = await feeCollector.setOperator(operatorAddress);
+ await tx.wait();
+ console.log('feeCollectorOperator set to', operatorAddress);
+
+ return feeCollectorAddress;
+}
+
+module.exports = { deployFeeCollectorForOperator };
+module.exports.skip = async () => true;
\ No newline at end of file
diff --git a/hardhat.config.js b/hardhat.config.js
index 5aa33c4..e8d0d05 100644
--- a/hardhat.config.js
+++ b/hardhat.config.js
@@ -55,6 +55,8 @@ module.exports = {
},
deployOpts: {
contractHelperConfigs: process.env.OPS_EVM_HELPER_CONFIGS ? JSON.parse(process.env.OPS_EVM_HELPER_CONFIGS) : [],
+ feeCollectorOperatorName: process.env.OPS_FEE_COLLECTOR_OPERATOR_NAME || '',
+ feeCollectorOperator: process.env.OPS_FEE_COLLECTOR_OPERATOR || '',
feeCollectorOperatorNames: process.env.OPS_FEE_COLLECTOR_OPERATOR_NAMES ? JSON.parse(process.env.OPS_FEE_COLLECTOR_OPERATOR_NAMES) : [],
deploymentMethod: process.env.OPS_DEPLOYMENT_METHOD,
},