BAUSD

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/stablecoins/BAUSD.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;


import { PaxosTokenV2 } from "./../PaxosTokenV2.sol";

/**
 * @title BAUSD Smart contract
 * @dev This contract is a {PaxosTokenV2-PaxosTokenV2} ERC20 token.
 * This is just a test token, it's only used for test purposes by Blockaid
 * Do not interact with this token
 * @custom:security-contact hamzab@blockaid.co
 */
contract BAUSD is PaxosTokenV2 {
    /*
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return "Blockaid USD";
    }

    /*
     * @dev Returns the symbol of the token.
     */
    function symbol() public view virtual override returns (string memory) {
        return "BAUSD";
    }

    /*
     * @dev Returns the decimal count of the token.
     */
    function decimals() public view virtual override returns (uint8) {
        return 6;
    }
}"
    },
    "contracts/PaxosTokenV2.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { BaseStorage } from "./BaseStorage.sol";
import { SupplyControl } from "./SupplyControl.sol";
import { EIP2612 } from "./lib/EIP2612.sol";
import { EIP3009 } from "./lib/EIP3009.sol";
import { EIP712 } from "./lib/EIP712.sol";
import { AccessControlDefaultAdminRulesUpgradeable } from "./openzeppelin/contracts-upgradeable/access/AccessControlDefaultAdminRulesUpgradeable.sol";

/**
 * @title PaxosTokenV2
 * @dev this contract is a Pausable ERC20 token with Burn and Mint
 * controlled by a `SupplyControl` contract.
 * NOTE: The storage defined here will actually be held in the Proxy
 * contract and all calls to this contract should be made through
 * the proxy, including admin actions done as owner or supplyController.
 * Any call to transfer against this contract should fail
 * with insufficient funds since no tokens will be issued there.
 * @custom:security-contact smart-contract-security@paxos.com
 */
