// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;
/// @title An beacon that provides a default implementation for proxies, must implement IDefaultImplementationBeacon.
interface IDefaultImplementationBeacon {
/// @dev The address of an implementation for proxies.
function defaultImplementation() external view returns (address defaultImplementation_);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;
import { IMapleProxyFactory } from "../../modules/maple-proxy-factory/contracts/interfaces/IMapleProxyFactory.sol";
/// @title MapleLoanFactory deploys Loan instances.
interface IMapleLoanFactory is IMapleProxyFactory {
/**
* @dev Whether the proxy is a MapleLoan deployed by this factory.
* @param proxy_ The address of the proxy contract.
* @return isLoan_ Whether the proxy is a MapleLoan deployed by this factory.
*/
function isLoan(address proxy_) external view returns (bool isLoan_);
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;
import { IDefaultImplementationBeacon } from "../../modules/proxy-factory/contracts/interfaces/IDefaultImplementationBeacon.sol";
/// @title A Maple factory for Proxy contracts that proxy MapleProxied implementations.
interface IMapleProxyFactory is IDefaultImplementationBeacon {
/**************************************************************************************************************************************/
/*** Events ***/
/**************************************************************************************************************************************/
/**
* @dev A default version was set.
* @param version_ The default version.
*/
event DefaultVersionSet(uint256 indexed version_);
/**
* @dev A version of an implementation, at some address, was registered, with an optional initializer.
* @param version_ The version registered.
* @param implementationAddress_ The address of the implementation.
* @param initializer_ The address of the initializer, if any.
*/
event ImplementationRegistered(uint256 indexed version_, address indexed implementationAddress_, address indexed initializer_);
/**
* @dev A proxy contract was deployed with some initialization arguments.
* @param version_ The version of the implementation being proxied by the deployed proxy contract.
* @param instance_ The address of the proxy contract deployed.
* @param initializationArguments_ The arguments used to initialize the proxy contract, if any.
*/
event InstanceDeployed(uint256 indexed version_, address indexed instance_, bytes initializationArguments_);
/**
* @dev A instance has upgraded by proxying to a new implementation, with some migration arguments.
* @param instance_ The address of the proxy contract.
* @param fromVersion_ The initial implementation version being proxied.
* @param toVersion_ The new implementation version being proxied.
* @param migrationArguments_ The arguments used to migrate, if any.
*/
event InstanceUpgraded(address indexed instance_, uint256 indexed fromVersion_, uint256 indexed toVersion_, bytes migrationArguments_);
/**
* @dev The MapleGlobals was set.
* @param mapleGlobals_ The address of a Maple Globals contract.
*/
event MapleGlobalsSet(address indexed mapleGlobals_);
/**
* @dev An upgrade path was disabled, with an optional migrator contract.
* @param fromVersion_ The starting version of the upgrade path.
* @param toVersion_ The destination version of the upgrade path.
*/
event UpgradePathDisabled(uint256 indexed fromVersion_, uint256 indexed toVersion_);
/**
* @dev An upgrade path was enabled, with an optional migrator contract.
* @param fromVersion_ The starting version of the upgrade path.
* @param toVersion_ The destination version of the upgrade path.
* @param migrator_ The address of the migrator, if any.
*/
event UpgradePathEnabled(uint256 indexed fromVersion_, uint256 indexed toVersion_, address indexed migrator_);
/**************************************************************************************************************************************/
/*** State Variables ***/
/**************************************************************************************************************************************/
/**
* @dev The default version.
*/
function defaultVersion() external view returns (uint256 defaultVersion_);
/**
* @dev The address of the MapleGlobals contract.
*/
function mapleGlobals() external view returns (address mapleGlobals_);
/**
* @dev Whether the upgrade is enabled for a path from a version to another version.
* @param toVersion_ The initial version.
* @param fromVersion_ The destination version.
* @return allowed_ Whether the upgrade is enabled.
*/
function upgradeEnabledForPath(uint256 toVersion_, uint256 fromVersion_) external view returns (bool allowed_);
/**************************************************************************************************************************************/
/*** State Changing Functions ***/
/**************************************************************************************************************************************/
/**
* @dev Deploys a new instance proxying the default implementation version, with some initialization arguments.
* Uses a nonce and `msg.sender` as a salt for the CREATE2 opcode during instantiation to produce deterministic addresses.
* @param arguments_ The initialization arguments to use for the instance deployment, if any.
* @param salt_ The salt to use in the contract creation process.
* @return instance_ The address of the deployed proxy contract.
*/
function createInstance(bytes calldata arguments_, bytes32 salt_) external returns (address instance_);
/**
* @dev Enables upgrading from a version to a version of an implementation, with an optional migrator.
* Only the Governor can call this function.
* @param fromVersion_ The starting version of the upgrade path.
* @param toVersion_ The destination version of the upgrade path.
* @param migrator_ The address of the migrator, if any.
*/
function enableUpgradePath(uint256 fromVersion_, uint256 toVersion_, address migrator_) external;
/**
* @dev Disables upgrading from a version to a version of a implementation.
* Only the Governor can call this function.
* @param fromVersion_ The starting version of the upgrade path.
* @param toVersion_ The destination version of the upgrade path.
*/
function disableUpgradePath(uint256 fromVersion_, uint256 toVersion_) external;
/**
* @dev Registers the address of an implementation contract as a version, with an optional initializer.
* Only the Governor can call this function.
* @param version_ The version to register.
* @param implementationAddress_ The address of the implementation.
* @param initializer_ The address of the initializer, if any.
*/
function registerImplementation(uint256 version_, address implementationAddress_, address initializer_) external;
/**
* @dev Sets the default version.
* Only the Governor can call this function.
* @param version_ The implementation version to set as the default.
*/
function setDefaultVersion(uint256 version_) external;
/**
* @dev Sets the Maple Globals contract.
* Only the Governor can call this function.
* @param mapleGlobals_ The address of a Maple Globals contract.
*/
function setGlobals(address mapleGlobals_) external;
/**
* @dev Upgrades the calling proxy contract's implementation, with some migration arguments.
* @param toVersion_ The implementation version to upgrade the proxy contract to.
* @param arguments_ The migration arguments, if any.
*/
function upgradeInstance(uint256 toVersion_, bytes calldata arguments_) external;
/**************************************************************************************************************************************/
/*** View Functions ***/
/**************************************************************************************************************************************/
/**
* @dev Returns the deterministic address of a potential proxy, given some arguments and salt.
* @param arguments_ The initialization arguments to be used when deploying the proxy.
* @param salt_ The salt to be used when deploying the proxy.
* @return instanceAddress_ The deterministic address of a potential proxy.
*/
function getInstanceAddress(bytes calldata arguments_, bytes32 salt_) external view returns (address instanceAddress_);
/**
* @dev Returns the address of an implementation version.
* @param version_ The implementation version.
* @return implementation_ The address of the implementation.
*/
function implementationOf(uint256 version_) external view returns (address implementation_);
/**
* @dev Returns if a given address has been deployed by this factory/
* @param instance_ The address to check.
* @return isInstance_ A boolean indication if the address has been deployed by this factory.
*/
function isInstance(address instance_) external view returns (bool isInstance_);
/**
* @dev Returns the address of a migrator contract for a migration path (from version, to version).
* If oldVersion_ == newVersion_, the migrator is an initializer.
* @param oldVersion_ The old version.
* @param newVersion_ The new version.
* @return migrator_ The address of a migrator contract.
*/
function migratorForPath(uint256 oldVersion_, uint256 newVersion_) external view returns (address migrator_);
/**
* @dev Returns the version of an implementation contract.
* @param implementation_ The address of an implementation contract.
* @return version_ The version of the implementation contract.
*/
function versionOf(address implementation_) external view returns (uint256 version_);
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;
/// @title An implementation that is to be proxied, must implement IProxied.
interface IProxied {
/**
* @dev The address of the proxy factory.
*/
function factory() external view returns (address factory_);
/**
* @dev The address of the implementation contract being proxied.
*/
function implementation() external view returns (address implementation_);
/**
* @dev Modifies the proxy's implementation address.
* @param newImplementation_ The address of an implementation contract.
*/
function setImplementation(address newImplementation_) external;
/**
* @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;
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;
interface IGlobalsLike {
function canDeploy(address caller_) external view returns (bool canDeploy_);
function governor() external view returns (address governor_);
function isBorrower(address account_) external view returns (bool isBorrower_);
function isFunctionPaused(bytes4 sig_) external view returns (bool isFunctionPaused_);
function isInstanceOf(bytes32 instanceId_, address instance_) external view returns (bool isInstance_);
function isPoolAsset(address poolAsset_) external view returns (bool isPoolAsset_);
function platformServiceFeeRate(address poolManager) external view returns (uint256 platformServiceFeeRate_);
function securityAdmin() external view returns (address securityAdmin_);
}
interface ILenderLike {
function claim(
int256 principal_,
uint256 interest_,
uint256 delegateServiceFee_,
uint256 platformServiceFee_,
uint40 paymentDueDate_
) external;
function factory() external view returns (address factory_);
function fundsAsset() external view returns (address fundsAsset_);
function poolManager() external view returns (address poolManager_);
}
interface IMapleProxyFactoryLike {
function isInstance(address instance_) external view returns (bool isInstance_);
function mapleGlobals() external view returns (address mapleGlobals_);
}
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.7;
import { IMapleProxyFactory, MapleProxyFactory } from "../modules/maple-proxy-factory/contracts/MapleProxyFactory.sol";
import { IMapleLoanFactory } from "./interfaces/IMapleLoanFactory.sol";
import { IGlobalsLike } from "./interfaces/Interfaces.sol";
/// @title MapleLoanFactory deploys Loan instances.
contract MapleLoanFactory is IMapleLoanFactory, MapleProxyFactory {
mapping(address => bool) public override isLoan;
/// @param mapleGlobals_ The address of a Maple Globals contract.
constructor(address mapleGlobals_) MapleProxyFactory(mapleGlobals_) {}
function createInstance(bytes calldata arguments_, bytes32 salt_)
override(IMapleProxyFactory, MapleProxyFactory) public returns (
address instance_
)
{
require(IGlobalsLike(mapleGlobals).canDeploy(msg.sender), "LF:CI:CANNOT_DEPLOY");
isLoan[instance_ = super.createInstance(arguments_, salt_)] = true;
}
}
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.7;
import { ProxyFactory } from "../modules/proxy-factory/contracts/ProxyFactory.sol";
import { IMapleGlobalsLike } from "./interfaces/Interfaces.sol";
import { IMapleProxied } from "./interfaces/IMapleProxied.sol";
import { IMapleProxyFactory } from "./interfaces/IMapleProxyFactory.sol";
/// @title A Maple factory for Proxy contracts that proxy MapleProxied implementations.
contract MapleProxyFactory is IMapleProxyFactory, ProxyFactory {
address public override mapleGlobals;
uint256 public override defaultVersion;
mapping(address => bool) public override isInstance;
mapping(uint256 => mapping(uint256 => bool)) public override upgradeEnabledForPath;
constructor(address mapleGlobals_) {
require(IMapleGlobalsLike(mapleGlobals = mapleGlobals_).governor() != address(0), "MPF:C:INVALID_GLOBALS");
}
modifier onlyGovernor() {
require(msg.sender == IMapleGlobalsLike(mapleGlobals).governor(), "MPF:NOT_GOVERNOR");
_;
}
modifier whenProtocolNotPaused() {
require(!IMapleGlobalsLike(mapleGlobals).protocolPaused(), "MPF:PROTOCOL_PAUSED");
_;
}
/**************************************************************************************************************************************/
/*** Admin Functions ***/
/**************************************************************************************************************************************/
function disableUpgradePath(uint256 fromVersion_, uint256 toVersion_) public override virtual onlyGovernor {
require(fromVersion_ != toVersion_, "MPF:DUP:OVERWRITING_INITIALIZER");
require(_registerMigrator(fromVersion_, toVersion_, address(0)), "MPF:DUP:FAILED");
emit UpgradePathDisabled(fromVersion_, toVersion_);
upgradeEnabledForPath[fromVersion_][toVersion_] = false;
}
function enableUpgradePath(uint256 fromVersion_, uint256 toVersion_, address migrator_) public override virtual onlyGovernor {
require(fromVersion_ != toVersion_, "MPF:EUP:OVERWRITING_INITIALIZER");
require(_registerMigrator(fromVersion_, toVersion_, migrator_), "MPF:EUP:FAILED");
emit UpgradePathEnabled(fromVersion_, toVersion_, migrator_);
upgradeEnabledForPath[fromVersion_][toVersion_] = true;
}
function registerImplementation(uint256 version_, address implementationAddress_, address initializer_)
public override virtual onlyGovernor
{
// Version 0 reserved as "no version" since default `defaultVersion` is 0.
require(version_ != uint256(0), "MPF:RI:INVALID_VERSION");
emit ImplementationRegistered(version_, implementationAddress_, initializer_);
require(_registerImplementation(version_, implementationAddress_), "MPF:RI:FAIL_FOR_IMPLEMENTATION");
// Set migrator for initialization, which understood as fromVersion == toVersion.
require(_registerMigrator(version_, version_, initializer_), "MPF:RI:FAIL_FOR_MIGRATOR");
}
function setDefaultVersion(uint256 version_) public override virtual onlyGovernor {
// Version must be 0 (to disable creating new instances) or be registered.
require(version_ == 0 || _implementationOf[version_] != address(0), "MPF:SDV:INVALID_VERSION");
emit DefaultVersionSet(defaultVersion = version_);
}
function setGlobals(address mapleGlobals_) public override virtual onlyGovernor {
require(IMapleGlobalsLike(mapleGlobals_).governor() != address(0), "MPF:SG:INVALID_GLOBALS");
emit MapleGlobalsSet(mapleGlobals = mapleGlobals_);
}
/**************************************************************************************************************************************/
/*** Instance Functions ***/
/**************************************************************************************************************************************/
function createInstance(bytes calldata arguments_, bytes32 salt_)
public override virtual whenProtocolNotPaused returns (address instance_)
{
bool success;
( success, instance_ ) = _newInstance(arguments_, keccak256(abi.encodePacked(arguments_, salt_)));
require(success, "MPF:CI:FAILED");
isInstance[instance_] = true;
emit InstanceDeployed(defaultVersion, instance_, arguments_);
}
// NOTE: The implementation proxied by the instance defines the access control logic for its own upgrade.
function upgradeInstance(uint256 toVersion_, bytes calldata arguments_) public override virtual whenProtocolNotPaused {
uint256 fromVersion = _versionOf[IMapleProxied(msg.sender).implementation()];
require(upgradeEnabledForPath[fromVersion][toVersion_], "MPF:UI:NOT_ALLOWED");
emit InstanceUpgraded(msg.sender, fromVersion, toVersion_, arguments_);
require(_upgradeInstance(msg.sender, toVersion_, arguments_), "MPF:UI:FAILED");
}
/**************************************************************************************************************************************/
/*** View Functions ***/
/**************************************************************************************************************************************/
function getInstanceAddress(bytes calldata arguments_, bytes32 salt_) public view override virtual returns (address instanceAddress_) {
return _getDeterministicProxyAddress(keccak256(abi.encodePacked(arguments_, salt_)));
}
function implementationOf(uint256 version_) public view override virtual returns (address implementation_) {
return _implementationOf[version_];
}
function defaultImplementation() external view override returns (address defaultImplementation_) {
return _implementationOf[defaultVersion];
}
function migratorForPath(uint256 oldVersion_, uint256 newVersion_) public view override virtual returns (address migrator_) {
return _migratorForPath[oldVersion_][newVersion_];
}
function versionOf(address implementation_) public view override virtual returns (uint256 version_) {
return _versionOf[implementation_];
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.7;
abstract contract SlotManipulatable {
function _getReferenceTypeSlot(bytes32 slot_, bytes32 key_) internal pure returns (bytes32 value_) {
return keccak256(abi.encodePacked(key_, slot_));
}
function _getSlotValue(bytes32 slot_) internal view returns (bytes32 value_) {
assembly {
value_ := sload(slot_)
}
}
function _setSlotValue(bytes32 slot_, bytes32 value_) internal {
assembly {
sstore(slot_, value_)
}
}
}