Skip to content

Commit

Permalink
chore: natspec, checksums, whitespace, cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
deluca-mike committed Dec 24, 2021
1 parent dd23e9d commit e6f18a0
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 67 deletions.
38 changes: 19 additions & 19 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
version: 2.1

jobs:
dapp_test:
docker:
- image: bakii0499/dapptools:0.48.0-solc-0.8.7
steps:
- run:
name: Checkout proxy-factory
command: |
GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git clone git@github.com:maple-labs/proxy-factory.git .
git checkout $CIRCLE_BRANCH
- run:
name: Build and test contracts
command: |
GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git submodule update --init --recursive
./test.sh -c ./config/ci.json
dapp_test:
docker:
- image: bakii0499/dapptools:0.48.0-solc-0.8.7
steps:
- run:
name: Checkout proxy-factory
command: |
GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git clone git@github.com:maple-labs/proxy-factory.git .
git checkout $CIRCLE_BRANCH
- run:
name: Build and test contracts
command: |
GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" git submodule update --init --recursive
./test.sh -c ./config/ci.json
workflows:
version: 2
test_all:
jobs:
- dapp_test:
context: seth
version: 2
test_all:
jobs:
- dapp_test:
context: seth
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/out
hevm*
.vscode/*
artifacts/*
docs/*
metadata.json
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Set of base contracts to deploy and manage versions on chain, designed to be min
### Features
- **No interfaces:** Contracts only define internal functionality and do not expose any external interfaces. Implementers are encouraged to mix and match the internal functions to cater to their specific needs.

- **Opt-In Upgrades:** Proxy contracts were designed to be upgraded individually.
- **Opt-In Upgrades:** Proxy contracts were designed to be upgraded individually.

- **CREATE2:** Contracts can be deployed with CREATE or CREATE2 opcodes, allowing the option for Proxy contracts to be deployed with deterministic addresses.

Expand All @@ -25,27 +25,27 @@ Responsible for deploying new Proxy instances and triggering initialization and
```js
contract ProxyFactory {

/// @notice Deploys a new proxy for some version, with some initialization arguments, using `create` (i.e. factory's nonce determines the address).
/// @dev Deploys a new proxy for some version, with some initialization arguments, using `create` (i.e. factory's nonce determines the address).
function _newInstance(uint256 version_, bytes memory arguments_) internal virtual returns (bool success_, address proxy_);

/// @notice Deploys a new proxy, with some initialization arguments, using `create2` (i.e. salt determines the address).
/// @dev This factory needs to be IDefaultImplementationBeacon, since the proxy will pull its implementation from it.
/// @dev Deploys a new proxy, with some initialization arguments, using `create2` (i.e. salt determines the address).
/// This factory needs to be IDefaultImplementationBeacon, since the proxy will pull its implementation from it.
function _newInstance(bytes memory arguments_, bytes32 salt_) internal virtual returns (bool success_, address proxy_);

/// @notice Registers an implementation for some version.
/// @dev Registers an implementation for some version.
function _registerImplementation(uint256 version_, address implementation_) internal virtual returns (bool success_);

/// @notice Registers a migrator for between two versions. If `fromVersion_ == toVersion_`, migrator is an initializer.
/// @dev Registers a migrator for between two versions. If `fromVersion_ == toVersion_`, migrator is an initializer.
function _registerMigrator(uint256 fromVersion_, uint256 toVersion_, address migrator_) internal virtual returns (bool success_);

/// @notice Upgrades a proxy to a new version of an implementation, with some migration arguments.
/// @dev Inheritor should revert on `success_ = false`, since proxy can be set to new implementation, but failed to migrate.
/// @dev Upgrades a proxy to a new version of an implementation, with some migration arguments.
/// Inheritor should revert on `success_ = false`, since proxy can be set to new implementation, but failed to migrate.
function _upgradeInstance(address proxy_, uint256 toVersion_, bytes memory arguments_) internal virtual returns (bool success_);

/// @notice Returns the deterministic address of a proxy given some salt.
/// @dev Returns the deterministic address of a proxy given some salt.
function _getDeterministicProxyAddress(bytes32 salt_) internal virtual view returns (address deterministicProxyAddress_);

/// @notice Returns whether the account is currently a contract.
/// @dev Returns whether the account is currently a contract.
function _isContract(address account_) internal view returns (bool isContract_);

}
Expand All @@ -59,13 +59,13 @@ Helper contract that can manually modify storage when necessary (i.e., during a
contract SlotManipulatable {

/// @dev Returns the value stored at the given slot.
function _getSlotValue(bytes32 slot_) internal view returns (bytes32 value_);
function _getSlotValue(bytes32 slot_) internal view returns (bytes32 value_);

/// @dev Sets the value stored at the given slot.
function _setSlotValue(bytes32 slot_, bytes32 value_) internal;
function _setSlotValue(bytes32 slot_, bytes32 value_) internal;

// @dev Returns the storage slot for a reference type.
function _getReferenceTypeSlot(bytes32 slot_, bytes32 key_) internal pure returns (bytes32 value_);
function _getReferenceTypeSlot(bytes32 slot_, bytes32 key_) internal pure returns (bytes32 value_);

}
```
Expand Down
6 changes: 3 additions & 3 deletions contracts/Proxied.sol → contracts/ProxiedInternals.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ pragma solidity ^0.8.7;
import { SlotManipulatable } from "./SlotManipulatable.sol";

/// @title An implementation that is to be proxied, must extend Proxied.
contract Proxied is SlotManipulatable {
abstract contract ProxiedInternals is SlotManipulatable {

/// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.factory') - 1`.
bytes32 private constant FACTORY_SLOT = bytes32(0x7a45a402e4cb6e08ebc196f20f66d5d30e67285a2a8aa80503fa409e727a4af1);

/// @dev Storage slot with the address of the current factory. `keccak256('eip1967.proxy.implementation') - 1`.
bytes32 private constant IMPLEMENTATION_SLOT = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);

/// @dev Delegatecalls to a migrator contract to manipulate storage during an inititalization or migration.
/// @dev Delegatecalls to a migrator contract to manipulate storage during an initialization or migration.
function _migrate(address migrator_, bytes calldata arguments_) internal virtual returns (bool success_) {
uint256 size;

Expand All @@ -37,7 +37,7 @@ contract Proxied is SlotManipulatable {
return true;
}

//// @dev Returns the factory address.
/// @dev Returns the factory address.
function _factory() internal view virtual returns (address factory_) {
return address(uint160(uint256(_getSlotValue(FACTORY_SLOT))));
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ contract Proxy is SlotManipulatable {
// If the implementation is empty, fetch it from the factory, which can act as a beacon.
address implementation = implementation_ == address(0) ? IDefaultImplementationBeacon(factory_).defaultImplementation() : implementation_;

require(implementation != address(0), "P:C:INVALID_IMPLEMENTATION");
require(implementation != address(0));

_setSlotValue(IMPLEMENTATION_SLOT, bytes32(uint256(uint160(implementation))));
}

fallback() payable external virtual {
bytes32 implementation = _getSlotValue(IMPLEMENTATION_SLOT);

require(address(uint160(uint256(implementation))).code.length != uint256(0), "P:F:INVALID_IMPLEMENTATION");
require(address(uint160(uint256(implementation))).code.length != uint256(0));

assembly {
calldatacopy(0, 0, calldatasize())
Expand Down
26 changes: 13 additions & 13 deletions contracts/ProxyFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ import { IProxied } from "./interfaces/IProxied.sol";
import { Proxy } from "./Proxy.sol";

/// @title A factory for Proxy contracts that proxy Proxied implementations.
contract ProxyFactory {
abstract contract ProxyFactory {

mapping(uint256 => address) internal _implementationOf;

mapping(address => uint256) internal _versionOf;

mapping(uint256 => mapping(uint256 => address)) internal _migratorForPath;

/// @notice Returns the implementation of `proxy_`.
/// @dev Returns the implementation of `proxy_`.
function _getImplementationOfProxy(address proxy_) private view returns (bool success_, address implementation_) {
bytes memory returnData;
// Since `_getImplementationOfProxy` is a private function, no need to check `proxy_` is a contract.
( success_, returnData ) = proxy_.staticcall(abi.encodeWithSelector(IProxied.implementation.selector));
implementation_ = abi.decode(returnData, (address));
}

/// @notice Initializes `proxy_` using the initializer for `version_`, given some initialization arguments.
/// @dev Initializes `proxy_` using the initializer for `version_`, given some initialization arguments.
function _initializeInstance(address proxy_, uint256 version_, bytes memory arguments_) private returns (bool success_) {
// The migrator, where fromVersion == toVersion, is an initializer.
address initializer = _migratorForPath[version_][version_];
Expand All @@ -35,7 +35,7 @@ contract ProxyFactory {
( success_, ) = proxy_.call(abi.encodeWithSelector(IProxied.migrate.selector, initializer, arguments_));
}

/// @notice Deploys a new proxy for some version, with some initialization arguments, using `create` (i.e. factory's nonce determines the address).
/// @dev Deploys a new proxy for some version, with some initialization arguments, using `create` (i.e. factory's nonce determines the address).
function _newInstance(uint256 version_, bytes memory arguments_) internal virtual returns (bool success_, address proxy_) {
address implementation = _implementationOf[version_];

Expand All @@ -45,12 +45,12 @@ contract ProxyFactory {
success_ = _initializeInstance(proxy_, version_, arguments_);
}

/// @notice Deploys a new proxy, with some initialization arguments, using `create2` (i.e. salt determines the address).
/// @dev This factory needs to be IDefaultImplementationBeacon, since the proxy will pull its implementation from it.
/// @dev Deploys a new proxy, with some initialization arguments, using `create2` (i.e. salt determines the address).
/// This factory needs to be IDefaultImplementationBeacon, since the proxy will pull its implementation from it.
function _newInstance(bytes memory arguments_, bytes32 salt_) internal virtual returns (bool success_, address proxy_) {
proxy_ = address(new Proxy{ salt: salt_ }(address(this), address(0)));

// Fetch the implementation from the proxy. Don't care about success, since the version of the implementation will be checked in the nest step.
// Fetch the implementation from the proxy. Don't care about success, since the version of the implementation will be checked in the next step.
( , address implementation ) = _getImplementationOfProxy(proxy_);

// Get the version of the implementation.
Expand All @@ -60,7 +60,7 @@ contract ProxyFactory {
success_ = (version != uint256(0)) && _initializeInstance(proxy_, version, arguments_);
}

/// @notice Registers an implementation for some version.
/// @dev Registers an implementation for some version.
function _registerImplementation(uint256 version_, address implementation_) internal virtual returns (bool success_) {
// Version 0 is not allowed since its the default value of all _versionOf[implementation_].
// Implementation cannot already be registered and cannot be empty account (and thus also not address(0)).
Expand All @@ -78,7 +78,7 @@ contract ProxyFactory {
return true;
}

/// @notice Registers a migrator for between two versions. If `fromVersion_ == toVersion_`, migrator is an initializer.
/// @dev Registers a migrator for between two versions. If `fromVersion_ == toVersion_`, migrator is an initializer.
function _registerMigrator(uint256 fromVersion_, uint256 toVersion_, address migrator_) internal virtual returns (bool success_) {
// Version 0 is invalid.
if (fromVersion_ == uint256(0) || toVersion_ == uint256(0)) return false;
Expand All @@ -91,8 +91,8 @@ contract ProxyFactory {
return true;
}

/// @notice Upgrades a proxy to a new version of an implementation, with some migration arguments.
/// @dev Inheritor should revert on `success_ = false`, since proxy can be set to new implementation, but failed to migrate.
/// @dev Upgrades a proxy to a new version of an implementation, with some migration arguments.
/// Inheritor should revert on `success_ = false`, since proxy can be set to new implementation, but failed to migrate.
function _upgradeInstance(address proxy_, uint256 toVersion_, bytes memory arguments_) internal virtual returns (bool success_) {
// Check that the proxy is currently a contract, just once, ahead of the 3 times it will be low-level-called.
if (!_isContract(proxy_)) return false;
Expand Down Expand Up @@ -123,7 +123,7 @@ contract ProxyFactory {
( success_, ) = proxy_.call(abi.encodeWithSelector(IProxied.migrate.selector, migrator, arguments_));
}

/// @notice Returns the deterministic address of a proxy given some salt.
/// @dev Returns the deterministic address of a proxy given some salt.
function _getDeterministicProxyAddress(bytes32 salt_) internal virtual view returns (address deterministicProxyAddress_) {
// See https://docs.soliditylang.org/en/v0.8.7/control-structures.html#salted-contract-creations-create2
return address(
Expand All @@ -142,7 +142,7 @@ contract ProxyFactory {
);
}

/// @notice Returns whether the account is currently a contract.
/// @dev Returns whether the account is currently a contract.
function _isContract(address account_) internal view returns (bool isContract_) {
return account_.code.length != uint256(0);
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/SlotManipulatable.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;

contract SlotManipulatable {
abstract contract SlotManipulatable {

function _getReferenceTypeSlot(bytes32 slot_, bytes32 key_) internal pure returns (bytes32 value_) {
return keccak256(abi.encodePacked(key_, slot_));
Expand Down
4 changes: 1 addition & 3 deletions contracts/interfaces/IDefaultImplementationBeacon.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ pragma solidity ^0.8.7;
/// @title An beacon that provides a default implementation for proxies, must implement IDefaultImplementationBeacon.
interface IDefaultImplementationBeacon {

/**
* @notice The address of an implementation for proxies.
*/
/// @dev The address of an implementation for proxies.
function defaultImplementation() external view returns (address defaultImplementation_);

}
16 changes: 8 additions & 8 deletions contracts/interfaces/IProxied.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@ pragma solidity ^0.8.7;
interface IProxied {

/**
* @notice The address of the proxy factory.
* @dev The address of the proxy factory.
*/
function factory() external view returns (address factory_);

/**
* @notice The address of the implementation contract being proxied.
* @dev The address of the implementation contract being proxied.
*/
function implementation() external view returns (address implementation_);

/**
* @notice Modifies the proxy's implementation address.
* @param newImplementation_ The address of an implementation contract.
* @dev Modifies the proxy's implementation address.
* @param newImplementation_ The address of an implementation contract.
*/
function setImplementation(address newImplementation_) external;

/**
* @notice Modifies the proxy's storage by delegate-calling a migrator contract with some arguments.
* @dev Access control logic critical since caller can force a selfdestruct via a malicious `migrator_` which is delegatecalled.
* @param migrator_ The address of a migrator contract.
* @param arguments_ Some encoded arguments to use for the migration.
* @dev Modifies the proxy's storage by delegate-calling a migrator contract with some arguments.
* Access control logic critical since caller can force a selfdestruct via a malicious `migrator_` which is delegatecalled.
* @param migrator_ The address of a migrator contract.
* @param arguments_ Some encoded arguments to use for the migration.
*/
function migrate(address migrator_, bytes calldata arguments_) external;

Expand Down
4 changes: 2 additions & 2 deletions contracts/test/ProxyFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ contract ProxyFactoryTests is DSTest {

bytes32 salt = keccak256(abi.encodePacked("salt"));

assertEq(factory.getDeterministicProxyAddress(salt), 0x8D4Ab495177fBE2145fd15A9392966CC190FF5eC);
assertEq(factory.newInstance(new bytes(0), salt), 0x8D4Ab495177fBE2145fd15A9392966CC190FF5eC);
assertEq(factory.getDeterministicProxyAddress(salt), 0x045A1D5dF300FdfB7CE80Ed8397b46a4C634c508);
assertEq(factory.newInstance(new bytes(0), salt), 0x045A1D5dF300FdfB7CE80Ed8397b46a4C634c508);
}

function test_newInstance_withSaltAndInvalidInitializerArguments() external {
Expand Down
6 changes: 3 additions & 3 deletions contracts/test/mocks/Mocks.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity ^0.8.7;
import { IDefaultImplementationBeacon } from "../../interfaces/IDefaultImplementationBeacon.sol";
import { IProxied } from "../../interfaces/IProxied.sol";

import { Proxied } from "../../Proxied.sol";
import { ProxiedInternals } from "../../ProxiedInternals.sol";
import { ProxyFactory } from "../../ProxyFactory.sol";
import { SlotManipulatable } from "../../SlotManipulatable.sol";

Expand Down Expand Up @@ -110,7 +110,7 @@ interface IMockImplementationV1 is IProxied {

}

contract MockImplementationV1 is IProxied, Proxied, IMockImplementationV1 {
contract MockImplementationV1 is IProxied, IMockImplementationV1, ProxiedInternals {

// Some "Nothing Up My Sleeve" Slot
bytes32 private constant DELTA_SLOT = 0x1111111111111111111111111111111111111111111111111111111111111111;
Expand Down Expand Up @@ -235,7 +235,7 @@ interface IMockImplementationV2 is IProxied {

}

contract MockImplementationV2 is IProxied, Proxied, IMockImplementationV2 {
contract MockImplementationV2 is IProxied, IMockImplementationV2, ProxiedInternals {

// Same "Nothing Up My Sleeve" Slot as in V1
bytes32 private constant DERBY_SLOT = 0x1111111111111111111111111111111111111111111111111111111111111111;
Expand Down

0 comments on commit e6f18a0

Please sign in to comment.