contract PaxosTokenV2 is BaseStorage, EIP2612, EIP3009, AccessControlDefaultAdminRulesUpgradeable {
    /**
     * EVENTS
     */

    // ERC20 BASIC EVENTS
    event Transfer(address indexed from, address indexed to, uint256 value);

    // ERC20 EVENTS
    event Approval(address indexed owner, address indexed spender, uint256 value);

    // PAUSABLE EVENTS
    event Pause();
    event Unpause();

    // ASSET PROTECTION EVENTS
    event FrozenAddressWiped(address indexed addr);
    event FreezeAddress(address indexed addr);
    event UnfreezeAddress(address indexed addr);

    // SUPPLY CONTROL EVENTS
    event SupplyIncreased(address indexed to, uint256 value);
    event SupplyDecreased(address indexed from, uint256 value);
    event SupplyControlSet(address supplyControlAddress);

    // Event when sanction address changes.
    event SanctionedAddressListUpdate(address newSanctionedAddress);

    /**
     * ERRORS
     */
    error OnlySupplyController();
    error InsufficientFunds();
    error AddressNotFrozen();
    error ZeroValue();
    error AlreadyPaused();
    error AlreadyUnPaused();
    error InsufficientAllowance();
    error SupplyControllerUnchanged();
    error OnlySupplyControllerOrOwner();

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /**
     * External Functions
     */

    /**
     * @notice Reclaim all tokens at the contract address
     * @dev Transfers the tokens this contract holds, to the owner of smart contract.
     * Note: This is not affected by freeze constraints.
     */
    function reclaimToken() external onlyRole(DEFAULT_ADMIN_ROLE) {
        uint256 _balance = balances[address(this)];
        address owner = owner();
        balances[address(this)] = 0;
        balances[owner] += _balance;
        emit Transfer(address(this), owner, _balance);
    }

    /**
     * @dev Update the supply control contract which controls minting and burning for this token.
     * @param supplyControlAddress Supply control contract address
     */
    function setSupplyControl(
        address supplyControlAddress
    ) external onlyRole(DEFAULT_ADMIN_ROLE) isNonZeroAddress(supplyControlAddress) {
        supplyControl = SupplyControl(supplyControlAddress);
        emit SupplyControlSet(supplyControlAddress);
    }

    /**
     * @notice Return the freeze status of an address.
     * @dev Check if whether the address is currently frozen.
     * @param addr The address to check if frozen.
     * @return A bool representing whether the given address is frozen.
     */
    function isFrozen(address addr) external view returns (bool) {
        return _isAddrFrozen(addr);
    }

    /**
     * Public Functions
     */

    /**
     * @notice Initialize the contract.
     * @dev Wrapper around {_initialize}. This is useful to get the version before
     * it is updated by {reinitializer}.
     * @param initialDelay Initial delay for changing the owner
     * @param initialOwner Address of the initial owner
     * @param pauser Address of the pauser
     * @param assetProtector Address of the asset protector
     */
    function initialize(uint48 initialDelay, address initialOwner, address pauser, address assetProtector) public {
        uint64 pastVersion = _getInitializedVersion();
        _initialize(pastVersion, initialDelay, initialOwner, pauser, assetProtector);
    }

    /**
     * @notice Initialize the domain separator for the contract.
     * @dev This is public to allow for updates to the domain separator if the name is updated.
     */
    function initializeDomainSeparator() public {
        _initializeDomainSeparator();
    }

    /**
     * @notice Returns the total supply of the token.
     * @return An uint256 representing the total supply of the token.
     */
    function totalSupply() public view returns (uint256) {
        return totalSupply_;
    }

    /**
     * @notice Execute a transfer
     * @dev Transfer token to the specified address from msg.sender
     * @param to The address to transfer to
     * @param value The amount to be transferred
     * @return True if successful
     */
    function transfer(address to, uint256 value) public whenNotPaused returns (bool) {
        _transfer(msg.sender, to, value);
        return true;
    }

    /**
     * @notice Gets the balance of the specified address
     * @param addr The address to query the the balance of
     * @return An uint256 representing the amount owned by the passed address
     */
    function balanceOf(address addr) public view returns (uint256) {
        return balances[addr];
    }

    /**
     * @notice Transfer tokens from one address to another
     * @param from address The address which you want to send tokens from
     * @param to address The address which you want to transfer to
     * @param value uint256 the amount of tokens to be transferred
     * @return True if successful
     */
    function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) {
        if (_isAddrFrozen(msg.sender)) revert AddressFrozen();
        _transferFromAllowance(from, to, value);
        return true;
    }

    /**
     * @notice Transfer tokens from one set of addresses to another in a single transaction
     * @param from addres[] The addresses which you want to send tokens from
     * @param to address[] The addresses which you want to transfer to
     * @param value uint256[] The amounts of tokens to be transferred
     * @return True if successful
     */
    function transferFromBatch(
        address[] calldata from,
        address[] calldata to,
        uint256[] calldata value
    ) public whenNotPaused returns (bool) {
        // Validate length of each parameter with "_from" argument to make sure lengths of all input arguments are the same.
        if (to.length != from.length || value.length != from.length) revert ArgumentLengthMismatch();
        if (_isAddrFrozen(msg.sender)) revert AddressFrozen();
        for (uint16 i = 0; i < from.length; i++) {
            _transferFromAllowance(from[i], to[i], value[i]);
        }
        return true;
    }

    /**
     * @notice Set allowance of spender to spend tokens on behalf of msg.sender
     * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
     * Beware that changing an allowance with this method brings the risk that someone may use both the old
     * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
     * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     * @param spender The address which will spend the funds
     * @param value The amount of tokens to be spent
     * @return True if successful
     */
    function approve(address spender, uint256 value) public whenNotPaused isNonZeroAddress(spender) returns (bool) {
        if (_isAddrFrozen(spender) || _isAddrFrozen(msg.sender)) revert AddressFrozen();
        _approve(msg.sender, spender, value);
        return true;
    }

    /**
     * @notice Increase the allowance of spender to spend tokens on behalf of msg.sender
     * @dev Increase the amount of tokens that an owner allowed to a spender.
     * To increment allowed value is better to use this function to avoid 2 calls (and wait until the first transaction
     * is mined) instead of approve.
     * @param spender The address which will spend the funds
     * @param addedValue The amount of tokens to increase the allowance by
     * @return True if successful
     */
    function increaseApproval(address spender, uint256 addedValue) public whenNotPaused returns (bool) {
        if (_isAddrFrozen(spender) || _isAddrFrozen(msg.sender)) revert AddressFrozen();
        if (addedValue == 0) revert ZeroValue();
        allowed[msg.sender][spender] += addedValue;
        emit Approval(msg.sender, spender, allowed[msg.sender][spender]);
        return true;
    }

    /**
     * @notice Decrease the allowance of spender to spend tokens on behalf of msg.sender
     * @dev Decrease the amount of tokens that an owner allowed to a spender.
     * To decrement allowed value is better to use this function to avoid 2 calls (and wait until the first transaction
     * is mined) instead of approve.
     * @param spender The address which will spend the funds
     * @param subtractedValue The amount of tokens to decrease the allowance by
     * @return True if successful
     */
    function decreaseApproval(address spender, uint256 subtractedValue) public whenNotPaused returns (bool) {
        if (_isAddrFrozen(spender) || _isAddrFrozen(msg.sender)) revert AddressFrozen();
        if (subtractedValue == 0) revert ZeroValue();
        if (subtractedValue > allowed[msg.sender][spender]) {
            allowed[msg.sender][spender] = 0;
        } else {
            allowed[msg.sender][spender] -= subtractedValue;
        }
        emit Approval(msg.sender, spender, allowed[msg.sender][spender]);
        return true;
    }

    /**
     * @dev Get the amount of token allowance that an owner allowed to a spender
     * @param owner address The address which owns the funds
     * @param spender address The address which will spend the funds
     * @return A uint256 specifying the amount of tokens still available for the spender
     */
    function allowance(address owner, address spender) public view returns (uint256) {
        return allowed[owner][spender];
    }

    /**
     * @notice Pause the contract
     * @dev called by the owner to pause, triggers stopped state
     */
    function pause() public onlyRole(PAUSE_ROLE) {
        if (paused) revert AlreadyPaused();
        paused = true;
        emit Pause();
    }

    /**
     * @notice Unpause the contract
     * @dev called by the owner to unpause, returns to normal state
     */
    function unpause() public onlyRole(PAUSE_ROLE) {
        if (!paused) revert AlreadyUnPaused();
        paused = false;
        emit Unpause();
    }

    // ASSET PROTECTION FUNCTIONALITY
    /**
     * @notice Wipe the token balance of a frozen address
     * @dev Wipes the balance of a frozen address, and burns the tokens
     * @param addr The new frozen address to wipe
     */
    function wipeFrozenAddress(address addr) public onlyRole(ASSET_PROTECTION_ROLE) {
        if (!_isAddrFrozen(addr)) revert AddressNotFrozen();
        uint256 balance = balances[addr];
        balances[addr] = 0;
        totalSupply_ -= balance;
        emit FrozenAddressWiped(addr);
        emit SupplyDecreased(addr, balance);
        emit Transfer(addr, address(0), balance);
    }

    /**
     * @dev Freezes an address balance from being transferred.
     * @param addr The address to freeze.
     */
    function freeze(address addr) public onlyRole(ASSET_PROTECTION_ROLE) {
        _freeze(addr);
    }

    /**
     * @dev Freezes all addresses balance from being transferred.
     * @param addresses The addresses to freeze.
     */
    function freezeBatch(address[] calldata addresses) public onlyRole(ASSET_PROTECTION_ROLE) {
        for (uint256 i = 0; i < addresses.length; ) {
            _freeze(addresses[i]);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @dev Unfreezes an address balance allowing transfer.
     * @param addr The new address to unfreeze.
     */
    function unfreeze(address addr) public onlyRole(ASSET_PROTECTION_ROLE) {
        _unfreeze(addr);
    }

    /**
     * @dev Unfreezes all addresses balance from being transferred.
     * @param addresses The addresses to unfreeze.
     */
    function unfreezeBatch(address[] calldata addresses) public onlyRole(ASSET_PROTECTION_ROLE) {
        for (uint256 i = 0; i < addresses.length; ) {
            _unfreeze(addresses[i]);
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Increases the total supply by minting the specified number of tokens to the supply controller account.
     * Function is marked virtual to aid in testing, but is never overridden on the actual token.
     * @param value The number of tokens to add
     * @param mintToAddress Address to mint tokens to.
     * @return success A boolean that indicates if the operation was successful
     */
    function increaseSupplyToAddress(uint256 value, address mintToAddress) public virtual returns (bool success) {
        require(!_isAddrFrozen(mintToAddress), "mintToAddress frozen");
        supplyControl.canMintToAddress(mintToAddress, value, msg.sender);

        totalSupply_ += value;
        balances[mintToAddress] += value;
        emit SupplyIncreased(mintToAddress, value);
        emit Transfer(address(0), mintToAddress, value);
        return true;
    }

    /**
     * @dev Wrapper around 'increaseSupplyToAddress' to extend the API
     * @param value The number of tokens to add. 
     * @return success A boolean that indicates if the operation was successful
     */
    function increaseSupply(uint256 value) public returns (bool success) {
        return increaseSupplyToAddress(value, msg.sender);
    }

    /**
     * @dev Wrapper around `increaseSupplyToAddress` to extend the API
     * @param account Address to mint tokens to
     * @param amount The number of tokens to add
     */
    function mint(address account, uint256 amount) public {
        increaseSupplyToAddress(amount, account);
    }

    /**
     * @notice Decreases the total supply by burning the specified number of tokens.  Can only be called by a
     * supply controller. Function is marked virtual to aid in testing, but is never overridden on the actual token.
     * @param value The number of tokens to remove
     * @param burnFromAddress Address to burn tokens from.
     * @return success A boolean that indicates if the operation was successful
     */
    function decreaseSupplyFromAddress(uint256 value, address burnFromAddress) public virtual returns (bool success) {
        require(!_isAddrFrozen(burnFromAddress), "burnFromAddress frozen");
        supplyControl.canBurnFromAddress(burnFromAddress, msg.sender);
        if (value > balances[burnFromAddress]) revert InsufficientFunds();

        balances[burnFromAddress] -= value;
        totalSupply_ -= value;
        emit SupplyDecreased(burnFromAddress, value);
        emit Transfer(burnFromAddress, address(0), value);
        return true;
    }

    /**
     * @dev Wrapper around 'decreaseSupplyFromAddress' to extend the API
     * @param value The number of tokens to remove.  
     * @return success A boolean that indicates if the operation was successful
     */
    function decreaseSupply(uint256 value) public returns (bool success) {
        return decreaseSupplyFromAddress(value, msg.sender);
    }

    /**
     * @dev Wrapper around `decreaseSupply` to extend the API
     * @param amount The number of tokens to remove
     */
    function burn(uint256 amount) public {
        decreaseSupply(amount);
    }

    /**
     * Internal Functions
     */

    /**
     * @dev See {PaxosBaseAbstract-_isPaused}
     */
    function _isPaused() internal view override returns (bool) {
        return paused;
    }

    /**
     * @dev See {PaxosBaseAbstract-_isAddrFrozen}
     */
    function _isAddrFrozen(address addr) internal view override returns (bool) {
        return frozen[addr];
    }

    /**
     * @dev Internal function to transfer balances from => to.
     * Internal to the contract - see transferFrom and transferFromBatch.
     * @param from address The address which you want to send tokens from
     * @param to address The address which you want to transfer to
     * @param value uint256 the amount of tokens to be transferred
     */
    function _transferFromAllowance(address from, address to, uint256 value) internal {
        if (value > allowed[from][msg.sender]) revert InsufficientAllowance();
        _transfer(from, to, value);
        allowed[from][msg.sender] -= value;
    }

    /**
     * @dev See {PaxosBaseAbstract-_approve}
     */
    function _approve(address owner, address spender, uint256 value) internal override {
        allowed[owner][spender] = value;
        emit Approval(owner, spender, value);
    }

    /**
     * @dev See {PaxosBaseAbstract-_transfer}
     */
    function _transfer(address from, address to, uint256 value) internal override isNonZeroAddress(to) {
        if (_isAddrFrozen(to) || _isAddrFrozen(from)) revert AddressFrozen();
        if (value > balances[from]) revert InsufficientFunds();

        balances[from] -= value;
        balances[to] += value;
        emit Transfer(from, to, value);
    }

    /**
     * Private Functions
     */

    /**
     * @dev Called on deployment, can only be called once. If the contract is ever upgraded,
     * the version in reinitializer will be incremented and additional initialization logic
     * can be added for the new version.
     * @param pastVersion Previous contract version
     * @param initialDelay Initial delay for changing the owner
     * @param initialOwner Address of the initial owner
     * @param pauser Address of the pauser
     * @param assetProtector Address of the asset protector
     */
    function _initialize(
        uint64 pastVersion,
        uint48 initialDelay,
        address initialOwner,
        address pauser,
        address assetProtector
    ) private reinitializer(2) {
        _initializeV1(pastVersion);
        _initializeV2(initialDelay, initialOwner, pauser, assetProtector);
    }

    /**
     * @dev Called on deployment to initialize V1 state. If contract already initialized,
     * it returns immediately.
     * @param pastVersion Previous contract version
     */
    function _initializeV1(uint64 pastVersion) private {
        if (pastVersion < 1 && !initializedV1) {
            //Need this second condition since V1 could have used old upgrade pattern
            totalSupply_ = 0;
            initializedV1 = true;
        }
    }

    /**
     * @dev Called on deployment to initialize V2 state
     * @param initialDelay Initial delay for changing the owner
     * @param initialOwner Address of the initial owner
     * @param pauser Address of the pauser
     * @param assetProtector Address of the assetProtector
     */
    function _initializeV2(
        uint48 initialDelay,
        address initialOwner,
        address pauser,
        address assetProtector
    ) private isNonZeroAddress(pauser) isNonZeroAddress(assetProtector) {
        __AccessControlDefaultAdminRules_init(initialDelay, initialOwner);
        _grantRole(PAUSE_ROLE, pauser);
        _grantRole(ASSET_PROTECTION_ROLE, assetProtector);
        _initializeDomainSeparator();
    }

    /**
     * @dev Private function to initialize the domain separator for the contract.
     */
    function _initializeDomainSeparator() private {
        DOMAIN_SEPARATOR = EIP712._makeDomainSeparator(name(), "1");
    }

    /**
     * @dev Private function to Freezes an address balance from being transferred.
     * @param addr The addresses to freeze.
     */
    function _freeze(address addr) private {
        frozen[addr] = true;
        emit FreezeAddress(addr);
    }

    /**
     * @dev Private function to Unfreezes an address balance from being transferred.
     * @param addr The addresses to unfreeze.
     */
    function _unfreeze(address addr) private {
        delete frozen[addr];
        emit UnfreezeAddress(addr);
    }
}
"
    },
    "contracts/openzeppelin/contracts-upgradeable/access/AccessControlDefaultAdminRulesUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControlDefaultAdminRules.sol)

pragma solidity ^0.8.0;

import "./AccessControlUpgradeable.sol";
import "./IAccessControlDefaultAdminRulesUpgradeable.sol";
import "../utils/math/SafeCastUpgradeable.sol";
import "../interfaces/IERC5313Upgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Extension of {AccessControl} that allows specifying special rules to manage
 * the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions
 * over other roles that may potentially have privileged rights in the system.
 *
 * If a specific role doesn't have an admin role assigned, the holder of the
 * `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.
 *
 * This contract implements the following risk mitigations on top of {AccessControl}:
 *
 * * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.
 * * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.
 * * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.
 * * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.
 * * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.
 *
 * Example usage:
 *
 * ```solidity
 * contract MyToken is AccessControlDefaultAdminRules {
 *   constructor() AccessControlDefaultAdminRules(
 *     3 days,
 *     msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder
 *    ) {}
 * }
 * ```
 *
 * _Available since v4.9._
 */
abstract contract AccessControlDefaultAdminRulesUpgradeable is Initializable, IAccessControlDefaultAdminRulesUpgradeable, IERC5313Upgradeable, AccessControlUpgradeable {
    // pending admin pair read/written together frequently
    address private _pendingDefaultAdmin;
    uint48 private _pendingDefaultAdminSchedule; // 0 == unset

    uint48 private _currentDelay;
    address private _currentDefaultAdmin;

    // pending delay pair read/written together frequently
    uint48 private _pendingDelay;
    uint48 private _pendingDelaySchedule; // 0 == unset

    /**
     * @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
     */
    function __AccessControlDefaultAdminRules_init(uint48 initialDelay, address initialDefaultAdmin) internal onlyInitializing {
        __AccessControlDefaultAdminRules_init_unchained(initialDelay, initialDefaultAdmin);
    }

    function __AccessControlDefaultAdminRules_init_unchained(uint48 initialDelay, address initialDefaultAdmin) internal onlyInitializing {
        require(initialDefaultAdmin != address(0), "AccessControl: 0 default admin");
        _currentDelay = initialDelay;
        _grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControlDefaultAdminRulesUpgradeable).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC5313-owner}.
     */
    function owner() public view virtual returns (address) {
        return defaultAdmin();
    }

    ///
    /// Override AccessControl role management
    ///

    /**
     * @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
     */
    function grantRole(bytes32 role, address account) public virtual override(AccessControlUpgradeable, IAccessControlUpgradeable) {
        require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly grant default admin role");
        super.grantRole(role, account);
    }

    /**
     * @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
     */
    function revokeRole(bytes32 role, address account) public virtual override(AccessControlUpgradeable, IAccessControlUpgradeable) {
        require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly revoke default admin role");
        super.revokeRole(role, account);
    }

    /**
     * @dev See {AccessControl-renounceRole}.
     *
     * For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling
     * {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule
     * has also passed when calling this function.
     *
     * After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.
     *
     * NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},
     * thereby disabling any functionality that is only available for it, and the possibility of reassigning a
     * non-administrated role.
     */
    function renounceRole(bytes32 role, address account) public virtual override(AccessControlUpgradeable, IAccessControlUpgradeable) {
        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
            (address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
            require(
                newDefaultAdmin == address(0) && _isScheduleSet(schedule) && _hasSchedulePassed(schedule),
                "AccessControl: only can renounce in two delayed steps"
            );
            delete _pendingDefaultAdminSchedule;
        }
        super.renounceRole(role, account);
    }

    /**
     * @dev See {AccessControl-_grantRole}.
     *
     * For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the
     * role has been previously renounced.
     *
     * NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`
     * assignable again. Make sure to guarantee this is the expected behavior in your implementation.
     */
    function _grantRole(bytes32 role, address account) internal virtual override {
        if (role == DEFAULT_ADMIN_ROLE) {
            require(defaultAdmin() == address(0), "AccessControl: default admin already granted");
            _currentDefaultAdmin = account;
        }
        super._grantRole(role, account);
    }

    /**
     * @dev See {AccessControl-_revokeRole}.
     */
    function _revokeRole(bytes32 role, address account) internal virtual override {
        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
            delete _currentDefaultAdmin;
        }
        super._revokeRole(role, account);
    }

    /**
     * @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
        require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't violate default admin rules");
        super._setRoleAdmin(role, adminRole);
    }

    ///
    /// AccessControlDefaultAdminRules accessors
    ///

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function defaultAdmin() public view virtual returns (address) {
        return _currentDefaultAdmin;
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
        return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function defaultAdminDelay() public view virtual returns (uint48) {
        uint48 schedule = _pendingDelaySchedule;
        return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
        schedule = _pendingDelaySchedule;
        return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {
        return 5 days;
    }

    ///
    /// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin
    ///

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _beginDefaultAdminTransfer(newAdmin);
    }

    /**
     * @dev See {beginDefaultAdminTransfer}.
     *
     * Internal function without access restriction.
     */
    function _beginDefaultAdminTransfer(address newAdmin) internal virtual {
        uint48 newSchedule = SafeCastUpgradeable.toUint48(block.timestamp) + defaultAdminDelay();
        _setPendingDefaultAdmin(newAdmin, newSchedule);
        emit DefaultAdminTransferScheduled(newAdmin, newSchedule);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _cancelDefaultAdminTransfer();
    }

    /**
     * @dev See {cancelDefaultAdminTransfer}.
     *
     * Internal function without access restriction.
     */
    function _cancelDefaultAdminTransfer() internal virtual {
        _setPendingDefaultAdmin(address(0), 0);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function acceptDefaultAdminTransfer() public virtual {
        (address newDefaultAdmin, ) = pendingDefaultAdmin();
        require(_msgSender() == newDefaultAdmin, "AccessControl: pending admin must accept");
        _acceptDefaultAdminTransfer();
    }

    /**
     * @dev See {acceptDefaultAdminTransfer}.
     *
     * Internal function without access restriction.
     */
    function _acceptDefaultAdminTransfer() internal virtual {
        (address newAdmin, uint48 schedule) = pendingDefaultAdmin();
        require(_isScheduleSet(schedule) && _hasSchedulePassed(schedule), "AccessControl: transfer delay not passed");
        _revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
        _grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
        delete _pendingDefaultAdmin;
        delete _pendingDefaultAdminSchedule;
    }

    ///
    /// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay
    ///

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _changeDefaultAdminDelay(newDelay);
    }

    /**
     * @dev See {changeDefaultAdminDelay}.
     *
     * Internal function without access restriction.
     */
    function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {
        uint48 newSchedule = SafeCastUpgradeable.toUint48(block.timestamp) + _delayChangeWait(newDelay);
        _setPendingDelay(newDelay, newSchedule);
        emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);
    }

    /**
     * @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
     */
    function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _rollbackDefaultAdminDelay();
    }

    /**
     * @dev See {rollbackDefaultAdminDelay}.
     *
     * Internal function without access restriction.
     */
    function _rollbackDefaultAdminDelay() internal virtual {
        _setPendingDelay(0, 0);
    }

    /**
     * @dev Returns the amount of seconds to wait after the `newDelay` will
     * become the new {defaultAdminDelay}.
     *
     * The value returned guarantees that if the delay is reduced, it will go into effect
     * after a wait that honors the previously set delay.
     *
     * See {defaultAdminDelayIncreaseWait}.
     */
    function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {
        uint48 currentDelay = defaultAdminDelay();

        // When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up
        // to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day
        // to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new
        // delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like
        // using milliseconds instead of seconds.
        //
        // When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees
        // that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled.
        // For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.
        return
            newDelay > currentDelay
                ? uint48(MathUpgradeable.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48
                : currentDelay - newDelay;
    }

    ///
    /// Private setters
    ///

    /**
     * @dev Setter of the tuple for pending admin and its schedule.
     *
     * May emit a DefaultAdminTransferCanceled event.
     */
    function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {
        (, uint48 oldSchedule) = pendingDefaultAdmin();

        _pendingDefaultAdmin = newAdmin;
        _pendingDefaultAdminSchedule = newSchedule;

        // An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.
        if (_isScheduleSet(oldSchedule)) {
            // Emit for implicit cancellations when another default admin was scheduled.
            emit DefaultAdminTransferCanceled();
        }
    }

    /**
     * @dev Setter of the tuple for pending delay and its schedule.
     *
     * May emit a DefaultAdminDelayChangeCanceled event.
     */
    function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {
        uint48 oldSchedule = _pendingDelaySchedule;

        if (_isScheduleSet(oldSchedule)) {
            if (_hasSchedulePassed(oldSchedule)) {
                // Materialize a virtual delay
                _currentDelay = _pendingDelay;
            } else {
                // Emit for implicit cancellations when another delay was scheduled.
                emit DefaultAdminDelayChangeCanceled();
            }
        }

        _pendingDelay = newDelay;
        _pendingDelaySchedule = newSchedule;
    }

    ///
    /// Private helpers
    ///

    /**
     * @dev Defines if an `schedule` is considered set. For consistency purposes.
     */
    function _isScheduleSet(uint48 schedule) private pure returns (bool) {
        return schedule != 0;
    }

    /**
     * @dev Defines if an `schedule` is considered passed. For consistency purposes.
     */
    function _hasSchedulePassed(uint48 schedule) private view returns (bool) {
        return schedule < block.timestamp;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[48] private __gap;
}
"
    },
    "contracts/lib/EIP712.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { ECRecover } from "./ECRecover.sol";

