creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/Controller.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.19;\r\n\r\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\r\nimport \"@openzeppelin/contracts/proxy/Clones.sol\";\r\nimport \"./interfaces/IOnDemandOracle.sol\";\r\nimport \"./interfaces/IController.sol\";\r\nimport \"./interfaces/IBaseVault.sol\";\r\nimport \"./interfaces/ISynth.sol\";\r\n\r\n\r\ncontract Controller is Ownable, IController {\r\n\r\n  event SetTreasury(address treasury, uint16 treasuryFee);\r\n  event SetControllerParameters(uint16 emergencyPoolShare, uint16 priceBounds, uint16 yieldAdjustmentSpeed);\r\n  event SetAddresses(address _synth, address _savingsPool, address _lz0Endpoint, address _uniswapV3Factory, address _usdc);\r\n  event AddedVault(address vault);\r\n  event CreatedSynth(address newSynth, string name, address oracle);\r\n  event SetLiquidationThresholds(address collateral, uint liquidationThreshold, uint hardLiquidationThreshold);\r\n\r\n  /// @notice Treasury\r\n  address public treasury;\r\n  /// @notice Treasury fee percentage X4\r\n  uint16 public treasuryFee;\r\n  \r\n  /// @notice The emergency pool as target percentage of total deposits: adjust fees up or down (X4)\r\n  uint16 public emergencyPoolShare = 1000;\r\n  \r\n  /// @notice Staking yield bounds beyond which the staking yield is either 0 or 100% (X4)\r\n  uint16 internal priceBounds = 500;\r\n  /// @notice Yield adjustment speed. \r\n  // Eg, yield is 50-50 and price moves to the upper bound (1 synth = $1.05), increase yield by 10% if yieldAdjustmentSpeed == 10%\r\n  uint16 internal yieldAdjustmentSpeed = 1000;\r\n  \r\n  /// @notice Vaults\r\n  address[] public vaults;\r\n  // @notice Collateral to Vault implementation mapping\r\n  mapping(address => address) public collateralToVault;\r\n  \r\n  /// @notice Liquidation thresholds in percent X2 per collateral vault\r\n  mapping(address => uint16) internal liquidationThresholds;\r\n  mapping(address => uint16) internal hardLiquidationThresholds;\r\n  \r\n  \r\n  /// @notice Savings contract logic for clones\r\n  address public savingsPoolLogic;\r\n  /// @notice Synth contract logic for clones\r\n  address public synth;\r\n  /// @notice Synths\r\n  address[] public synths;\r\n  \r\n  /// @notice LayerZero v2 endpoint\r\n  address public lzEndpoint;\r\n  /// @notice Uniswap v3 factory, used to query twap\r\n  address public uniswapV3Factory; // mainnet = 0x1F98431c8aD98523631AE4a59f267346ea31F984;\r\n  /// @notice Stablecoin counterparty used for price checks\r\n  address public usdcAddress;\r\n  /// @notice Stablecoin counterparty used for price checks\r\n  address public twapPriceLib;\r\n\r\n  uint32 internal constant TWAP_DURATION = 3600;\r\n  \r\n  \r\n  function setAddresses(address _synth, address _savingsPool, address _lzEndpoint, address _uniswapV3Factory, address _usdc) public onlyOwner {\r\n    require(\r\n      _lzEndpoint != address(0) \r\n      && _uniswapV3Factory != address(0) \r\n      && _synth != address(0) \r\n      && _savingsPool != address(0) \r\n      && _usdc != address(0) ,\r\n      \"Conf: Invalid Addresses\"\r\n    );\r\n    synth = _synth;\r\n    savingsPoolLogic = _savingsPool;\r\n    lzEndpoint = _lzEndpoint;\r\n    uniswapV3Factory = _uniswapV3Factory;\r\n    usdcAddress = _usdc;\r\n    emit SetAddresses(_synth, _savingsPool, _lzEndpoint, _uniswapV3Factory, _usdc);\r\n  }\r\n  \r\n  /// @notice Get vaults length\r\n  function getVaultsLength() public view returns (uint) {\r\n    return vaults.length;\r\n  }\r\n  \r\n  \r\n  /// @notice Get synths length\r\n  function getSynthsLength() public view returns (uint) {\r\n    return synths.length;\r\n  }\r\n\r\n  \r\n  /// @notice Get liquidation thresholds\r\n  function getLiquidationThresholds(address collateral) public view returns (uint16 liquidationThreshold, uint16 hardLiquidationThreshold){\r\n    return (liquidationThresholds[collateral], hardLiquidationThresholds[collateral]);\r\n  }\r\n  /// @notice Set liquidation thresholds\r\n  function setLiquidationThresholds(address collateral, uint16 _liquidationThreshold, uint16 _hardLiquidationThreshold) public onlyOwner {\r\n    // cant have both levels with same value or will cause DIV_0 issues in vaults\r\n    require(_liquidationThreshold > _hardLiquidationThreshold, \"Ctrl: Invalid Range\");\r\n    require(_liquidationThreshold > 120, \"Ctrl: Invalid Liq Threshold\");\r\n    require(_hardLiquidationThreshold > 110, \"Ctrl: Invalid Hard Threshold\");\r\n    liquidationThresholds[collateral] = _liquidationThreshold;\r\n    hardLiquidationThresholds[collateral] = _hardLiquidationThreshold;\r\n  }\r\n  \r\n  \r\n  /// @notice Set treasury\r\n  function setTreasury(address _treasury, uint16 _treasuryFee) public onlyOwner {\r\n    require(_treasury != address(0), \"Ctrl: Null Address\");\r\n    require(_treasuryFee <= 10000, \"Ctrl: Fee Over 100%\");\r\n    treasury = _treasury;\r\n    treasuryFee = _treasuryFee;\r\n    emit SetTreasury(_treasury, _treasuryFee);\r\n  }\r\n  \r\n  /// @notice Set various parameters\r\n  function setParameters(uint16 _emergencyPoolShare, uint16 _priceBounds, uint16 _yieldAdjustmentSpeed) public onlyOwner {\r\n    require(_emergencyPoolShare < 5000, \"Ctrl: EP share too high\");\r\n    require(_priceBounds < 2000, \"Ctrl: Price bounds too high\");\r\n    require(_yieldAdjustmentSpeed < 5000, \"Ctrl: Adj. Speed too high\");\r\n    emergencyPoolShare = _emergencyPoolShare;\r\n    priceBounds = _priceBounds;\r\n    yieldAdjustmentSpeed = _yieldAdjustmentSpeed;\r\n    emit SetControllerParameters(_emergencyPoolShare, _priceBounds, _yieldAdjustmentSpeed);\r\n  }\r\n  \r\n  \r\n  /// @notice Add a new collateral vault\r\n  function addVault(address vault) public onlyOwner returns (uint) {\r\n    require(vault != address(0), \"Ctrl: Invalid Vault\");\r\n    address collateral = IBaseVault(vault).collateralAsset();\r\n    address oracle = IBaseVault(vault).oracle();\r\n    require(collateral != address(0) && oracle != address(0), \"Ctrl: Invalid Vault\");\r\n    require(collateralToVault[collateral] == address(0), \"Ctrl: Vault Already Exists\");\r\n      \r\n    vaults.push(vault);\r\n    collateralToVault[collateral] = vault;\r\n    // set default liquidation thresholds\r\n    setLiquidationThresholds(collateral, 150, 120);\r\n    emit AddedVault(vault);\r\n    return vaults.length;\r\n  }\r\n  \r\n  \r\n  /// @notice Get UniswapV3 pool parameters: factory address, quote pair, decimals\r\n  function getUniswapParams() public view \r\n    returns (address uniswapFactory, address quoteToken, uint8 tokenDecimals, uint32 twapDuration, uint16 priceBoundsX4, uint16 yieldAdjustmentSpeedX4) \r\n  {\r\n    return (uniswapV3Factory, usdcAddress, 6, TWAP_DURATION, priceBounds, yieldAdjustmentSpeed);\r\n  }\r\n  \r\n  \r\n  /// @notice Create a new synth\r\n  /// @dev oracle == address(0) -> will use fixed value 1e8 for USD peg\r\n  function createSynth(string memory name, address oracle) public onlyOwner returns (address newSynth) {\r\n    if (oracle != address(0)) require(IOnDemandOracle(oracle).latestAnswer() > 0, \"Ctrl: No Such Oracle\");\r\n    string memory _name = string(abi.encodePacked(\"Monroe \", name));\r\n    string memory _symbol = string(abi.encodePacked(\"mon\", name));\r\n    newSynth = Clones.cloneDeterministic(synth, keccak256(abi.encodePacked(\"Monroe \", name)));\r\n    ISynth(newSynth).initialize(_name, _symbol, oracle, lzEndpoint, savingsPoolLogic);\r\n    synths.push(newSynth);\r\n    ISynth(newSynth).transferOwnership(msg.sender);\r\n    emit CreatedSynth(newSynth, name, oracle);\r\n  }\r\n  \r\n}"
      },
      "node_modules/@openzeppelin/contracts/access/Ownable.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _transferOwnership(_msgSender());\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        _checkOwner();\n        _;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if the sender is not the owner.\n     */\n    function _checkOwner() internal view virtual {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby disabling any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
      },
      "node_modules/@openzeppelin/contracts/proxy/Clones.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for\n * deploying minimal proxy contracts, also known as \"clones\".\n *\n * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies\n * > a minimal bytecode implementation that delegates all calls to a known, fixed address.\n *\n * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`\n * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the\n * deterministic method.\n *\n * _Available since v3.4._\n */\nlibrary Clones {\n    /**\n     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n     *\n     * This function uses the create opcode, which should never revert.\n     */\n    function clone(address implementation) internal returns (address instance) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes\n            // of the `implementation` address with the bytecode before the address.\n            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\n            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.\n            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))\n            instance := create(0, 0x09, 0x37)\n        }\n        require(instance != address(0), \"ERC1167: create failed\");\n    }\n\n    /**\n     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.\n     *\n     * This function uses the create2 opcode and a `salt` to deterministically deploy\n     * the clone. Using the same `implementation` and `salt` multiple time will revert, since\n     * the clones cannot be deployed twice at the same address.\n     */\n    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes\n            // of the `implementation` address with the bytecode before the address.\n            mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))\n            // Packs the remaining 17 bytes of `implementation` with the bytecode after the address.\n            mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))\n            instance := create2(0, 0x09, 0x37, salt)\n        }\n        require(instance != address(0), \"ERC1167: create2 failed\");\n    }\n\n    /**\n     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n     */\n    function predictDeterministicAddress(\n        address implementation,\n        bytes32 salt,\n        address deployer\n    ) internal pure returns (address predicted) {\n        /// @solidity memory-safe-assembly\n        assembly {\n            let ptr := mload(0x40)\n            mstore(add(ptr, 0x38), deployer)\n            mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)\n            mstore(add(ptr, 0x14), implementation)\n            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)\n            mstore(add(ptr, 0x58), salt)\n            mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))\n            predicted := keccak256(add(ptr, 0x43), 0x55)\n        }\n    }\n\n    /**\n     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.\n     */\n    function predictDeterministicAddress(\n        address implementation,\n        bytes32 salt\n    ) internal view returns (address predicted) {\n        return predictDeterministicAddress(implementation, salt, address(this));\n    }\n}\n"
      },
      "contracts/interfaces/IOnDemandOracle.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.19;\r\n\r\n/// @notice IOnDemandOracle used for oracles that get their value from offchain signed data\r\n/// https://github.com/MonroeProtocol/OnDemandOracle\r\ninterface IOnDemandOracle {\r\n  /// @notice Get latest oracle answer (may be stale)\r\n  function latestAnswer() external view returns (int256);\r\n  \r\n  /// @notice Update and get latest oracle answer based on signed offchain data\r\n  /// Reverts on bad priceUpdateData\r\n  function updateAnswer(bytes calldata priceUpdateData) external payable returns (int256);\r\n  \r\n  function decimals() external view returns (uint256);\r\n  \r\n  function lastUpdate() external view returns (uint256);\r\n  \r\n  function isStale() external view returns (bool);\r\n}"
      },
      "contracts/interfaces/IController.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.19;\r\n\r\n\r\ninterface IController {\r\n  function getVaultsLength() external view returns (uint);\r\n  function vaults(uint vaultId) external view returns (address);\r\n  function getUniswapParams() external view \r\n    returns (address uniswapFactory, address quoteToken, uint8 tokenDecimals, uint32 twapDuration, uint16 priceBoundsX4, uint16 yieldAdjustmentSpeedX4);\r\n    \r\n  function treasury() external view returns (address);\r\n  function treasuryFee() external view returns (uint16);\r\n  function emergencyPoolShare() external view returns (uint16);\r\n  function setTreasury(address _treasury, uint16 _treasuryFee) external;\r\n  function setParameters(uint16 _emergencyPoolShare, uint16 _priceBounds, uint16 _yieldAdjustmentSpeed) external;\r\n  function setAddresses(address _synth, address _savingsPool, address _lzEndpoint, address _uniswapV3Factory, address _usdc) external;\r\n  function lzEndpoint() external returns (address);\r\n  \r\n  function getLiquidationThresholds(address collateral) external view returns (uint16 liquidationThreshold, uint16 hardLiquidationThreshold);\r\n  function setLiquidationThresholds(address collateral, uint16 _liquidationThreshold, uint16 _hardLiquidationThreshold) external;\r\n}"
      },
      "contracts/interfaces/IBaseVault.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.19;\r\n\r\n\r\ninterface IBaseVault {\r\n  function depositAndMint(uint256 collateralAmount, uint256 burnAmount, bytes calldata priceUpdateData) external payable;\r\n  function withdrawAndBurn(uint256 collateralAmount, uint256 burnAmount, bytes calldata priceUpdateData) external payable;\r\n  function balanceOf(address owner) external view returns (uint);\r\n  function initialize(address _synth, address _oracle, address _collateralAsset, address _controller, string memory symbol) external;\r\n  function collateralAsset() external pure returns (address);\r\n  function oracle() external view returns (address);\r\n  function updatePrice(bytes calldata priceUpdateData) external payable returns (uint price);\r\n  function latestPrice() external view returns (uint price);\r\n}"
      },
      "contracts/interfaces/ISynth.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.19;\r\n\r\n\r\ninterface ISynth {\r\n  function initialize(string memory name, string memory symbol, address _oracle, address lz0Endpoint, address _savingPoolLogic) external;\r\n  function transferOwnership(address newOwner) external;\r\n  function mint(address _recipient, uint256 _amount) external;\r\n  function burn(address _recipient, uint256 _amount) external;\r\n  function getPrice() external view returns (uint);\r\n  function getSavingsYield() external returns (uint);\r\n  function collectSavingsIncome(address asset, uint amount) external;\r\n}"
      },
      "node_modules/@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
      }
    },
    "settings": {
      "remappings": [
        "@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
        "@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
        "@uniswap/v3-periphery/=node_modules/@uniswap/v3-periphery/",
        "ds-test/=lib/forge-std/lib/ds-test/src/",
        "forge-std/=lib/forge-std/src/",
        "layerzero/=node_modules/@layerzerolabs/solidity-examples/contracts/"
      ],
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "metadata": {
        "useLiteralContent": false,
        "bytecodeHash": "ipfs",
        "appendCBOR": true
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "evmVersion": "paris",
      "viaIR": false,
      "libraries": {}
    }
  }}