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

VAL-114 First cut of adding an upgradeable contract #127

Merged
merged 8 commits into from
Dec 6, 2022
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ typechain-types
cache
artifacts

# OpenZeppelin
.openzeppelin/unknown-*.json
.openzeppelin/.session
24 changes: 13 additions & 11 deletions contracts/Pool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,26 @@ import "./controllers/interfaces/IWithdrawController.sol";
import "./controllers/interfaces/IPoolController.sol";
import "./factories/interfaces/IWithdrawControllerFactory.sol";
import "./factories/interfaces/IPoolControllerFactory.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Annoyingly, everything needs to be the *Upgradeable OpenZep library

import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import "./libraries/PoolLib.sol";
import "./FeeVault.sol";
import "./FirstLossVault.sol";
import "./upgrades/interfaces/IBeaconImplementation.sol";

/**
* @title Pool
*/
contract Pool is IPool, ERC20 {
using SafeERC20 for IERC20;
contract Pool is IPool, ERC20Upgradeable, IBeaconImplementation {
using SafeERC20Upgradeable for IERC20Upgradeable;
using SafeMath for uint256;
using EnumerableSet for EnumerableSet.AddressSet;

IERC20 private _liquidityAsset;
IERC20Upgradeable private _liquidityAsset;
FeeVault private _feeVault;
IPoolAccountings private _accountings;

Expand Down Expand Up @@ -105,7 +106,7 @@ contract Pool is IPool, ERC20 {
}

/**
* @dev Constructor for Pool
* @dev Initializer for Pool
* @param liquidityAsset asset held by the poo
* @param poolAdmin admin of the pool
* @param serviceConfiguration address of global service configuration
Expand All @@ -114,7 +115,7 @@ contract Pool is IPool, ERC20 {
* @param tokenName Name used for issued pool tokens
* @param tokenSymbol Symbol used for issued pool tokens
*/
constructor(
function initialize(
address liquidityAsset,
address poolAdmin,
address serviceConfiguration,
Expand All @@ -123,8 +124,9 @@ contract Pool is IPool, ERC20 {
IPoolConfigurableSettings memory poolSettings,
string memory tokenName,
string memory tokenSymbol
) ERC20(tokenName, tokenSymbol) {
_liquidityAsset = IERC20(liquidityAsset);
) public initializer {
__ERC20_init(tokenName, tokenSymbol);
_liquidityAsset = IERC20Upgradeable(liquidityAsset);
_feeVault = new FeeVault(address(this));

// Build the withdraw controller
Expand Down Expand Up @@ -366,7 +368,7 @@ contract Pool is IPool, ERC20 {
);

_accountings.fixedFeeDueDate += fixedFeeInterval * 1 days;
IERC20(_liquidityAsset).safeTransfer(recipient, fixedFee);
IERC20Upgradeable(_liquidityAsset).safeTransfer(recipient, fixedFee);
}

/*//////////////////////////////////////////////////////////////
Expand Down
61 changes: 50 additions & 11 deletions contracts/PoolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ pragma solidity ^0.8.16;
import "./Pool.sol";
import "./interfaces/IServiceConfiguration.sol";
import "./interfaces/IPoolFactory.sol";
import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import "./upgrades/interfaces/IBeacon.sol";

/**
* @title PoolFactory
*/
contract PoolFactory is IPoolFactory {
contract PoolFactory is IPoolFactory, IBeacon {
/**
* @dev Reference to the ServiceConfiguration contract
*/
Expand All @@ -24,6 +26,22 @@ contract PoolFactory is IPoolFactory {
*/
address internal _poolControllerFactory;

/**
* @inheritdoc IBeacon
*/
address public implementation;

/**
* @dev Modifier that requires that the sender is registered as a protocol deployer.
*/
modifier onlyDeployer() {
require(
IServiceConfiguration(_serviceConfiguration).isDeployer(msg.sender),
"Upgrade: unauthorized"
);
_;
}

constructor(
address serviceConfiguration,
address withdrawControllerFactory,
Expand All @@ -34,6 +52,18 @@ contract PoolFactory is IPoolFactory {
_poolControllerFactory = poolControllerFactory;
}

/**
* @inheritdoc IBeacon
*/
function setImplementation(address newImplementation)
external
override
onlyDeployer
{
implementation = newImplementation;
emit ImplementationSet(newImplementation);
}

/**
* @dev Creates a pool
* @dev Emits `PoolCreated` event.
Expand All @@ -42,6 +72,10 @@ contract PoolFactory is IPoolFactory {
address liquidityAsset,
IPoolConfigurableSettings calldata settings
) public virtual returns (address poolAddress) {
require(
implementation != address(0),
"PoolFactory: no implementation set"
);
require(
IServiceConfiguration(_serviceConfiguration).paused() == false,
"PoolFactory: Protocol paused"
Expand Down Expand Up @@ -95,16 +129,21 @@ contract PoolFactory is IPoolFactory {
address liquidityAsset,
IPoolConfigurableSettings calldata settings
) internal virtual returns (address) {
Pool pool = new Pool(
liquidityAsset,
msg.sender,
_serviceConfiguration,
_withdrawControllerFactory,
_poolControllerFactory,
settings,
"PerimeterPoolToken",
"PPT"
// Create beacon proxy
BeaconProxy proxy = new BeaconProxy(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the key bit

address(this),
abi.encodeWithSelector(
Pool.initialize.selector,
liquidityAsset,
msg.sender,
_serviceConfiguration,
_withdrawControllerFactory,
_poolControllerFactory,
settings,
"PerimeterPoolToken",
"PPT"
)
);
return address(pool);
return address(proxy);
}
}
57 changes: 43 additions & 14 deletions contracts/ServiceConfiguration.sol
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "./interfaces/IServiceConfiguration.sol";
import "./upgrades/DeployerUUPSUpgradeable.sol";
import "hardhat/console.sol";

/**
* @title The ServiceConfiguration contract
* @dev Implementation of the {IServiceConfiguration} interface.
*/
contract ServiceConfiguration is AccessControl, IServiceConfiguration {
contract ServiceConfiguration is
IServiceConfiguration,
AccessControlUpgradeable,
DeployerUUPSUpgradeable
{
/**
* @dev The Operator Role
*/
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");

/**
* @dev The Operator Role
*/
bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE");

/**
* @dev Whether the protocol is paused.
*/
bool public paused = false;
bool public paused;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All non-constant values should be set in the initializer, ideally, otherwise they risk getting reset on an upgrade unexpectedly.


mapping(address => bool) public isLiquidityAsset;

mapping(address => uint256) public firstLossMinimum;

uint256 public firstLossFeeBps = 500;
uint256 public firstLossFeeBps;

address public tosAcceptanceRegistry;

uint256 public protocolFeeBps = 0;
uint256 public protocolFeeBps;

/**
* @dev Holds a reference to valid LoanFactories
Expand Down Expand Up @@ -69,15 +80,6 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {
*/
event TermsOfServiceRegistrySet(address indexed registry);

/**
* @dev Constructor for the contract, which sets up the default roles and
* owners.
*/
constructor() {
// Grant the contract deployer the Operator role
_setupRole(OPERATOR_ROLE, msg.sender);
}

/**
* @dev Modifier that checks that the caller account has the Operator role.
*/
Expand All @@ -89,6 +91,19 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {
_;
}

/**
* @dev Constructor for the contract, which sets up the default roles and
* owners.
*/
function initialize() public initializer {
// Initialize values
paused = false;
firstLossFeeBps = 500;
protocolFeeBps = 0;

_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}

/**
* @dev Set a liquidity asset as valid or not.
*/
Expand Down Expand Up @@ -116,6 +131,13 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {
return hasRole(OPERATOR_ROLE, addr);
}

/**
* @inheritdoc IServiceConfiguration
*/
function isDeployer(address addr) external view returns (bool) {
return hasRole(DEPLOYER_ROLE, addr);
}

/**
* @inheritdoc IServiceConfiguration
*/
Expand Down Expand Up @@ -159,4 +181,11 @@ contract ServiceConfiguration is AccessControl, IServiceConfiguration {
firstLossFeeBps = value;
emit ParameterSet("firstLossFeeBps", value);
}

/**
* @inheritdoc IServiceConfigurable
*/
function serviceConfiguration() external view override returns (address) {
return address(this);
}
}
4 changes: 2 additions & 2 deletions contracts/interfaces/IERC4626.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";

/**
* @title The interface according to the ERC-4626 standard.
*/
interface IERC4626 is IERC20 {
interface IERC4626 is IERC20Upgradeable {
/**
* @dev Emitted when tokens are deposited into the vault via the mint and deposit methods.
*/
Expand Down
13 changes: 13 additions & 0 deletions contracts/interfaces/IServiceConfigurable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

/**
* @title IServiceConfigurable
* @dev Interface indicating that the contract is controlled by the protocol service configuration.
*/
interface IServiceConfigurable {
/**
* @dev Address of the protocol service configuration.
*/
function serviceConfiguration() external view returns (address);
}
9 changes: 6 additions & 3 deletions contracts/interfaces/IServiceConfiguration.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

import "@openzeppelin/contracts/access/IAccessControl.sol";

/**
* @title The protocol global Service Configuration
*/
interface IServiceConfiguration is IAccessControl {
interface IServiceConfiguration {
/**
* @dev checks if a given address has the Operator role
*/
function isOperator(address addr) external view returns (bool);

/**
* @dev checks if a given address has the Deployer role
*/
function isDeployer(address addr) external view returns (bool);

function paused() external view returns (bool);

function firstLossMinimum(address addr) external view returns (uint256);
Expand Down
8 changes: 8 additions & 0 deletions contracts/mocks/upgrades/MockUpgrade.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

abstract contract MockUpgrade {
function foo() external pure returns (bool) {
return true;
}
}
12 changes: 12 additions & 0 deletions contracts/mocks/upgrades/PoolMockV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

import "../../Pool.sol";
import "./MockUpgrade.sol";

/**
* @dev Simulated new Pool implementation
*/
contract PoolMockV2 is Pool, MockUpgrade {

}
12 changes: 12 additions & 0 deletions contracts/mocks/upgrades/ServiceConfigurationMockV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;

import "../../ServiceConfiguration.sol";
import "./MockUpgrade.sol";

/**
* @dev Simulated new ServiceConfiguration implementation
*/
contract ServiceConfigurationMockV2 is ServiceConfiguration, MockUpgrade {

}
Loading