/**
 * @title EIP712
 * @notice A library that provides EIP712 helper functions
 * @custom:security-contact smart-contract-security@paxos.com
 */
library EIP712 {
    // keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
    bytes32 public constant EIP712_DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /**
     * @notice Make EIP712 domain separator
     * @param name      Contract name
     * @param version   Contract version
     * @return Domain separator
     */
    function _makeDomainSeparator(string memory name, string memory version) internal view returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    EIP712_DOMAIN_TYPEHASH,
                    keccak256(bytes(name)),
                    keccak256(bytes(version)),
                    block.chainid,
                    address(this)
                )
            );
    }

    /**
     * @notice Recover signer's address from a EIP712 signature
     * @param domainSeparator   Domain separator
     * @param v                 v of the signature
     * @param r                 r of the signature
     * @param s                 s of the signature
     * @param typeHashAndData   Type hash concatenated with data
     * @return Signer's address
     */
    function _recover(
        bytes32 domainSeparator,
        uint8 v,
        bytes32 r,
        bytes32 s,
        bytes memory typeHashAndData
    ) internal pure returns (address) {
        bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, keccak256(typeHashAndData)));
        return ECRecover.recover(digest, v, r, s);
    }
}
"
    },
    "contracts/lib/EIP3009.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { PaxosBaseAbstract } from "./PaxosBaseAbstract.sol";
