Skip to content

Commit

Permalink
document proxies contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
mmv08 committed Feb 23, 2023
1 parent 9edb94d commit 8b9023d
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 32 deletions.
11 changes: 11 additions & 0 deletions contracts/proxies/IProxyCreationCallback.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
pragma solidity >=0.7.0 <0.9.0;
import "./SafeProxy.sol";

/**
* @title IProxyCreationCallback
* @dev An interface for a contract that implements a callback function to be executed after the creation of a proxy instance.
*/
interface IProxyCreationCallback {
/**
* @dev Function to be called after the creation of a SafeProxy instance.
* @param proxy The newly created SafeProxy instance.
* @param _singleton The address of the singleton contract used to create the proxy.
* @param initializer The initializer function call data.
* @param saltNonce The nonce used to generate the salt for the proxy deployment.
*/
function proxyCreated(SafeProxy proxy, address _singleton, bytes calldata initializer, uint256 saltNonce) external;
}
22 changes: 14 additions & 8 deletions contracts/proxies/SafeProxy.sol
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

/// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
/// @author Richard Meissner - <[email protected]>
/**
* @title IProxy - Helper interface to access the singleton address of the Proxy on-chain.
* @author Richard Meissner - @rmeissner
*/
interface IProxy {
function masterCopy() external view returns (address);
}

/// @title SafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
/// @author Stefan George - <[email protected]>
/// @author Richard Meissner - <[email protected]>
/**
* @title SafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
* @author Stefan George - <[email protected]>
* @author Richard Meissner - <[email protected]>
*/
contract SafeProxy {
// singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
// Singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
// To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
address internal singleton;

/// @dev Constructor function sets address of singleton contract.
/// @param _singleton Singleton address.
/**
* @notice Constructor function sets address of singleton contract.
* @param _singleton Singleton address.
*/
constructor(address _singleton) {
require(_singleton != address(0), "Invalid singleton address provided");
singleton = _singleton;
Expand Down
68 changes: 44 additions & 24 deletions contracts/proxies/SafeProxyFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ pragma solidity >=0.7.0 <0.9.0;
import "./SafeProxy.sol";
import "./IProxyCreationCallback.sol";

/// @title Proxy Factory - Allows to create a new proxy contract and execute a message call to the new proxy within one transaction.
/// @author Stefan George - <[email protected]>
/**
* @title Proxy Factory - Allows to create a new proxy contract and execute a message call to the new proxy within one transaction.
* @author Stefan George - @Georgi87
*/
contract SafeProxyFactory {
event ProxyCreation(SafeProxy proxy, address singleton);

Expand All @@ -14,11 +16,13 @@ contract SafeProxyFactory {
return type(SafeProxy).creationCode;
}

/// @dev Allows to create a new proxy contract using CREATE2. Optionally executes an initializer call to a new proxy.
/// This method is only meant as an utility to be called from other methods
/// @param _singleton Address of singleton contract. Must be deployed at the time of execution.
/// @param initializer Payload for a message call to be sent to a new proxy contract.
/// @param salt Create2 salt to use for calculating the address of the new proxy contract.
/**
* @notice Internal method to create a new proxy contract using CREATE2. Optionally executes an initializer call to a new proxy.
* @param _singleton Address of singleton contract. Must be deployed at the time of execution.
* @param initializer (Optional) Payload for a message call to be sent to a new proxy contract.
* @param salt Create2 salt to use for calculating the address of the new proxy contract.
* @return proxy Address of the new proxy contract.
*/
function deployProxy(address _singleton, bytes memory initializer, bytes32 salt) internal returns (SafeProxy proxy) {
require(isContract(_singleton), "Singleton contract not deployed");

Expand All @@ -39,22 +43,27 @@ contract SafeProxyFactory {
}
}

/// @dev Allows to create a new proxy contract and execute a message call to the new proxy within one transaction.
/// @param _singleton Address of singleton contract. Must be deployed at the time of execution.
/// @param initializer Payload for a message call to be sent to a new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
/**
* @notice Deploys a new proxy with `_singleton` singleton and `saltNonce` salt. Optionally executes an initializer call to a new proxy.
* @param _singleton Address of singleton contract. Must be deployed at the time of execution.
* @param initializer Payload for a message call to be sent to a new proxy contract.
* @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
*/
function createProxyWithNonce(address _singleton, bytes memory initializer, uint256 saltNonce) public returns (SafeProxy proxy) {
// If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
proxy = deployProxy(_singleton, initializer, salt);
emit ProxyCreation(proxy, _singleton);
}

/// @dev Allows to create a new proxy contract that should exist only on 1 network (e.g. specific governance or admin accounts)
/// by including the chain id in the create2 salt. Such proxies cannot be created on other networks by replaying the transaction.
/// @param _singleton Address of singleton contract. Must be deployed at the time of execution.
/// @param initializer Payload for a message call to be sent to a new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
/**
* @notice Deploys a new chain-specific proxy with `_singleton` singleton and `saltNonce` salt. Optionally executes an initializer call to a new proxy.
* @dev Allows to create a new proxy contract that should exist only on 1 network (e.g. specific governance or admin accounts)
* by including the chain id in the create2 salt. Such proxies cannot be created on other networks by replaying the transaction.
* @param _singleton Address of singleton contract. Must be deployed at the time of execution.
* @param initializer Payload for a message call to be sent to a new proxy contract.
* @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
*/
function createChainSpecificProxyWithNonce(
address _singleton,
bytes memory initializer,
Expand All @@ -66,11 +75,14 @@ contract SafeProxyFactory {
emit ProxyCreation(proxy, _singleton);
}

/// @dev Allows to create a new proxy contract, execute a message call to the new proxy and call a specified callback within one transaction
/// @param _singleton Address of singleton contract. Must be deployed at the time of execution.
/// @param initializer Payload for a message call to be sent to a new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
/// @param callback Callback that will be invoked after the new proxy contract has been successfully deployed and initialized.
/**
* @notice Deploy a new proxy with `_singleton` singleton and `saltNonce` salt.
* Optionally executes an initializer call to a new proxy and calls a specified callback address `callback`.
* @param _singleton Address of singleton contract. Must be deployed at the time of execution.
* @param initializer Payload for a message call to be sent to a new proxy contract.
* @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
* @param callback Callback that will be invoked after the new proxy contract has been successfully deployed and initialized.
*/
function createProxyWithCallback(
address _singleton,
bytes memory initializer,
Expand All @@ -82,8 +94,13 @@ contract SafeProxyFactory {
if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
}

/// @dev Returns true if `account` is a contract.
/// @param account The address being queried
/**
* @notice Returns true if `account` is a contract.
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account The address being queried
* @return True if `account` is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// solhint-disable-next-line no-inline-assembly
Expand All @@ -93,7 +110,10 @@ contract SafeProxyFactory {
return size > 0;
}

/// @dev Returns the chain id used by this contract.
/**
* @notice Returns the ID of the chain the contract is currently deployed on.
* @return The ID of the current chain as a uint256.
*/
function getChainId() public view returns (uint256) {
uint256 id;
// solhint-disable-next-line no-inline-assembly
Expand Down

0 comments on commit 8b9023d

Please sign in to comment.