Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Document proxies/ and test/ contracts #518

Merged
merged 2 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
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
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
50 changes: 33 additions & 17 deletions contracts/test/ERC1155Token.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ pragma solidity >=0.7.0 <0.9.0;
import "../interfaces/ERC1155TokenReceiver.sol";
import "../external/SafeMath.sol";

/**
* @title ERC1155Token - A test ERC1155 token contract
*/
contract ERC1155Token {
using SafeMath for uint256;

Expand All @@ -14,26 +17,26 @@ contract ERC1155Token {
mapping(address => mapping(address => bool)) private _operatorApprovals;

/**
@dev Get the specified address' balance for token with specified ID.
@param owner The address of the token holder
@param id ID of the token
@return The owner's balance of the token type requested
* @dev Get the specified address' balance for token with specified ID.
* @param owner The address of the token holder
* @param id ID of the token
* @return The owner's balance of the token type requested
*/
function balanceOf(address owner, uint256 id) public view returns (uint256) {
require(owner != address(0), "ERC1155: balance query for the zero address");
return _balances[id][owner];
}

/**
@dev Transfers `value` amount of an `id` from the `from` address to the `to` address specified.
Caller must be approved to manage the tokens being transferred out of the `from` account.
If `to` is a smart contract, will call `onERC1155Received` on `to` and act appropriately.
@param from Source address
@param to Target address
@param id ID of the token type
@param value Transfer amount
@param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
* @notice Transfers `value` amount of an `id` from the `from` address to the `to` address specified.
* Caller must be approved to manage the tokens being transferred out of the `from` account.
* If `to` is a smart contract, will call `onERC1155Received` on `to` and act appropriately.
* @param from Source address
* @param to Target address
* @param id ID of the token type
* @param value Transfer amount
* @param data Data forwarded to `onERC1155Received` if `to` is a contract receiver
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external {
require(to != address(0), "ERC1155: target address must be non-zero");
require(
Expand Down Expand Up @@ -62,11 +65,14 @@ contract ERC1155Token {
_doSafeTransferAcceptanceCheck(msg.sender, address(0), to, id, value, data);
}

/**
* @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) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.

uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly {
Expand All @@ -75,6 +81,16 @@ contract ERC1155Token {
return size > 0;
}

/**
* @dev Internal function to invoke `onERC1155Received` on a target address
* The call is not executed if the target address is not a contract
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param to The address which will now own the token
* @param id The id of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
*/
function _doSafeTransferAcceptanceCheck(
address operator,
address from,
Expand Down
7 changes: 7 additions & 0 deletions contracts/test/ERC20Token.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ pragma solidity >=0.6.0 <0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

/**
* @title ERC20Token
* @dev This contract is an ERC20 token contract that extends the OpenZeppelin ERC20 contract.
*/
contract ERC20Token is ERC20 {
/**
* @dev Constructor that sets the name and symbol of the token and mints an initial supply to the contract deployer.
*/
constructor() public ERC20("TestToken", "TT") {
_mint(msg.sender, 1000000000000000);
}
Expand Down
8 changes: 8 additions & 0 deletions contracts/test/TestHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ pragma solidity >=0.7.0 <0.9.0;

import "../handler/HandlerContext.sol";

/**
* @title TestHandler - A test FallbackHandler contract
*/
contract TestHandler is HandlerContext {
/**
* @notice Returns the sender and manager address provided by the HandlerContext
* @return sender The sender address
* @return manager The manager address
*/
function dudududu() external view returns (address sender, address manager) {
return (_msgSender(), _manager());
}
Expand Down