import { EIP712Domain } from "./EIP712Domain.sol";
import { EIP712 } from "./EIP712.sol";

/**
 * @title EIP3009 contract
 * @dev An abstract contract to provide EIP3009 functionality.
 * @notice These functions do not prevent replay attacks when an initial 
 * transaction fails. If conditions change, such as the contract going
 * from paused to unpaused, an external observer can reuse the data from the 
 * failed transaction to execute it later.
 * @custom:security-contact smart-contract-security@paxos.com
 */
abstract contract EIP3009 is PaxosBaseAbstract, EIP712Domain {
    // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
    bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
        0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;

    // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
    bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH =
        0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;

    // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
    bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH =
        0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;

    error CallerMustBePayee();
    error AuthorizationInvalid();
    error AuthorizationExpired();
    error BlockedAccountAuthorizer();

    /**
     * @dev authorizer address => nonce => state (true = used / false = unused)
     */
    mapping(address => mapping(bytes32 => bool)) internal _authorizationStates;

    // Storage gap: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps
    uint256[10] __gap_EIP3009;

    event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
    event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce);
    event AuthorizationAlreadyUsed(address indexed authorizer, bytes32 indexed nonce);

    /**
     * @notice Returns the state of an authorization
     * @dev Nonces are randomly generated 32-byte data unique to the authorizer's
     * address
     * @param authorizer    Authorizer's address
     * @param nonce         Nonce of the authorization
     * @return True if the nonce is used
     */
    function authorizationState(address authorizer, bytes32 nonce) external view returns (bool) {
        return _authorizationStates[authorizer][nonce];
    }

    /**
     * @notice Execute a transfer with a signed authorization
     * @param from          Payer's address (Authorizer)
     * @param to            Payee's address
     * @param value         Amount to be transferred
     * @param validAfter    The time after which this is valid (unix time)
     * @param validBefore   The time before which this is valid (unix time)
     * @param nonce         Unique nonce
     * @param v             v of the signature
     * @param r             r of the signature
     * @param s             s of the signature
     */
    function transferWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external whenNotPaused {
        _transferWithAuthorization(
            TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
            from,
            to,
            value,
            validAfter,
            validBefore,
            nonce,
            v,
            r,
            s
        );
    }

    function transferWithAuthorizationBatch(
        address[] memory from,
        address[] memory to,
        uint256[] memory value,
        uint256[] memory validAfter,
        uint256[] memory validBefore,
        bytes32[] memory nonce,
        uint8[] memory v,
        bytes32[] memory r,
        bytes32[] memory s
    ) external whenNotPaused {
        // Validate length of each parameter with "from" argument to make sure lengths of all input arguments are the same.
        if (
            !(to.length == from.length &&
                value.length == from.length &&
                validAfter.length == from.length &&
                validBefore.length == from.length &&
                nonce.length == from.length &&
                v.length == from.length &&
                r.length == from.length &&
                s.length == from.length)
        ) {
            revert ArgumentLengthMismatch();
        }

        for (uint16 i = 0; i < from.length; i++) {
            _transferWithAuthorization(
                TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
                from[i],
                to[i],
                value[i],
                validAfter[i],
                validBefore[i],
                nonce[i],
                v[i],
                r[i],
                s[i]
            );
        }
    }

    /**
     * @notice Receive a transfer with a signed authorization from the payer
     * @dev This has an additional check to ensure that the payee's address matches
     * the caller of this function to prevent front-running attacks. (See security
     * considerations)
     * @param from          Payer's address (Authorizer)
     * @param to            Payee's address
     * @param value         Amount to be transferred
     * @param validAfter    The time after which this is valid (unix time)
     * @param validBefore   The time before which this is valid (unix time)
     * @param nonce         Unique nonce
     * @param v             v of the signature
     * @param r             r of the signature
     * @param s             s of the signature
     */
    function receiveWithAuthorization(
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external whenNotPaused {
        if (to != msg.sender) revert CallerMustBePayee();

        _transferWithAuthorization(
            RECEIVE_WITH_AUTHORIZATION_TYPEHASH,
            from,
            to,
            value,
            validAfter,
            validBefore,
            nonce,
            v,
            r,
            s
        );
    }

    /**
     * @notice Attempt to cancel an authorization
     * @param authorizer    Authorizer's address
     * @param nonce         Nonce of the authorization
     * @param v             v of the signature
     * @param r             r of the signature
     * @param s             s of the signature
     */
    function cancelAuthorization(
        address authorizer,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external whenNotPaused {
        if (_isAddrFrozen(authorizer)) revert AddressFrozen();
        if (_authorizationStates[authorizer][nonce]) {
            emit AuthorizationAlreadyUsed(authorizer, nonce);
            return; //Return instead of throwing an error to prevent front running from blocking complex txs
        }

        bytes memory data = abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce);
        if (EIP712._recover(DOMAIN_SEPARATOR, v, r, s, data) != authorizer) revert InvalidSignature();

        _authorizationStates[authorizer][nonce] = true;
        emit AuthorizationCanceled(authorizer, nonce);
    }

    /*
     * @dev Internal function to execute a single transfer with a signed authorization
     * @param typeHash      The typehash of transfer or receive.
     * @param from          Payer's address (Authorizer)
     * @param to            Payee's address
     * @param value         Amount to be transferred
     * @param validAfter    The time after which this is valid (unix time)
     * @param validBefore   The time before which this is valid (unix time)
     * @param nonce         Unique nonce
     * @param v             v of the signature
     * @param r             r of the signature
     * @param s             s of the signature
     */
    function _transferWithAuthorization(
        bytes32 typeHash,
        address from,
        address to,
        uint256 value,
        uint256 validAfter,
        uint256 validBefore,
        bytes32 nonce,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        if (block.timestamp <= validAfter) revert AuthorizationInvalid();
        if (block.timestamp >= validBefore) revert AuthorizationExpired();

        if (_authorizationStates[from][nonce]) {
            emit AuthorizationAlreadyUsed(from, nonce);
            return; //Return instead of throwing an error to prevent front running from blocking batches
        }

        bytes memory data = abi.encode(typeHash, from, to, value, validAfter, validBefore, nonce);
        if (EIP712._recover(DOMAIN_SEPARATOR, v, r, s, data) != from) revert InvalidSignature();

        _authorizationStates[from][nonce] = true;
        emit AuthorizationUsed(from, nonce);

        _transfer(from, to, value);
    }
}
"
    },
    "contracts/lib/EIP2612.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { PaxosBaseAbstract } from "./PaxosBaseAbstract.sol";
