Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import {Script, console} from "forge-std/Script.sol";
import {IBatchAuthenticator} from "interfaces/L1/IBatchAuthenticator.sol";
import {ISystemConfig} from "interfaces/L1/ISystemConfig.sol";
import {IEspressoTEEVerifier} from "@espresso-tee-contracts/interface/IEspressoTEEVerifier.sol";
import {ProxyAdmin} from "src/universal/ProxyAdmin.sol";
import {Proxy} from "src/universal/Proxy.sol";
import {BatchAuthenticator} from "src/L1/BatchAuthenticator.sol";

/// @notice Deploys only the BatchAuthenticator (proxy + impl) against an existing TEEVerifier.
///
/// Usage:
/// forge script scripts/deploy/DeployBatchAuthenticator.s.sol:DeployBatchAuthenticator \
/// --rpc-url <L1_RPC_URL> \
/// --broadcast \
/// --private-key <DEPLOYER_KEY> \
/// --verify \
/// --etherscan-api-key <API_KEY> \
/// --sig "run(address,address,address,address)" \
/// <ESPRESSO_BATCHER_ADDRESS> \
/// <SYSTEM_CONFIG_ADDRESS> \
/// <TEE_VERIFIER_ADDRESS> \
/// <PROXY_ADMIN_OWNER>
contract DeployBatchAuthenticator is Script {
function run(
address espressoBatcher,
address systemConfig,
address teeVerifier,
address proxyAdminOwner
) public {
require(espressoBatcher != address(0), "espressoBatcher required");
require(systemConfig != address(0), "systemConfig required");
require(teeVerifier != address(0), "teeVerifier required");

Comment thread
jjeangal marked this conversation as resolved.
if (proxyAdminOwner == address(0)) {
proxyAdminOwner = msg.sender;
console.log("WARN: proxyAdminOwner not set, defaulting to msg.sender");
}

vm.startBroadcast(msg.sender);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The proxyAdminOwner is passed to BatchAuthenticator.initialize as the _owner parameter (line 55), which calls __OwnableWithGuardians_init(_owner). But the ProxyAdmin is created with msg.sender as the owner (line 42), and upgradeAndCall is gated by _assertOnlyProxyAdminOrProxyAdminOwner() inside initialize.

This works because msg.sender owns the ProxyAdmin at the time of upgradeAndCall, so the assertion passes. However, after ownership is transferred on line 63, the proxyAdminOwner becomes the ProxyAdmin owner while msg.sender remains the BatchAuthenticator owner set via __OwnableWithGuardians_init — wait, no, proxyAdminOwner is passed as _owner to initialize. That's correct.

One subtle issue: if the caller intends proxyAdminOwner to be a multisig or timelock, the ProxyAdmin ownership is transferred (line 63), but the BatchAuthenticator's Ownable owner is also set to proxyAdminOwner via the initialize call. This means the same address controls both the proxy upgrade path AND the contract's own admin functions. This coupling may be intentional, but it's worth calling out in a comment or the NatSpec so future operators understand the ownership model.

ProxyAdmin proxyAdmin = new ProxyAdmin(msg.sender);
vm.label(address(proxyAdmin), "BatchAuthenticatorProxyAdmin");
Proxy proxy = new Proxy(address(proxyAdmin));
vm.label(address(proxy), "BatchAuthenticatorProxy");
proxyAdmin.setProxyType(address(proxy), ProxyAdmin.ProxyType.ERC1967);
BatchAuthenticator impl = new BatchAuthenticator();
Comment thread
jjeangal marked this conversation as resolved.
vm.label(address(impl), "BatchAuthenticatorImpl");

bytes memory initData = abi.encodeCall(
BatchAuthenticator.initialize,
(
IEspressoTEEVerifier(teeVerifier),
espressoBatcher,
ISystemConfig(systemConfig),
proxyAdminOwner
)
);
proxyAdmin.upgradeAndCall(
payable(address(proxy)),
address(impl),
initData
);

if (proxyAdminOwner != msg.sender) {
proxyAdmin.transferOwnership(proxyAdminOwner);
}

vm.stopBroadcast();

console.log("BatchAuthenticator (proxy):", address(proxy));
console.log("BatchAuthenticator (impl): ", address(impl));
console.log("ProxyAdmin: ", address(proxyAdmin));
}
}
Loading