Contract Name:
MasterMinter
Contract Source Code:
/**
* Copyright CENTRE SECZ 2018 - 2021
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
pragma solidity 0.6.12;
import "./MintController.sol";
/**
* @title MasterMinter
* @notice MasterMinter uses multiple controllers to manage minters for a
* contract that implements the MinterManagerInterface.
* @dev MasterMinter inherits all its functionality from MintController.
*/
contract MasterMinter is MintController {
constructor(address _minterManager) public MintController(_minterManager) {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
/**
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2018 zOS Global Limited.
* Copyright (c) 2018-2020 CENTRE SECZ
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
pragma solidity 0.6.12;
/**
* @notice The Ownable contract has an owner address, and provides basic
* authorization control functions
* @dev Forked from https://github.com/OpenZeppelin/openzeppelin-labs/blob/3887ab77b8adafba4a26ace002f3a684c1a3388b/upgradeability_ownership/contracts/ownership/Ownable.sol
* Modifications:
* 1. Consolidate OwnableStorage into this contract (7/13/18)
* 2. Reformat, conform to Solidity 0.6 syntax, and add error messages (5/13/20)
* 3. Make public functions external (5/27/20)
*/
contract Ownable {
// Owner of the contract
address private _owner;
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event OwnershipTransferred(address previousOwner, address newOwner);
/**
* @dev The constructor sets the original owner of the contract to the sender account.
*/
constructor() public {
setOwner(msg.sender);
}
/**
* @dev Tells the address of the owner
* @return the address of the owner
*/
function owner() external view returns (address) {
return _owner;
}
/**
* @dev Sets a new owner address
*/
function setOwner(address newOwner) internal {
_owner = newOwner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == _owner, "Ownable: caller is not the owner");
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) external onlyOwner {
require(
newOwner != address(0),
"Ownable: new owner is the zero address"
);
emit OwnershipTransferred(_owner, newOwner);
setOwner(newOwner);
}
}
/**
* Copyright CENTRE SECZ 2018 - 2021
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
pragma solidity 0.6.12;
/**
* @notice A contract that implements the MinterManagementInterface has external
* functions for adding and removing minters and modifying their allowances.
* An example is the FiatTokenV1 contract that implements USDC.
*/
interface MinterManagementInterface {
function isMinter(address _account) external view returns (bool);
function minterAllowance(address _minter) external view returns (uint256);
function configureMinter(address _minter, uint256 _minterAllowedAmount)
external
returns (bool);
function removeMinter(address _minter) external returns (bool);
}
/**
* Copyright CENTRE SECZ 2018 - 2021
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
pragma solidity 0.6.12;
import "./Controller.sol";
import "./MinterManagementInterface.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
/**
* @title MintController
* @notice The MintController contract manages minters for a contract that
* implements the MinterManagerInterface. It lets the owner designate certain
* addresses as controllers, and these controllers then manage the
* minters by adding and removing minters, as well as modifying their minting
* allowance. A controller may manage exactly one minter, but the same minter
* address may be managed by multiple controllers.
* @dev MintController inherits from the Controller contract. It treats the
* Controller workers as minters.
*/
contract MintController is Controller {
using SafeMath for uint256;
/**
* @title MinterManagementInterface
* @notice MintController calls the minterManager to execute/record minter
* management tasks, as well as to query the status of a minter address.
*/
MinterManagementInterface internal minterManager;
event MinterManagerSet(
address indexed _oldMinterManager,
address indexed _newMinterManager
);
event MinterConfigured(
address indexed _msgSender,
address indexed _minter,
uint256 _allowance
);
event MinterRemoved(address indexed _msgSender, address indexed _minter);
event MinterAllowanceIncremented(
address indexed _msgSender,
address indexed _minter,
uint256 _increment,
uint256 _newAllowance
);
event MinterAllowanceDecremented(
address indexed msgSender,
address indexed minter,
uint256 decrement,
uint256 newAllowance
);
/**
* @notice Initializes the minterManager.
* @param _minterManager The address of the minterManager contract.
*/
constructor(address _minterManager) public {
minterManager = MinterManagementInterface(_minterManager);
}
/**
* @notice gets the minterManager
*/
function getMinterManager()
external
view
returns (MinterManagementInterface)
{
return minterManager;
}
// onlyOwner functions
/**
* @notice Sets the minterManager.
* @param _newMinterManager The address of the new minterManager contract.
*/
function setMinterManager(address _newMinterManager) public onlyOwner {
emit MinterManagerSet(address(minterManager), _newMinterManager);
minterManager = MinterManagementInterface(_newMinterManager);
}
// onlyController functions
/**
* @notice Removes the controller's own minter.
*/
function removeMinter() public onlyController returns (bool) {
address minter = controllers[msg.sender];
emit MinterRemoved(msg.sender, minter);
return minterManager.removeMinter(minter);
}
/**
* @notice Enables the minter and sets its allowance.
* @param _newAllowance New allowance to be set for minter.
*/
function configureMinter(uint256 _newAllowance)
public
onlyController
returns (bool)
{
address minter = controllers[msg.sender];
emit MinterConfigured(msg.sender, minter, _newAllowance);
return internal_setMinterAllowance(minter, _newAllowance);
}
/**
* @notice Increases the minter's allowance if and only if the minter is an
* active minter.
* @dev An minter is considered active if minterManager.isMinter(minter)
* returns true.
*/
function incrementMinterAllowance(uint256 _allowanceIncrement)
public
onlyController
returns (bool)
{
require(
_allowanceIncrement > 0,
"Allowance increment must be greater than 0"
);
address minter = controllers[msg.sender];
require(
minterManager.isMinter(minter),
"Can only increment allowance for minters in minterManager"
);
uint256 currentAllowance = minterManager.minterAllowance(minter);
uint256 newAllowance = currentAllowance.add(_allowanceIncrement);
emit MinterAllowanceIncremented(
msg.sender,
minter,
_allowanceIncrement,
newAllowance
);
return internal_setMinterAllowance(minter, newAllowance);
}
/**
* @notice decreases the minter allowance if and only if the minter is
* currently active. The controller can safely send a signed
* decrementMinterAllowance() transaction to a minter and not worry
* about it being used to undo a removeMinter() transaction.
*/
function decrementMinterAllowance(uint256 _allowanceDecrement)
public
onlyController
returns (bool)
{
require(
_allowanceDecrement > 0,
"Allowance decrement must be greater than 0"
);
address minter = controllers[msg.sender];
require(
minterManager.isMinter(minter),
"Can only decrement allowance for minters in minterManager"
);
uint256 currentAllowance = minterManager.minterAllowance(minter);
uint256 actualAllowanceDecrement = (
currentAllowance > _allowanceDecrement
? _allowanceDecrement
: currentAllowance
);
uint256 newAllowance = currentAllowance.sub(actualAllowanceDecrement);
emit MinterAllowanceDecremented(
msg.sender,
minter,
actualAllowanceDecrement,
newAllowance
);
return internal_setMinterAllowance(minter, newAllowance);
}
// Internal functions
/**
* @notice Uses the MinterManagementInterface to enable the minter and
* set its allowance.
* @param _minter Minter to set new allowance of.
* @param _newAllowance New allowance to be set for minter.
*/
function internal_setMinterAllowance(address _minter, uint256 _newAllowance)
internal
returns (bool)
{
return minterManager.configureMinter(_minter, _newAllowance);
}
}
/**
* Copyright CENTRE SECZ 2018 - 2021
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
pragma solidity 0.6.12;
import { Ownable } from "../v1/Ownable.sol";
/**
* @title Controller
* @notice Generic implementation of the owner-controller-worker model.
* One owner manages many controllers. Each controller manages one worker.
* Workers may be reused across different controllers.
*/
contract Controller is Ownable {
/**
* @notice A controller manages a single worker address.
* controllers[controller] = worker
*/
mapping(address => address) internal controllers;
event ControllerConfigured(
address indexed _controller,
address indexed _worker
);
event ControllerRemoved(address indexed _controller);
/**
* @notice Ensures that caller is the controller of a non-zero worker
* address.
*/
modifier onlyController() {
require(
controllers[msg.sender] != address(0),
"The value of controllers[msg.sender] must be non-zero"
);
_;
}
/**
* @notice Gets the worker at address _controller.
*/
function getWorker(address _controller) external view returns (address) {
return controllers[_controller];
}
// onlyOwner functions
/**
* @notice Configure a controller with the given worker.
* @param _controller The controller to be configured with a worker.
* @param _worker The worker to be set for the newly configured controller.
* _worker must not be a non-zero address. To disable a worker,
* use removeController instead.
*/
function configureController(address _controller, address _worker)
public
onlyOwner
{
require(
_controller != address(0),
"Controller must be a non-zero address"
);
require(_worker != address(0), "Worker must be a non-zero address");
controllers[_controller] = _worker;
emit ControllerConfigured(_controller, _worker);
}
/**
* @notice disables a controller by setting its worker to address(0).
* @param _controller The controller to disable.
*/
function removeController(address _controller) public onlyOwner {
require(
_controller != address(0),
"Controller must be a non-zero address"
);
require(
controllers[_controller] != address(0),
"Worker must be a non-zero address"
);
controllers[_controller] = address(0);
emit ControllerRemoved(_controller);
}
}