Skip to content

Commit

Permalink
update VerifiableFactory with ProxyAdmin logic
Browse files Browse the repository at this point in the history
  • Loading branch information
mdtanrikulu committed Oct 16, 2024
1 parent 016c335 commit e2b97ba
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 152 deletions.
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/clones-with-immutable-args"]
path = lib/clones-with-immutable-args
url = https://github.com/wighawag/clones-with-immutable-args
[submodule "lib/evmgateway"]
path = lib/evmgateway
url = https://github.com/ensdomains/evmgateway
1 change: 0 additions & 1 deletion lib/clones-with-immutable-args
Submodule clones-with-immutable-args deleted from 595072
3 changes: 1 addition & 2 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@ensdomains/evm-verifier/=lib/evmgateway/evm-verifier/contracts/
clones-with-immutable-args/=lib/clones-with-immutable-args/src/
@ensdomains/evm-verifier/=lib/evmgateway/evm-verifier/contracts/
13 changes: 7 additions & 6 deletions script/VerifiableFactory.s.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-License-Identifier: UNLICENSED
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Script, console} from "forge-std/Script.sol";
Expand All @@ -12,12 +12,13 @@ contract CounterScript is Script {
function run() public {
vm.startBroadcast();

string[] memory urls;
// initialize the ccip-read urls
urls = new string[](1);
urls[0] = "";
// 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(urls, bytes32(0));
factory = new VerifiableFactory(verifiers);

vm.stopBroadcast();
}
Expand Down
23 changes: 23 additions & 0 deletions src/RegistryProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract RegistryProxy {
uint256 public nonce; // slot 0
address public registry; // slot 1
address public admin; // slot 2

modifier onlyAdmin() {
require(msg.sender == admin, "Caller is not the admin");
_;
}

function initialize(uint256 _nonce, address _admin) external {
require(nonce == 0, "Already initialized");
nonce = _nonce;
admin = _admin; // set the admin (ProxyAdmin contract address)
}

function updateRegistry(address newRegistry) external onlyAdmin {
registry = newRegistry;
}
}
154 changes: 88 additions & 66 deletions src/VerifiableFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,72 @@ import {EVMFetcher} from "@ensdomains/evm-verifier/EVMFetcher.sol";
import {EVMFetchTarget} from "@ensdomains/evm-verifier/EVMFetchTarget.sol";
import {IEVMVerifier} from "@ensdomains/evm-verifier/IEVMVerifier.sol";

import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

contract ProxyFactory is EVMFetchTarget {
import { RegistryProxy } from './RegistryProxy.sol';

contract VerifiableFactory is EVMFetchTarget {
using EVMFetcher for EVMFetcher.EVMFetchRequest;

IEVMVerifier public immutable verifier;
struct Verifiers {
uint256 networkId;
address verifier;
}

ProxyAdmin public proxyAdmin;
uint256 constant PROXY_NONCE_SLOT = 0;
uint256 constant PROXY_REGISTRY_SLOT = 1;

mapping(uint256 => IEVMVerifier) public verifiers;

event ProxyDeployed(address indexed proxyAddress);

constructor(IEVMVerifier _verifier) {
constructor(Verifiers[] memory _verifiers) {
require(
address(_verifier) != address(0),
"Verifier address must be set"
_verifiers.length > 0,
"At least one verifier address must be set"
);
verifier = _verifier;

for (uint256 i = 0; i < _verifiers.length; i++) {
verifiers[_verifiers[i].networkId] = IEVMVerifier(_verifiers[i].verifier);
}

proxyAdmin = new ProxyAdmin(address(this));
}

function deployProxy(
address implementation,
uint256 nonce
) external returns (address) {

/**
* @dev deploys a new `TransparentUpgradeableProxy` contract using a deterministic address derived from
* the sender's address and a nonce.
*
* The function creates a new proxy contract that is controlled by the factory's `ProxyAdmin`.
* When the proxy is deployed, it starts by using the `RegistryProxy` contract as its main implementation.
* During the deployment, the initialize function is called to set up the proxy.
* The nonce ensures that each user gets a unique proxy, even if the same user deploys multiple proxies.
*
* - The function uses a `salt` to create a deterministic address based on `msg.sender` and a provided nonce.
* - The `initialize` function of the `RegistryProxy` contract is called immediately after deployment to set up
* the proxy with the nonce and `ProxyAdmin`.
* - The proxy is managed by a `ProxyAdmin` contract, ensuring that upgrades and critical functions are restricted to the admin.
* - A custom event `ProxyDeployed` is emitted to track the deployment of the new proxy.
*
* @param nonce A unique number provided by the caller to create a unique proxy address.
* @return proxy The address of the deployed `TransparentUpgradeableProxy` contract.
*/
function deployProxy(uint256 nonce) external returns (address) {
bytes32 salt = keccak256(abi.encodePacked(msg.sender, nonce));
bytes memory data = abi.encodeWithSignature(
"initialize(uint256)",
nonce
nonce,
address(proxyAdmin)
);

RegistryProxy proxyInstance = new RegistryProxy();
address proxy = address(
new TransparentUpgradeableProxy{salt: salt}(
implementation,
address(this),
address(proxyInstance),
address(proxyAdmin),
data
)
);
Expand All @@ -48,54 +81,59 @@ contract ProxyFactory is EVMFetchTarget {
return proxy;
}

function updateRegistry(address proxy, address newRegistry) external {
(bool success, bytes memory result) = proxy.staticcall(
abi.encodeWithSignature("nonce()")
);
require(success, "Failed to retrieve nonce from proxy");
uint256 proxyNonce = abi.decode(result, (uint256));

bytes32 salt = keccak256(abi.encodePacked(msg.sender, proxyNonce));
address expectedProxyAddress = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(
type(TransparentUpgradeableProxy).creationCode
)
)
)
)
)
);
require(
expectedProxyAddress == proxy,
"Only the creator can update the registry"
);

(success, ) = proxy.call(
abi.encodeWithSignature("updateRegistry(address)", newRegistry)
);
require(success, "Registry update failed");
function updateRegistry(address proxy, address newImplementation) external {
// todo
}

function verifyContract(address proxy) public view {
/**
* @dev verifies a proxy contract on a specified network using the EVMGateway.
*
* The function starts the process of verifying a specific proxy contract by sending a request
* to the correct IEVMVerifier for the given network. It then retrieves fixed values from certain
* storage slots (like the proxy's nonce and registry address) to help with the verification
*
* The verification request is sent through the `EVMFetcher` to the gateway, and the result
* is processed by the `verifyCallback` function upon completion.
*
* - The function looks up the correct verifier for the network based on the provided `networkId`.
* - It retrieves static values from storage slots (`PROXY_NONCE_SLOT` and `PROXY_REGISTRY_SLOT`) to assist in the verification process.
* - The fetched data is passed to `verifyCallback` for further processing.
*
* @param proxy The address of the proxy contract to be verified.
* @param networkId The ID of the network where the verification will take place.
*/
function verifyContract(address proxy, uint256 networkId) public view {
IEVMVerifier verifier = verifiers[networkId];
require(address(verifier) != address(0), "Verifier is not available for the given netowrk");

EVMFetcher
.newFetchRequest(verifier, proxy)
.newFetchRequest(verifiers[networkId], proxy)
.getStatic(PROXY_NONCE_SLOT)
.getStatic(PROXY_REGISTRY_SLOT)
.fetch(this.verifyCallback.selector, abi.encode(proxy));
}

/**
* @dev callback function that processes the response from the EVMGateway after fetching the verification data.
*
* This function decodes the fetched data and verifies the correctness of the proxy contract's address by
* calculating the expected address using a salt derived from the sender's address and the proxy's nonce.
*
* - The response data contains the nonce, registry address, and extra data (proxy address) retrieved from both verfiyContract and gateway requests.
* - The proxy address is reconstructed using the same logic used during deployment to ensure it matches the expected proxy address.
* - If the proxy address matches, the function returns `true`, indicating a successful verification.
*
* @param response The response data retrieved from the EVMGateway, containing the nonce, registry address, and extra data.
* @return bool Returns `true` if the proxy address matches the expected address, otherwise the function will revert.
*/
function verifyCallback(
bytes calldata response
) public view returns (bool) {
(uint256 nonce, /*address registryAddress*/, bytes memory extraData) = abi
.decode(response, (uint256, address, bytes));
(
uint256 nonce /*address registryAddress*/,
,
bytes memory extraData
) = abi.decode(response, (uint256, address, bytes));
address proxy = abi.decode(extraData, (address));

bytes32 salt = keccak256(abi.encodePacked(msg.sender, nonce));
Expand Down Expand Up @@ -130,19 +168,3 @@ contract ProxyFactory is EVMFetchTarget {
return size > 0;
}
}

// RegistryProxy Contract
contract RegistryProxy {
uint256 public nonce;
address public registry;

function updateRegistry(address newRegistry) external {
require(msg.sender == registry, "Only owner can update");
registry = newRegistry;
}

function initialize(uint256 _nonce) external {
require(nonce == 0, "Already initialized");
nonce = _nonce;
}
}
2 changes: 1 addition & 1 deletion src/ChildContract.sol → src/mock/MockRegistry.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract ChildContract {
contract MockRegistry {
uint256 public value;
address public factory;

Expand Down
17 changes: 0 additions & 17 deletions src/mock/VerifyCreate2Harness.sol

This file was deleted.

Loading

0 comments on commit e2b97ba

Please sign in to comment.