import { EIP712Domain } from "./EIP712Domain.sol";
import { EIP712 } from "./EIP712.sol";

/**
 * @title EIP2612 contract
 * @dev An abstract contract to provide EIP2612 functionality.
 * @custom:security-contact smart-contract-security@paxos.com
 */
abstract contract EIP2612 is PaxosBaseAbstract, EIP712Domain {
    // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
    bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    mapping(address => uint256) internal _nonces;
    // Storage gap: https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#storage-gaps
    uint256[10] __gap_EIP2612;

    error PermitExpired();

    /**
     * @notice Nonces for permit
     * @param owner Token owner's address
     * @return Next nonce
     */
    function nonces(address owner) external view returns (uint256) {
        return _nonces[owner];
    }

    /**
     * @notice Update allowance with a signed permit
     * @param owner     Token owner's address (Authorizer)
     * @param spender   Spender's address
     * @param value     Amount of allowance
     * @param deadline  The time at which this expires (unix time)
     * @param v         v of the signature
     * @param r         r of the signature
     * @param s         s of the signature
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external whenNotPaused isNonZeroAddress(owner) isNonZeroAddress(spender) {
        if (deadline < block.timestamp) revert PermitExpired();
        if (_isAddrFrozen(spender) || _isAddrFrozen(owner)) revert AddressFrozen();

        bytes memory data = abi.encode(PERMIT_TYPEHASH, owner, spender, value, _nonces[owner]++, deadline);

        if (EIP712._recover(DOMAIN_SEPARATOR, v, r, s, data) != owner) revert InvalidSignature();

        _approve(owner, spender, value);
    }
}
"
    },
    "contracts/SupplyControl.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import { AccessControlDefaultAdminRulesUpgradeable } from "./openzeppelin/contracts-upgradeable/access/AccessControlDefaultAdminRulesUpgradeable.sol";
import { UUPSUpgradeable } from "./openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { IAccessControl } from "./openzeppelin/contracts/access/IAccessControl.sol";
import { EnumerableSet } from "./openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { DoubleEndedQueue } from "./openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol";
import { PaxosBaseAbstract } from "./lib/PaxosBaseAbstract.sol";
import { RateLimit } from "./lib/RateLimit.sol";

/**
 * @title SupplyControl
 * @dev control the token supply. The `SUPPLY_CONTROLLER_MANAGER_ROLE` role is responsible for managing
 * addresses with the `SUPPLY_CONTROLLER_ROLE`, referred to as supplyControllers. Only supplyControllers can
 * mint and burn tokens. SupplyControllers can optionally have rate limits to limit how many tokens can be
 * minted over a given time frame.
 * @custom:security-contact smart-contract-security@paxos.com
 */
contract SupplyControl is AccessControlDefaultAdminRulesUpgradeable, UUPSUpgradeable {
    using EnumerableSet for EnumerableSet.AddressSet;
    // Access control roles
    // keccak256("SUPPLY_CONTROLLER_MANAGER_ROLE")
    // Can add, update, and remove `SupplyController`s
    bytes32 public constant SUPPLY_CONTROLLER_MANAGER_ROLE =
        0x5d3e9f1ecbcdad7b0da30e7d29c9eddaef83a4502dafe3d2dd85cfdb12e4af10;
    // keccak256("SUPPLY_CONTROLLER_ROLE")
    // Can mint/burn tokens
    bytes32 public constant SUPPLY_CONTROLLER_ROLE = 0x9c00d6f280439b1dfa4da90321e0a3f3c2e87280f4d07fea9fa43ff2cf02df2b;
    // keccak256("TOKEN_CONTRACT_ROLE")
    // Tracks the token contract to protect functions which impact rate limits
    bytes32 public constant TOKEN_CONTRACT_ROLE = 0xd32fd1ee5f4f111da6f27444787e5200ec57a8849509c00ef2998467052b32a3;

    // SUPPLY CONTROL DATA
    mapping(address => SupplyController) internal supplyControllerMap;

    //Used to get all supply controllers
    EnumerableSet.AddressSet internal supplyControllerSet;

    uint256[35] private __gap_SC; // solhint-disable-line var-name-mixedcase

    /**
     * @dev Struct defines a supply controller. Different supply controllers can have different rules.
     * @param rateLimit Contract which handles rate limit logic
     * @param mintAddressWhitelist Addresses the {SupplyController} can mint to
     * @param allowAnyMintAndBurnAddress If true, allows the supply controller to mint to and burn from any address
     */
    struct SupplyController {
        RateLimit.Storage rateLimitStorage;
        EnumerableSet.AddressSet mintAddressWhitelist;
        bool allowAnyMintAndBurnAddress;
    }

    /**
     * @dev Struct defines the configuration needed when creating a new supply controller.
     * @param newSupplyController Address of the new supply controller
     * @param limitConfig Limit configuration
     * @param mintAddressWhitelist Addresses the supply controller can mint to
     * @param allowAnyMintAndBurnAddress If true, allows the supply controller to mint to and burn from any address
     */
    struct SupplyControllerInitialization {
        address newSupplyController;
        RateLimit.LimitConfig limitConfig;
        address[] mintAddressWhitelist;
        bool allowAnyMintAndBurnAddress;
    }

    /**
     * @dev Emitted when {addSupplyController} is called.
     * @param newSupplyController Address of the new supply controller
     * @param limitCapacity Max amount for the rate limit. Checked in `_checkCurrentPeriodAmount`
     * @param refillPerSecond Amount to add to limit each second up to the `limitCapacity`
     * @param mintAddressWhitelist Addresses the supply controller can mint to
     * @param allowAnyMintAndBurnAddress If true, allows the supply controller to mint to and burn from any address
     */
    event SupplyControllerAdded(
        address indexed newSupplyController,
        uint256 limitCapacity,
        uint256 refillPerSecond,
        address[] mintAddressWhitelist,
        bool allowAnyMintAndBurnAddress
    );

    /**
     * @dev Emitted when {removeSupplyController} is called.
     * @param oldSupplyController The old supply controller address
     */
    event SupplyControllerRemoved(address indexed oldSupplyController);

    /**
     * @dev Emitted when limit configuration is updated for `supplyController`.
     * Occurs when {updateLimitConfig} is called.
     * @param supplyController Supply controller address
     * @param newLimitConfig New limit configuration
     * @param oldLimitConfig Old limit configuration
     */
    event LimitConfigUpdated(
        address indexed supplyController,
        RateLimit.LimitConfig newLimitConfig,
        RateLimit.LimitConfig oldLimitConfig
    );

    /**
     * @dev Emitted when `allowAnyMintAndBurnAddress` is updated for `supplyController`.
     * Occurs when {updateAllowAnyMintAndBurnAddress} is called.
     * @param supplyController Supply controller address
     * @param newAllowAnyMintAndBurnAddress New allow config
     * @param oldAllowAnyMintAndBurnAddress Old allow config
     */
    event AllowAnyMintAndBurnAddressUpdated(
        address indexed supplyController,
        bool newAllowAnyMintAndBurnAddress,
        bool oldAllowAnyMintAndBurnAddress
    );

    /**
     * @dev Emitted when `mintAddress` is added to `mintAddressWhitelist` in `supplyController`.
     * Occurs when {addMintAddressToWhitelist} is called
     * @param supplyController Supply controller address
     * @param mintAddress New address which can be minted to
     */
    event MintAddressAddedToWhitelist(address indexed supplyController, address indexed mintAddress);

    /**
     * @dev Emitted when `mintAddress` is removed from `mintAddressWhitelist` in `supplyController`.
     * Occurs when {removeMintAddressFromWhitelist} is called
     * @param supplyController Supply controller address
     * @param mintAddress Address which can no longer be minted to
     */
    event MintAddressRemovedFromWhitelist(address indexed supplyController, address indexed mintAddress);

    error AccountMissingSupplyControllerRole(address account);
    error AccountAlreadyHasSupplyControllerRole(address account);
    error CannotMintToAddress(address supplyController, address mintToAddress);
    error CannotBurnFromAddress(address supplyController, address burnFromAddress);
    error CannotAddDuplicateAddress(address addressToAdd);
    error CannotRemoveNonExistantAddress(address addressToRemove);
    error ZeroAddress();

    /**
     * @dev Modifier which checks that the specified `supplyController` address has the SUPPLY_CONTROLLER_ROLE
     * @param supplyController Supply controller address
     */
    modifier onlySupplyController(address supplyController) {
        if (!hasRole(SUPPLY_CONTROLLER_ROLE, supplyController)) {
            revert AccountMissingSupplyControllerRole(supplyController);
        }
        _;
    }

    /**
     * @dev Modifier which checks that the specified `supplyController` address does not have the SUPPLY_CONTROLLER_ROLE
     * @param supplyController Supply controller address
     */
    modifier notSupplyController(address supplyController) {
        if (hasRole(SUPPLY_CONTROLLER_ROLE, supplyController)) {
            revert AccountAlreadyHasSupplyControllerRole(supplyController);
        }
        _;
    }

    /**
     * @dev Modifier to check for zero address.
     */
    modifier isNonZeroAddress(address addr) {
        if (addr == address(0)) {
            revert ZeroAddress();
        }
        _;
    }

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        _disableInitializers();
    }

    /**
     * @dev Initializer for SupplyControl.
     * Proper order of setting up the contracts:
     *  1. Deploy/reinitialize PaxosToken
     *  2. Deploy SupplyControl with `SupplyControllerInitialization` config
     *  3. Set SupplyControl address in PaxosToken via `setSupplyControl`
     * @param initialOwner Initial owner address
     * @param supplyControllerManager SupplyControllerManager address
     * @param tokenAddress Token contract address
     * @param scInitializationConfig Configuration to initialize a list of supply controllers
     */
    function initialize(
        address initialOwner,
        address supplyControllerManager,
        address tokenAddress,
        SupplyControllerInitialization[] calldata scInitializationConfig
    ) external initializer isNonZeroAddress(supplyControllerManager) isNonZeroAddress(tokenAddress) {
        __AccessControlDefaultAdminRules_init(3 hours, initialOwner);
        __UUPSUpgradeable_init();
        _grantRole(SUPPLY_CONTROLLER_MANAGER_ROLE, supplyControllerManager);
        _grantRole(TOKEN_CONTRACT_ROLE, tokenAddress);
        for (uint256 i = 0; i < scInitializationConfig.length; ) {
            _addSupplyController(scInitializationConfig[i]);
            unchecked {
                i++;
            }
        }
    }

    /**
     * @dev Adds a new supply controller which can be used to control the supply of a token.
     * Can be called externally by the `SUPPLY_CONTROLLER_MANAGER_ROLE`.
     * @param newSupplyController Address of the new supply controller
     * @param limitCapacity Max amount for the rate limit.
     * @param refillPerSecond Amount to add to limit each second up to the `limitCapacity`
     * @param mintAddressWhitelist Addresses the supply controller can mint to
     * @param allowAnyMintAndBurnAddress If true, allows the supply controller to mint to and burn from any address
     */
    function addSupplyController(
        address newSupplyController,
        uint256 limitCapacity,
        uint256 refillPerSecond,
        address[] memory mintAddressWhitelist,
        bool allowAnyMintAndBurnAddress
    ) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) {
        RateLimit.LimitConfig memory limitConfig = RateLimit.LimitConfig(limitCapacity, refillPerSecond);
        SupplyControllerInitialization memory scInitializationConfig = SupplyControllerInitialization(
            newSupplyController,
            limitConfig,
            mintAddressWhitelist,
            allowAnyMintAndBurnAddress
        );
        _addSupplyController(scInitializationConfig);
    }

    /**
     * @dev Removes `oldSupplyController`
     * @param oldSupplyController The old supply controller address
     */
    function removeSupplyController(
        address oldSupplyController
    ) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(oldSupplyController) {
        _revokeRole(SUPPLY_CONTROLLER_ROLE, oldSupplyController);
        SupplyController storage supplyController = supplyControllerMap[oldSupplyController];
        _removeAddressSet(supplyController.mintAddressWhitelist);
        EnumerableSet.remove(supplyControllerSet, oldSupplyController);
        delete supplyControllerMap[oldSupplyController];
        emit SupplyControllerRemoved(oldSupplyController);
    }

    /**
     * Update limit configuration
     * @param supplyController_ Supply controller address.
     * @param limitCapacity Max amount for the rate limit
     * @param refillPerSecond Amount to add to limit each second up to the `limitCapacity`
     */
    function updateLimitConfig(
        address supplyController_,
        uint256 limitCapacity,
        uint256 refillPerSecond
    ) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(supplyController_) {
        RateLimit.LimitConfig memory limitConfig = RateLimit.LimitConfig(limitCapacity, refillPerSecond);
        SupplyController storage supplyController = supplyControllerMap[supplyController_];
        RateLimit.LimitConfig memory oldLimitConfig = supplyController.rateLimitStorage.limitConfig;
        supplyController.rateLimitStorage.limitConfig = limitConfig;
        emit LimitConfigUpdated(supplyController_, limitConfig, oldLimitConfig);
    }

    function updateAllowAnyMintAndBurnAddress(
        address supplyController_,
        bool allowAnyMintAndBurnAddress
    ) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(supplyController_) {
        SupplyController storage supplyController = supplyControllerMap[supplyController_];
        bool oldAllowValue = supplyController.allowAnyMintAndBurnAddress;
        supplyController.allowAnyMintAndBurnAddress = allowAnyMintAndBurnAddress;
        emit AllowAnyMintAndBurnAddressUpdated(supplyController_, allowAnyMintAndBurnAddress, oldAllowValue);
    }

    /**
     * @dev Adds `mintAddress` to `mintAddressWhitelist` in `supplyController`.
     * @param supplyController_ Supply controller address
     * @param mintAddress Address which can be minted to
     */
    function addMintAddressToWhitelist(
        address supplyController_,
        address mintAddress
    ) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(supplyController_) {
        SupplyController storage supplyController = supplyControllerMap

Tags:
ERC20, ERC165, Multisig, Mintable, Burnable, Pausable, Voting, Upgradeable, Multi-Signature, Factory|addr:0xa18778445a7feb7d13655cace45481770498906d|verified:true|block:23648496|tx:0xa6667ce7a1ada3c1e879a1188b451ba1e931973712508c56e8deba196cce6ac4|first_check:1761333479

Submitted on: 2025-10-24 21:18:00

Comments

Log in to comment.

No comments yet.