Skip to content

Commit

Permalink
change the owner, add creator immutable state, update test
Browse files Browse the repository at this point in the history
  • Loading branch information
mdtanrikulu committed Nov 8, 2024
1 parent 5c7df17 commit 121f327
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 119 deletions.
9 changes: 1 addition & 8 deletions script/VerifiableFactory.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@ contract CounterScript is Script {

function run() public {
vm.startBroadcast();

// TODO complete it
VerifiableFactory.Verifiers[] memory verifiers = new VerifiableFactory.Verifiers[](3);
verifiers[0] = VerifiableFactory.Verifiers({networkId: 1, verifier: address(0)});
verifiers[1] = VerifiableFactory.Verifiers({networkId: 42, verifier: address(0)});
verifiers[2] = VerifiableFactory.Verifiers({networkId: 137, verifier: address(0)});

factory = new VerifiableFactory(verifiers);
factory = new VerifiableFactory();

vm.stopBroadcast();
}
Expand Down
14 changes: 11 additions & 3 deletions src/TransparentVerifiableProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ interface ITransparentVerifiableProxy {
}

contract TransparentVerifiableProxy is Proxy, Initializable {
uint256 public salt; // Salt, being used creating the proxy
address public owner; // The owner of the proxy contract
// immutable variable (in bytecode)
address immutable public creator;

// storage variables (in storage slots)
uint256 public salt; // Salt, being used creating the proxy (slot 0)
address public owner; // The owner of the proxy contract (slot 1)

// ### EVENTS
error ProxyDeniedOwnerAccess();
Expand All @@ -30,6 +34,10 @@ contract TransparentVerifiableProxy is Proxy, Initializable {
// _;
// }

constructor(address _creator) {
creator = _creator;
}

/**
* @dev Initializes the verifiable proxy with an initial implementation specified by `implementation`.
*
Expand Down Expand Up @@ -78,7 +86,7 @@ contract TransparentVerifiableProxy is Proxy, Initializable {
* @dev If caller is the owner, process the call internally, otherwise transparently fallback to the proxy behavior.
*/
function _fallback() internal virtual override {
if (msg.sender == owner) {
if (msg.sender == creator) {
if (
msg.sig != ITransparentVerifiableProxy.upgradeToAndCall.selector
) {
Expand Down
79 changes: 42 additions & 37 deletions src/VerifiableFactory.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {console} from "forge-std/console.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {ITransparentVerifiableProxy, TransparentVerifiableProxy} from "./TransparentVerifiableProxy.sol";

interface IProxy {
function salt() external view returns (uint256);

function owner() external view returns (address);

function creator() external view returns (address);
}

contract VerifiableFactory {

event ProxyDeployed(
address indexed sender,
address indexed proxyAddress,
Expand All @@ -24,12 +26,12 @@ contract VerifiableFactory {
/**
* @dev Deploys a new `TransparentVerifiableProxy` contract at a deterministic address.
*
* This function deploys a proxy contract using the CREATE2 opcode, ensuring a predictable
* address based on the sender's address and a provided salt. The deployed proxy is
* This function deploys a proxy contract using the CREATE2 opcode, ensuring a predictable
* address based on the sender's address and a provided salt. The deployed proxy is
* controlled by the factory and is initialized to use a specific implementation.
*
* - A unique address for the proxy is generated using the caller's address and the salt.
* - After deployment, the proxy's `initialize` function is called to configure it with the given salt,
* - After deployment, the proxy's `initialize` function is called to configure it with the given salt,
* the factory address, and the provided implementation address.
* - The proxy is fully managed by the factory, which controls upgrades and other administrative methods.
* - The event `ProxyDeployed` is emitted, logging details of the deployment including the sender, proxy address, salt, and implementation.
Expand All @@ -42,15 +44,17 @@ contract VerifiableFactory {
address implementation,
uint256 salt
) external returns (address) {
console.log("deploys");
console.logAddress(msg.sender);
bytes32 outerSalt = keccak256(abi.encode(msg.sender, salt));

TransparentVerifiableProxy proxy = new TransparentVerifiableProxy{
salt: outerSalt
}();
}(address(this));

require(isContract(address(proxy)), "Proxy deployment failed");

proxy.initialize(salt, address(this), implementation, "");
proxy.initialize(salt, msg.sender, implementation, "");

emit ProxyDeployed(msg.sender, address(proxy), salt, implementation);
return address(proxy);
Expand All @@ -62,7 +66,8 @@ contract VerifiableFactory {
address newImplementation,
bytes memory data
) external {
require(verifyContract(proxyAddress), "Only the owner can upgrade");
address owner = IProxy(proxyAddress).owner();
require(owner == msg.sender, "Only the owner can upgrade");

// Upgrade the proxy to point to the new implementation
ITransparentVerifiableProxy(payable(proxyAddress)).upgradeToAndCall(
Expand All @@ -74,44 +79,44 @@ contract VerifiableFactory {
/**
* @dev Initiates verification of a proxy contract.
*
* This function attempts to validate a proxy contract by retrieving its salt
* and reconstructing the address to ensure it was correctly deployed by the
* This function attempts to validate a proxy contract by retrieving its salt
* and reconstructing the address to ensure it was correctly deployed by the
* current factory.
*
* @param proxy The address of the proxy contract being verified.
* @return A boolean indicating whether the verification succeeded.
*/
function verifyContract(address proxy) public view returns (bool) {
// directly fetch storage
try IProxy(proxy).salt() returns (uint256 salt) {
address owner = IProxy(proxy).owner();

require(
address(this) == owner,
"Proxy owner does not match with factory address"
);

// reconstruct the address using CREATE2 and the original salt
bytes32 outerSalt = keccak256(abi.encode(msg.sender, salt));

// bytes memory bytecode = abi.encodePacked(
// type(TransparentVerifiableProxy).creationCode,
// abi.encode(salt, address(this))
// );

// Compute the expected proxy address using the outerSalt
address expectedProxyAddress = Create2.computeAddress(
outerSalt,
keccak256(type(TransparentVerifiableProxy).creationCode)
);

// Verify if the computed address matches the proxy address
require(expectedProxyAddress == proxy, "Proxy address mismatch");

return true;
} catch {
if (!isContract(proxy)) {
return false;
}
try IProxy(proxy).salt() returns (uint256 salt) {
try IProxy(proxy).creator() returns (address creator) {
// verify the creator matches this factory
if (address(this) != creator) {
return false;
}

// reconstruct the address using CREATE2 and verify it matches
bytes32 outerSalt = keccak256(abi.encode(msg.sender, salt));

// get creation bytecode with constructor arguments
bytes memory bytecode = abi.encodePacked(
type(TransparentVerifiableProxy).creationCode,
abi.encode(address(this))
);

address expectedProxyAddress = Create2.computeAddress(
outerSalt,
keccak256(bytecode),
address(this)
);

return expectedProxyAddress == proxy;
} catch {}
} catch {}

return false;
}

function isContract(address account) internal view returns (bool) {
Expand Down
53 changes: 53 additions & 0 deletions src/mock/MockRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
* @title MockRegistry
* @dev Simulates a registry implementation for testing
*/
contract MockRegistry {
mapping(address => bool) public registeredAddresses;
address public admin;
uint256 public constant version = 1;

// ### Events
event AddressRegistered(address indexed account);
event AddressUnregistered(address indexed account);
event AdminChanged(address indexed oldAdmin, address indexed newAdmin);

constructor() {
admin = msg.sender;
}

function register(address account) external {
require(!registeredAddresses[account], "Address already registered");
registeredAddresses[account] = true;
emit AddressRegistered(account);
}

function unregister(address account) external {
require(registeredAddresses[account], "Address not registered");
registeredAddresses[account] = false;
emit AddressUnregistered(account);
}

function isRegistered(address account) external view returns (bool) {
return registeredAddresses[account];
}

function changeAdmin(address newAdmin) external {
require(msg.sender == admin, "Only admin can change admin");
require(newAdmin != address(0), "New admin cannot be zero address");
emit AdminChanged(admin, newAdmin);
admin = newAdmin;
}

function getRegistryVersion() public pure virtual returns (uint256) {
return version;
}

function initialize(address _admin) external {
require(admin == address(0), "Already initialized");
admin = _admin;
}
}
10 changes: 10 additions & 0 deletions src/mock/MockRegistryV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {MockRegistry} from './MockRegistry.sol';

contract MockRegistryV2 is MockRegistry {
function getRegistryVersion() public pure override returns (uint256) {
return 2;
}
}
Loading

0 comments on commit 121f327

Please sign in to comment.