{"file_path":"contracts/msd/MSDControllerV2.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\n\nimport \"@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol\";\nimport \"@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol\";\n\nimport \"./MSD.sol\";\n\n/**\n * @dev Interface for Minters, minters now can be iMSD and MSDS\n */\ninterface IMinter {\n    function totalMint() external returns (uint256);\n}\n\n/**\n * @title dForce's Multi-currency Stable Debt Token Controller\n * @author dForce\n */\n\ncontract MSDControllerV2 is Initializable, Ownable {\n    using SafeMathUpgradeable for uint256;\n    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;\n\n    /// @dev EnumerableSet of all msdTokens\n    EnumerableSetUpgradeable.AddressSet internal msdTokens;\n\n    // @notice Mapping of msd tokens to corresponding minters\n    mapping(address => EnumerableSetUpgradeable.AddressSet) internal msdMinters;\n\n    struct TokenData {\n        // System earning from borrow interest\n        uint256 earning;\n        // System debt from saving interest\n        uint256 debt;\n    }\n\n    // @notice Mapping of msd tokens to corresponding TokenData\n    mapping(address => TokenData) public msdTokenData;\n\n    // @notice Mapping of msd minters to corresponding msd token mintage cap\n    // @dev Each minter now typically iMSD can only mint 1 msd token\n    /*\n     *  The mint cap of the msd minter, will be checked in mintMSD()\n     *  -1 means there is no limit on the cap\n     *  0 means the msd token can not be mint any more\n     */\n    mapping(address => mapping(address => uint256)) public mintCaps;\n\n    /**\n     * @dev Emitted when `token` is added into msdTokens.\n     */\n    event MSDAdded(address token);\n\n    /**\n     * @dev Emitted when `minter` is added into `tokens`'s minters.\n     */\n    event MinterAdded(address token, address minter);\n\n    /**\n     * @dev Emitted when `minter` is removed from `tokens`'s minters.\n     */\n    event MinterRemoved(address token, address minter);\n\n    /**\n     * @dev Emitted when reserve is withdrawn from `token`.\n     */\n    event ReservesWithdrawn(\n        address owner,\n        address token,\n        uint256 amount,\n        uint256 oldTotalReserves,\n        uint256 newTotalReserves\n    );\n\n    /// @dev Emitted when msd token minter's mint cap is changed\n    event NewMintCap(\n        address token,\n        address minter,\n        uint256 oldMintCap,\n        uint256 newMintCap\n    );\n\n    /**\n     * @notice Expects to call only once to initialize the MSD controller.\n     */\n    function initialize() external initializer {\n        __Ownable_init();\n    }\n\n    /**\n     * @notice Ensure this is a MSD Controller contract.\n     */\n    function isMSDController() external pure returns (bool) {\n        return true;\n    }\n\n    /**\n     * @dev Throws if token is not in msdTokens\n     */\n    function _checkMSD(address _token) internal view {\n        require(hasMSD(_token), \"token is not a valid MSD token\");\n    }\n\n    /**\n     * @dev Throws if token is not a valid MSD token.\n     */\n    modifier onlyMSD(address _token) {\n        _checkMSD(_token);\n        _;\n    }\n\n    /**\n     * @dev Throws if called by any account other than the _token's minters.\n     */\n    modifier onlyMSDMinter(address _token, address caller) {\n        _checkMSD(_token);\n\n        require(\n            msdMinters[_token].contains(caller),\n            \"onlyMinter: caller is not the token's minter\"\n        );\n\n        _;\n    }\n\n    function _addMSDInternal(address _token) internal {\n        require(_token != address(0), \"MSD token cannot be a zero address\");\n        if (msdTokens.add(_token)) {\n            emit MSDAdded(_token);\n        }\n    }\n\n    function _addMinterInternal(address _token, address _minter) internal {\n        require(_minter != address(0), \"minter cannot be a zero address\");\n\n        if (msdMinters[_token].add(_minter)) {\n            emit MinterAdded(_token, _minter);\n        }\n    }\n\n    function _removeMinterInternal(address _token, address _minter) internal {\n        require(_minter != address(0), \"minter cannot be a zero address\");\n\n        if (msdMinters[_token].remove(_minter)) {\n            emit MinterRemoved(_token, _minter);\n        }\n    }\n\n    function _setMintCapInternal(\n        address _token,\n        address _minter,\n        uint256 _newMintCap\n    ) internal {\n        uint256 oldMintCap = mintCaps[_token][_minter];\n\n        if (oldMintCap != _newMintCap) {\n            mintCaps[_token][_minter] = _newMintCap;\n            emit NewMintCap(_token, _minter, oldMintCap, _newMintCap);\n        }\n    }\n\n    function _addMintersInternal(\n        address _token,\n        address[] calldata _minters,\n        uint256[] calldata _mintCaps\n    ) internal {\n        require(\n            _minters.length == _mintCaps.length,\n            \"Length of _minters and _mintCaps mismatch\"\n        );\n\n        uint256 _len = _minters.length;\n        for (uint256 i = 0; i < _len; i++) {\n            _addMinterInternal(_token, _minters[i]);\n            _setMintCapInternal(_token, _minters[i], _mintCaps[i]);\n        }\n    }\n\n    /**\n     * @notice Add `_token` into msdTokens, and add `_minters` into minters along with `_mintCaps`.\n     * If `_token` have not been in msdTokens, emits a `MSDTokenAdded` event.\n     * If _minter in `_minters` have not been in msd Token's minters, emits a `MinterAdded` event.\n     * If cap in `_mintCaps` has changed, emits a `NewMintCap` event.\n     *\n     * @param _token The msd token to add\n     * @param _minters The addresses to add as msd token's minters\n     * @param _mintCaps The mint caps to set for minters\n     *\n     * Requirements:\n     * - the caller must be `owner`.\n     */\n    function _addMSD(\n        address _token,\n        address[] calldata _minters,\n        uint256[] calldata _mintCaps\n    ) external onlyOwner {\n        _addMSDInternal(_token);\n        _addMintersInternal(_token, _minters, _mintCaps);\n    }\n\n    /**\n     * @notice Add `_minters` into minters along with `_mintCaps`.\n     * If _minter in `_minters` have not been in msd Token's minters, emits a `MinterAdded` event.\n     * If cap in `_mintCaps` has changed, emits a `NewMintCap` event.\n     *\n     * @param _token The msd token to add minters\n     * @param _minters The addresses to add as msd token's minters\n     * @param _mintCaps The mint caps to set for minters\n     *\n     * Requirements:\n     * - the caller must be `owner`.\n     */\n    function _addMinters(\n        address _token,\n        address[] calldata _minters,\n        uint256[] calldata _mintCaps\n    ) external onlyOwner onlyMSD(_token) {\n        _addMintersInternal(_token, _minters, _mintCaps);\n    }\n\n    /**\n     * @notice Remove `minters` from minters and reset mint cap to 0.\n     * If `minter` is minters, emits a `MinterRemoved` event.\n     *\n     * @param _minters The minters to remove\n     *\n     * Requirements:\n     * - the caller must be `owner`, `_token` must be a MSD Token.\n     */\n    function _removeMinters(address _token, address[] calldata _minters)\n        external\n        onlyOwner\n        onlyMSD(_token)\n    {\n        uint256 _len = _minters.length;\n        for (uint256 i = 0; i < _len; i++) {\n            _removeMinterInternal(_token, _minters[i]);\n            _setMintCapInternal(_token, _minters[i], 0);\n        }\n    }\n\n    /**\n     * @notice set `_mintCaps` for `_token`'s `_minters`, emits a `NewMintCap` event.\n     *\n     * @param _token The msd token to set\n     * @param _minters The msd token's minters' addresses to set\n     * @param _mintCaps The mint caps to set for minters\n     *\n     * Requirements:\n     * - the caller must be `owner`.\n     */\n    function _setMintCaps(\n        address _token,\n        address[] calldata _minters,\n        uint256[] calldata _mintCaps\n    ) external onlyOwner onlyMSD(_token) {\n        require(\n            _minters.length == _mintCaps.length,\n            \"Length of _minters and _mintCaps mismatch\"\n        );\n\n        uint256 _len = _minters.length;\n        for (uint256 i = 0; i < _len; i++) {\n            require(\n                msdMinters[_token].contains(_minters[i]),\n                \"minter is not the token's minter\"\n            );\n            _setMintCapInternal(_token, _minters[i], _mintCaps[i]);\n        }\n    }\n\n    /**\n     * @notice Withdraw the reserve of `_token`.\n     * @param _token The MSD token to withdraw\n     * @param _amount The amount of token to withdraw\n     *\n     * Requirements:\n     * - the caller must be `owner`, `_token` must be a MSD Token.\n     */\n    function _withdrawReserves(address _token, uint256 _amount)\n        external\n        onlyOwner\n        onlyMSD(_token)\n    {\n        (uint256 _equity, ) = calcEquity(_token);\n\n        require(_equity >= _amount, \"Token do not have enough reserve\");\n\n        // Increase the token debt\n        msdTokenData[_token].debt = msdTokenData[_token].debt.add(_amount);\n\n        // Directly mint the token to owner\n        MSD(_token).mint(owner, _amount);\n\n        emit ReservesWithdrawn(\n            owner,\n            _token,\n            _amount,\n            _equity,\n            _equity.sub(_amount)\n        );\n    }\n\n    /**\n     * @notice Mint `amount` of `_token` to `_to`.\n     * @param _token The MSD token to mint\n     * @param _to The account to mint to\n     * @param _amount The amount of token to mint\n     *\n     * Requirements:\n     * - the caller must be `minter` of `_token`.\n     */\n    function mintMSD(\n        address _token,\n        address _to,\n        uint256 _amount\n    ) external onlyMSDMinter(_token, msg.sender) {\n        _beforeMint(_token, msg.sender, _to, _amount);\n\n        MSD(_token).mint(_to, _amount);\n    }\n\n    function _beforeMint(\n        address _token,\n        address _minter,\n        address _to,\n        uint256 _amount\n    ) internal virtual {\n        // Check minter's mint cap, -1 means no limit\n        // _amount has been taken account into totalMint/totalBorrows\n        require(\n            IMinter(_minter).totalMint() <= mintCaps[_token][_minter],\n            \"Minter mint capacity reached\"\n        );\n\n        _to;\n        _amount;\n    }\n\n    /*********************************/\n    /******** MSD Token Equity *******/\n    /*********************************/\n\n    /**\n     * @notice Get the MSD token equity\n     * @param _token The MSD token to query\n     * @return token equity, token debt, will call `updateInterest()` on its minters\n     *\n     * Requirements:\n     * - `_token` must be a MSD Token.\n     *\n     */\n    function calcEquity(address _token)\n        public\n        view\n        onlyMSD(_token)\n        returns (uint256, uint256)\n    {\n        TokenData storage _tokenData = msdTokenData[_token];\n\n        return\n            _tokenData.earning > _tokenData.debt\n                ? (_tokenData.earning.sub(_tokenData.debt), uint256(0))\n                : (uint256(0), _tokenData.debt.sub(_tokenData.earning));\n    }\n\n    /*********************************/\n    /****** General Information ******/\n    /*********************************/\n\n    /**\n     * @notice Return all of the MSD tokens\n     * @return _allMSDs The list of MSD token addresses\n     */\n    function getAllMSDs() public view returns (address[] memory _allMSDs) {\n        EnumerableSetUpgradeable.AddressSet storage _msdTokens = msdTokens;\n\n        uint256 _len = _msdTokens.length();\n        _allMSDs = new address[](_len);\n        for (uint256 i = 0; i < _len; i++) {\n            _allMSDs[i] = _msdTokens.at(i);\n        }\n    }\n\n    /**\n     * @notice Check whether a address is a valid MSD\n     * @param _token The token address to check for\n     * @return true if the _token is a valid MSD otherwise false\n     */\n    function hasMSD(address _token) public view returns (bool) {\n        return msdTokens.contains(_token);\n    }\n\n    /**\n     * @notice Return all minter of a MSD token\n     * @param _token The MSD token address to get minters for\n     * @return _minters The list of MSD token minter addresses\n     * Will retuen empty if `_token` is not a valid MSD token\n     */\n    function getMSDMinters(address _token)\n        public\n        view\n        returns (address[] memory _minters)\n    {\n        EnumerableSetUpgradeable.AddressSet storage _msdMinters =\n            msdMinters[_token];\n\n        uint256 _len = _msdMinters.length();\n        _minters = new address[](_len);\n        for (uint256 i = 0; i < _len; i++) {\n            _minters[i] = _msdMinters.at(i);\n        }\n    }\n}\n","deployed_bytecode":"0x608060405234801561001057600080fd5b50600436106101165760003560e01c80638da5cb5b116100a2578063d2de243411610071578063d2de243414610572578063e30c397814610598578063fb43d68d146105a0578063fc4d33f9146105a8578063fc662774146105b057610116565b80638da5cb5b1461041a578063b1e1436f1461043e578063b5add45c14610464578063bfeb5c1c146104a457610116565b80636e96dfd7116100e95780636e96dfd7146103475780637431fc381461036d5780638129fc1c146103a757806386c975e0146103af5780638af225ae146103ee57610116565b8063014db0011461011b578063494c6e88146101eb57806352be45af146102435780636e839b0b14610311575b600080fd5b6101e96004803603606081101561013157600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561015b57600080fd5b82018360208201111561016d57600080fd5b803590602001918460208302840111600160201b8311171561018e57600080fd5b919390929091602081019035600160201b8111156101ab57600080fd5b8201836020820111156101bd57600080fd5b803590602001918460208302840111600160201b831117156101de57600080fd5b50909250905061062e565b005b6101f36107a5565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561022f578181015183820152602001610217565b505050509050019250505060405180910390f35b6101e96004803603606081101561025957600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561028357600080fd5b82018360208201111561029557600080fd5b803590602001918460208302840111600160201b831117156102b657600080fd5b919390929091602081019035600160201b8111156102d357600080fd5b8201836020820111156102e557600080fd5b803590602001918460208302840111600160201b8311171561030657600080fd5b509092509050610841565b6101e96004803603606081101561032757600080fd5b506001600160a01b038135811691602081013590911690604001356108ac565b6101e96004803603602081101561035d57600080fd5b50356001600160a01b0316610996565b6103936004803603602081101561038357600080fd5b50356001600160a01b0316610a97565b604080519115158252519081900360200190f35b6101e9610aaa565b6103d5600480360360208110156103c557600080fd5b50356001600160a01b0316610b03565b6040805192835260208301919091528051918290030190f35b6101e96004803603604081101561040457600080fd5b506001600160a01b038135169060200135610b6b565b610422610d4b565b604080516001600160a01b039092168252519081900360200190f35b6103d56004803603602081101561045457600080fd5b50356001600160a01b0316610d5f565b6104926004803603604081101561047a57600080fd5b506001600160a01b0381358116916020013516610d78565b60408051918252519081900360200190f35b6101e9600480360360608110156104ba57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156104e457600080fd5b8201836020820111156104f657600080fd5b803590602001918460208302840111600160201b8311171561051757600080fd5b919390929091602081019035600160201b81111561053457600080fd5b82018360208201111561054657600080fd5b803590602001918460208302840111600160201b8311171561056757600080fd5b509092509050610d95565b6101f36004803603602081101561058857600080fd5b50356001600160a01b0316610e02565b610422610eb5565b610393610ec4565b6101e9610ec9565b6101e9600480360360408110156105c657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156105f057600080fd5b82018360208201111561060257600080fd5b803590602001918460208302840111600160201b8311171561062357600080fd5b509092509050610fc0565b60005461010090046001600160a01b0316331461067c5760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b8461068681611078565b8382146106c45760405162461bcd60e51b81526004018080602001828103825260298152602001806119c86029913960400191505060405180910390fd5b8360005b8181101561079b5761070a8787838181106106df57fe5b6001600160a01b038c81166000908152603660209081526040909120949202909101351690506110d5565b61075b576040805162461bcd60e51b815260206004820181905260248201527f6d696e746572206973206e6f742074686520746f6b656e2773206d696e746572604482015290519081900360640190fd5b6107938888888481811061076b57fe5b905060200201356001600160a01b031687878581811061078757fe5b905060200201356110f1565b6001016106c8565b5050505050505050565b6060603460006107b482611195565b90508067ffffffffffffffff811180156107cd57600080fd5b506040519080825280602002602001820160405280156107f7578160200160208202803683370190505b50925060005b8181101561083b5761080f83826111a0565b84828151811061081b57fe5b6001600160a01b03909216602092830291909101909101526001016107fd565b50505090565b60005461010090046001600160a01b0316331461088f5760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b610898856111ac565b6108a58585858585611240565b5050505050565b82336108b782611078565b6001600160a01b03821660009081526036602052604090206108d990826110d5565b6109145760405162461bcd60e51b815260040180806020018281038252602c815260200180611905602c913960400191505060405180910390fd5b610920853386866112ec565b846001600160a01b03166340c10f1985856040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561097757600080fd5b505af115801561098b573d6000803e3d6000fd5b505050505050505050565b60005461010090046001600160a01b031633146109e45760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b6001600160a01b03811615801590610a0a57506001546001600160a01b03828116911614155b610a455760405162461bcd60e51b815260040180806020018281038252604b8152602001806118ba604b913960600191505060405180910390fd5b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b90600090a35050565b6000610aa46034836110d5565b92915050565b60005460ff1615610aec5760405162461bcd60e51b815260040180806020018281038252602e815260200180611953602e913960400191505060405180910390fd5b610af46113cb565b6000805460ff19166001179055565b60008082610b1081611078565b6001600160a01b03841660009081526037602052604090206001810154815411610b4d5780546001820154600091610b489190611413565b610b60565b60018101548154610b5d91611413565b60005b935093505050915091565b60005461010090046001600160a01b03163314610bb95760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b81610bc381611078565b6000610bce84610b03565b50905082811015610c26576040805162461bcd60e51b815260206004820181905260248201527f546f6b656e20646f206e6f74206861766520656e6f7567682072657365727665604482015290519081900360640190fd5b6001600160a01b038416600090815260376020526040902060010154610c4c9084611455565b6001600160a01b0380861660008181526037602052604080822060010194909455805484516340c10f1960e01b8152610100909104909316600484015260248301879052925190926340c10f19926044808201939182900301818387803b158015610cb657600080fd5b505af1158015610cca573d6000803e3d6000fd5b50506000547fbb3007a1d7ecb62ce86e6c72f7b9d6a1455956365b1120e2f24f8d6ea69e56f0925061010090046001600160a01b03169050858584610d0f8183611413565b604080516001600160a01b039687168152949095166020850152838501929092526060830152608082015290519081900360a00190a150505050565b60005461010090046001600160a01b031681565b6037602052600090815260409020805460019091015482565b603860209081526000928352604080842090915290825290205481565b60005461010090046001600160a01b03163314610de35760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b84610ded81611078565b610dfa8686868686611240565b505050505050565b6001600160a01b0381166000908152603660205260408120606091610e2682611195565b90508067ffffffffffffffff81118015610e3f57600080fd5b50604051908082528060200260200182016040528015610e69578160200160208202803683370190505b50925060005b81811015610ead57610e8183826111a0565b848281518110610e8d57fe5b6001600160a01b0390921660209283029190910190910152600101610e6f565b505050919050565b6001546001600160a01b031681565b600190565b6001546001600160a01b03163314610f125760405162461bcd60e51b81526004018080602001828103825260258152602001806119a36025913960400191505060405180910390fd5b60008054600180546001600160a01b03818116610100818102610100600160a81b03198716178088556001600160a01b031990941690945560405194849004821695909493909204169184917f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236491a36001546040516001600160a01b03918216918316907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b90600090a35050565b60005461010090046001600160a01b0316331461100e5760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b8261101881611078565b8160005b81811015610dfa576110498686868481811061103457fe5b905060200201356001600160a01b03166114af565b6110708686868481811061105957fe5b905060200201356001600160a01b031660006110f1565b60010161101c565b61108181610a97565b6110d2576040805162461bcd60e51b815260206004820152601e60248201527f746f6b656e206973206e6f7420612076616c6964204d534420746f6b656e0000604482015290519081900360640190fd5b50565b60006110ea836001600160a01b03841661157b565b9392505050565b6001600160a01b0380841660009081526038602090815260408083209386168352929052205481811461118f576001600160a01b0380851660008181526038602090815260408083209488168084529482529182902086905581519283528201929092528082018390526060810184905290517f1e3f360c914ab8be8439fdda7a6e955753721d1bcb60a6b206d6678e6fd5b8e39181900360800190a15b50505050565b6000610aa482611593565b60006110ea8383611597565b6001600160a01b0381166111f15760405162461bcd60e51b81526004018080602001828103825260228152602001806119316022913960400191505060405180910390fd5b6111fc6034826115fb565b156110d257604080516001600160a01b038316815290517fcb1a9e6e24c5ee692ea7080e45f4c71c5cc2a6f1bcea90b6b291651ed0facdde9181900360200190a150565b82811461127e5760405162461bcd60e51b81526004018080602001828103825260298152602001806119c86029913960400191505060405180910390fd5b8260005b818110156112e3576112af8787878481811061129a57fe5b905060200201356001600160a01b0316611610565b6112db878787848181106112bf57fe5b905060200201356001600160a01b031686868581811061078757fe5b600101611282565b50505050505050565b6001600160a01b038085166000908152603860209081526040808320938716808452938252808320548151632cd3b8ad60e11b815291519094936359a7715a93600480850194919392918390030190829087803b15801561134c57600080fd5b505af1158015611360573d6000803e3d6000fd5b505050506040513d602081101561137657600080fd5b5051111561118f576040805162461bcd60e51b815260206004820152601c60248201527f4d696e746572206d696e74206361706163697479207265616368656400000000604482015290519081900360640190fd5b60008054610100600160a81b0319163361010081029190911782556040519091907f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b2364908290a3565b60006110ea83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506116db565b6000828201838110156110ea576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b03811661150a576040805162461bcd60e51b815260206004820152601f60248201527f6d696e7465722063616e6e6f742062652061207a65726f206164647265737300604482015290519081900360640190fd5b6001600160a01b038216600090815260366020526040902061152c9082611772565b1561157757604080516001600160a01b0380851682528316602082015281517f4b5ef9a786cf64a7d82ebcf2d5132667edc9faef4ac36260d9a9e52c526b6232929181900390910190a15b5050565b60009081526001919091016020526040902054151590565b5490565b815460009082106115d95760405162461bcd60e51b81526004018080602001828103825260228152602001806118986022913960400191505060405180910390fd5b8260000182815481106115e857fe5b9060005260206000200154905092915050565b60006110ea836001600160a01b038416611787565b6001600160a01b03811661166b576040805162461bcd60e51b815260206004820152601f60248201527f6d696e7465722063616e6e6f742062652061207a65726f206164647265737300604482015290519081900360640190fd5b6001600160a01b038216600090815260366020526040902061168d90826115fb565b1561157757604080516001600160a01b0380851682528316602082015281517f3c091dafb1d99e4a4c333024492eac3b2cd8bf921a3dd547c937db33be307bb8929181900390910190a15050565b6000818484111561176a5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561172f578181015183820152602001611717565b50505050905090810190601f16801561175c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b60006110ea836001600160a01b0384166117d1565b6000611793838361157b565b6117c957508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aa4565b506000610aa4565b6000818152600183016020526040812054801561188d578354600019808301919081019060009087908390811061180457fe5b906000526020600020015490508087600001848154811061182157fe5b60009182526020808320909101929092558281526001898101909252604090209084019055865487908061185157fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610aa4565b6000915050610aa456fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e64735f73657450656e64696e674f776e65723a204e6577206f77656e722063616e206e6f74206265207a65726f206164647265737320616e64206f776e657220686173206265656e20736574216f6e6c794d696e7465723a2063616c6c6572206973206e6f742074686520746f6b656e2773206d696e7465724d534420746f6b656e2063616e6e6f742062652061207a65726f2061646472657373496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a65646f6e6c794f776e65723a2063616c6c6572206973206e6f7420746865206f776e65725f6163636570744f776e65723a204f6e6c7920666f722070656e64696e67206f776e6572214c656e677468206f66205f6d696e7465727320616e64205f6d696e7443617073206d69736d61746368a2646970667358221220c55c5eb71f824ddf9dc75b921743c0aa725bf200379b3ad2b2c32fbe5fb9346d64736f6c634300060c0033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"github_repository_metadata":null,"compiler_settings":{"libraries":{},"optimizer":{"enabled":true,"runs":200},"outputSelection":{"*":{"*":["evm.bytecode","evm.deployedBytecode","abi"]}}},"optimization_runs":200,"sourcify_repo_url":null,"decoded_constructor_args":null,"compiler_version":"v0.6.12+commit.27d51765","is_verified_via_verifier_alliance":true,"verified_at":"2026-07-22T09:29:31.418852Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x608060405234801561001057600080fd5b50611a26806100206000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c80638da5cb5b116100a2578063d2de243411610071578063d2de243414610572578063e30c397814610598578063fb43d68d146105a0578063fc4d33f9146105a8578063fc662774146105b057610116565b80638da5cb5b1461041a578063b1e1436f1461043e578063b5add45c14610464578063bfeb5c1c146104a457610116565b80636e96dfd7116100e95780636e96dfd7146103475780637431fc381461036d5780638129fc1c146103a757806386c975e0146103af5780638af225ae146103ee57610116565b8063014db0011461011b578063494c6e88146101eb57806352be45af146102435780636e839b0b14610311575b600080fd5b6101e96004803603606081101561013157600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561015b57600080fd5b82018360208201111561016d57600080fd5b803590602001918460208302840111600160201b8311171561018e57600080fd5b919390929091602081019035600160201b8111156101ab57600080fd5b8201836020820111156101bd57600080fd5b803590602001918460208302840111600160201b831117156101de57600080fd5b50909250905061062e565b005b6101f36107a5565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561022f578181015183820152602001610217565b505050509050019250505060405180910390f35b6101e96004803603606081101561025957600080fd5b6001600160a01b038235169190810190604081016020820135600160201b81111561028357600080fd5b82018360208201111561029557600080fd5b803590602001918460208302840111600160201b831117156102b657600080fd5b919390929091602081019035600160201b8111156102d357600080fd5b8201836020820111156102e557600080fd5b803590602001918460208302840111600160201b8311171561030657600080fd5b509092509050610841565b6101e96004803603606081101561032757600080fd5b506001600160a01b038135811691602081013590911690604001356108ac565b6101e96004803603602081101561035d57600080fd5b50356001600160a01b0316610996565b6103936004803603602081101561038357600080fd5b50356001600160a01b0316610a97565b604080519115158252519081900360200190f35b6101e9610aaa565b6103d5600480360360208110156103c557600080fd5b50356001600160a01b0316610b03565b6040805192835260208301919091528051918290030190f35b6101e96004803603604081101561040457600080fd5b506001600160a01b038135169060200135610b6b565b610422610d4b565b604080516001600160a01b039092168252519081900360200190f35b6103d56004803603602081101561045457600080fd5b50356001600160a01b0316610d5f565b6104926004803603604081101561047a57600080fd5b506001600160a01b0381358116916020013516610d78565b60408051918252519081900360200190f35b6101e9600480360360608110156104ba57600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156104e457600080fd5b8201836020820111156104f657600080fd5b803590602001918460208302840111600160201b8311171561051757600080fd5b919390929091602081019035600160201b81111561053457600080fd5b82018360208201111561054657600080fd5b803590602001918460208302840111600160201b8311171561056757600080fd5b509092509050610d95565b6101f36004803603602081101561058857600080fd5b50356001600160a01b0316610e02565b610422610eb5565b610393610ec4565b6101e9610ec9565b6101e9600480360360408110156105c657600080fd5b6001600160a01b038235169190810190604081016020820135600160201b8111156105f057600080fd5b82018360208201111561060257600080fd5b803590602001918460208302840111600160201b8311171561062357600080fd5b509092509050610fc0565b60005461010090046001600160a01b0316331461067c5760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b8461068681611078565b8382146106c45760405162461bcd60e51b81526004018080602001828103825260298152602001806119c86029913960400191505060405180910390fd5b8360005b8181101561079b5761070a8787838181106106df57fe5b6001600160a01b038c81166000908152603660209081526040909120949202909101351690506110d5565b61075b576040805162461bcd60e51b815260206004820181905260248201527f6d696e746572206973206e6f742074686520746f6b656e2773206d696e746572604482015290519081900360640190fd5b6107938888888481811061076b57fe5b905060200201356001600160a01b031687878581811061078757fe5b905060200201356110f1565b6001016106c8565b5050505050505050565b6060603460006107b482611195565b90508067ffffffffffffffff811180156107cd57600080fd5b506040519080825280602002602001820160405280156107f7578160200160208202803683370190505b50925060005b8181101561083b5761080f83826111a0565b84828151811061081b57fe5b6001600160a01b03909216602092830291909101909101526001016107fd565b50505090565b60005461010090046001600160a01b0316331461088f5760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b610898856111ac565b6108a58585858585611240565b5050505050565b82336108b782611078565b6001600160a01b03821660009081526036602052604090206108d990826110d5565b6109145760405162461bcd60e51b815260040180806020018281038252602c815260200180611905602c913960400191505060405180910390fd5b610920853386866112ec565b846001600160a01b03166340c10f1985856040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561097757600080fd5b505af115801561098b573d6000803e3d6000fd5b505050505050505050565b60005461010090046001600160a01b031633146109e45760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b6001600160a01b03811615801590610a0a57506001546001600160a01b03828116911614155b610a455760405162461bcd60e51b815260040180806020018281038252604b8152602001806118ba604b913960600191505060405180910390fd5b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b90600090a35050565b6000610aa46034836110d5565b92915050565b60005460ff1615610aec5760405162461bcd60e51b815260040180806020018281038252602e815260200180611953602e913960400191505060405180910390fd5b610af46113cb565b6000805460ff19166001179055565b60008082610b1081611078565b6001600160a01b03841660009081526037602052604090206001810154815411610b4d5780546001820154600091610b489190611413565b610b60565b60018101548154610b5d91611413565b60005b935093505050915091565b60005461010090046001600160a01b03163314610bb95760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b81610bc381611078565b6000610bce84610b03565b50905082811015610c26576040805162461bcd60e51b815260206004820181905260248201527f546f6b656e20646f206e6f74206861766520656e6f7567682072657365727665604482015290519081900360640190fd5b6001600160a01b038416600090815260376020526040902060010154610c4c9084611455565b6001600160a01b0380861660008181526037602052604080822060010194909455805484516340c10f1960e01b8152610100909104909316600484015260248301879052925190926340c10f19926044808201939182900301818387803b158015610cb657600080fd5b505af1158015610cca573d6000803e3d6000fd5b50506000547fbb3007a1d7ecb62ce86e6c72f7b9d6a1455956365b1120e2f24f8d6ea69e56f0925061010090046001600160a01b03169050858584610d0f8183611413565b604080516001600160a01b039687168152949095166020850152838501929092526060830152608082015290519081900360a00190a150505050565b60005461010090046001600160a01b031681565b6037602052600090815260409020805460019091015482565b603860209081526000928352604080842090915290825290205481565b60005461010090046001600160a01b03163314610de35760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b84610ded81611078565b610dfa8686868686611240565b505050505050565b6001600160a01b0381166000908152603660205260408120606091610e2682611195565b90508067ffffffffffffffff81118015610e3f57600080fd5b50604051908082528060200260200182016040528015610e69578160200160208202803683370190505b50925060005b81811015610ead57610e8183826111a0565b848281518110610e8d57fe5b6001600160a01b0390921660209283029190910190910152600101610e6f565b505050919050565b6001546001600160a01b031681565b600190565b6001546001600160a01b03163314610f125760405162461bcd60e51b81526004018080602001828103825260258152602001806119a36025913960400191505060405180910390fd5b60008054600180546001600160a01b03818116610100818102610100600160a81b03198716178088556001600160a01b031990941690945560405194849004821695909493909204169184917f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236491a36001546040516001600160a01b03918216918316907fb3d55174552271a4f1aaf36b72f50381e892171636b3fb5447fe00e995e7a37b90600090a35050565b60005461010090046001600160a01b0316331461100e5760405162461bcd60e51b81526004018080602001828103825260228152602001806119816022913960400191505060405180910390fd5b8261101881611078565b8160005b81811015610dfa576110498686868481811061103457fe5b905060200201356001600160a01b03166114af565b6110708686868481811061105957fe5b905060200201356001600160a01b031660006110f1565b60010161101c565b61108181610a97565b6110d2576040805162461bcd60e51b815260206004820152601e60248201527f746f6b656e206973206e6f7420612076616c6964204d534420746f6b656e0000604482015290519081900360640190fd5b50565b60006110ea836001600160a01b03841661157b565b9392505050565b6001600160a01b0380841660009081526038602090815260408083209386168352929052205481811461118f576001600160a01b0380851660008181526038602090815260408083209488168084529482529182902086905581519283528201929092528082018390526060810184905290517f1e3f360c914ab8be8439fdda7a6e955753721d1bcb60a6b206d6678e6fd5b8e39181900360800190a15b50505050565b6000610aa482611593565b60006110ea8383611597565b6001600160a01b0381166111f15760405162461bcd60e51b81526004018080602001828103825260228152602001806119316022913960400191505060405180910390fd5b6111fc6034826115fb565b156110d257604080516001600160a01b038316815290517fcb1a9e6e24c5ee692ea7080e45f4c71c5cc2a6f1bcea90b6b291651ed0facdde9181900360200190a150565b82811461127e5760405162461bcd60e51b81526004018080602001828103825260298152602001806119c86029913960400191505060405180910390fd5b8260005b818110156112e3576112af8787878481811061129a57fe5b905060200201356001600160a01b0316611610565b6112db878787848181106112bf57fe5b905060200201356001600160a01b031686868581811061078757fe5b600101611282565b50505050505050565b6001600160a01b038085166000908152603860209081526040808320938716808452938252808320548151632cd3b8ad60e11b815291519094936359a7715a93600480850194919392918390030190829087803b15801561134c57600080fd5b505af1158015611360573d6000803e3d6000fd5b505050506040513d602081101561137657600080fd5b5051111561118f576040805162461bcd60e51b815260206004820152601c60248201527f4d696e746572206d696e74206361706163697479207265616368656400000000604482015290519081900360640190fd5b60008054610100600160a81b0319163361010081029190911782556040519091907f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b2364908290a3565b60006110ea83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506116db565b6000828201838110156110ea576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b6001600160a01b03811661150a576040805162461bcd60e51b815260206004820152601f60248201527f6d696e7465722063616e6e6f742062652061207a65726f206164647265737300604482015290519081900360640190fd5b6001600160a01b038216600090815260366020526040902061152c9082611772565b1561157757604080516001600160a01b0380851682528316602082015281517f4b5ef9a786cf64a7d82ebcf2d5132667edc9faef4ac36260d9a9e52c526b6232929181900390910190a15b5050565b60009081526001919091016020526040902054151590565b5490565b815460009082106115d95760405162461bcd60e51b81526004018080602001828103825260228152602001806118986022913960400191505060405180910390fd5b8260000182815481106115e857fe5b9060005260206000200154905092915050565b60006110ea836001600160a01b038416611787565b6001600160a01b03811661166b576040805162461bcd60e51b815260206004820152601f60248201527f6d696e7465722063616e6e6f742062652061207a65726f206164647265737300604482015290519081900360640190fd5b6001600160a01b038216600090815260366020526040902061168d90826115fb565b1561157757604080516001600160a01b0380851682528316602082015281517f3c091dafb1d99e4a4c333024492eac3b2cd8bf921a3dd547c937db33be307bb8929181900390910190a15050565b6000818484111561176a5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561172f578181015183820152602001611717565b50505050905090810190601f16801561175c5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b60006110ea836001600160a01b0384166117d1565b6000611793838361157b565b6117c957508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610aa4565b506000610aa4565b6000818152600183016020526040812054801561188d578354600019808301919081019060009087908390811061180457fe5b906000526020600020015490508087600001848154811061182157fe5b60009182526020808320909101929092558281526001898101909252604090209084019055865487908061185157fe5b60019003818190600052602060002001600090559055866001016000878152602001908152602001600020600090556001945050505050610aa4565b6000915050610aa456fe456e756d657261626c655365743a20696e646578206f7574206f6620626f756e64735f73657450656e64696e674f776e65723a204e6577206f77656e722063616e206e6f74206265207a65726f206164647265737320616e64206f776e657220686173206265656e20736574216f6e6c794d696e7465723a2063616c6c6572206973206e6f742074686520746f6b656e2773206d696e7465724d534420746f6b656e2063616e6e6f742062652061207a65726f2061646472657373496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a65646f6e6c794f776e65723a2063616c6c6572206973206e6f7420746865206f776e65725f6163636570744f776e65723a204f6e6c7920666f722070656e64696e67206f776e6572214c656e677468206f66205f6d696e7465727320616e64205f6d696e7443617073206d69736d61746368a2646970667358221220c55c5eb71f824ddf9dc75b921743c0aa725bf200379b3ad2b2c32fbe5fb9346d64736f6c634300060c0033","name":"MSDControllerV2","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"default","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":false,"additional_sources":[{"file_path":"@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Wrappers over Solidity's arithmetic operations with added overflow\n * checks.\n *\n * Arithmetic operations in Solidity wrap on overflow. This can easily result\n * in bugs, because programmers usually assume that an overflow raises an\n * error, which is the standard behavior in high level programming languages.\n * `SafeMath` restores this intuition by reverting the transaction when an\n * operation overflows.\n *\n * Using this library instead of the unchecked operations eliminates an entire\n * class of bugs, so it's recommended to use it always.\n */\nlibrary SafeMathUpgradeable {\n    /**\n     * @dev Returns the addition of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity's `+` operator.\n     *\n     * Requirements:\n     *\n     * - Addition cannot overflow.\n     */\n    function add(uint256 a, uint256 b) internal pure returns (uint256) {\n        uint256 c = a + b;\n        require(c >= a, \"SafeMath: addition overflow\");\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity's `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b) internal pure returns (uint256) {\n        return sub(a, b, \"SafeMath: subtraction overflow\");\n    }\n\n    /**\n     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on\n     * overflow (when the result is negative).\n     *\n     * Counterpart to Solidity's `-` operator.\n     *\n     * Requirements:\n     *\n     * - Subtraction cannot overflow.\n     */\n    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b <= a, errorMessage);\n        uint256 c = a - b;\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the multiplication of two unsigned integers, reverting on\n     * overflow.\n     *\n     * Counterpart to Solidity's `*` operator.\n     *\n     * Requirements:\n     *\n     * - Multiplication cannot overflow.\n     */\n    function mul(uint256 a, uint256 b) internal pure returns (uint256) {\n        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the\n        // benefit is lost if 'b' is also tested.\n        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522\n        if (a == 0) {\n            return 0;\n        }\n\n        uint256 c = a * b;\n        require(c / a == b, \"SafeMath: multiplication overflow\");\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers. Reverts on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity's `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b) internal pure returns (uint256) {\n        return div(a, b, \"SafeMath: division by zero\");\n    }\n\n    /**\n     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on\n     * division by zero. The result is rounded towards zero.\n     *\n     * Counterpart to Solidity's `/` operator. Note: this function uses a\n     * `revert` opcode (which leaves remaining gas untouched) while Solidity\n     * uses an invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b > 0, errorMessage);\n        uint256 c = a / b;\n        // assert(a == b * c + a % b); // There is no case in which this doesn't hold\n\n        return c;\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * Reverts when dividing by zero.\n     *\n     * Counterpart to Solidity's `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b) internal pure returns (uint256) {\n        return mod(a, b, \"SafeMath: modulo by zero\");\n    }\n\n    /**\n     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),\n     * Reverts with custom message when dividing by zero.\n     *\n     * Counterpart to Solidity's `%` operator. This function uses a `revert`\n     * opcode (which leaves remaining gas untouched) while Solidity uses an\n     * invalid opcode to revert (consuming all remaining gas).\n     *\n     * Requirements:\n     *\n     * - The divisor cannot be zero.\n     */\n    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {\n        require(b != 0, errorMessage);\n        return a % b;\n    }\n}\n"},{"file_path":"@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol","source_code":"// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSetUpgradeable {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping (bytes32 => uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value's index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) { // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as 'swap and pop').\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs\n            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.\n\n            bytes32 lastvalue = set._values[lastIndex];\n\n            // Move the last value to the index where the value to delete is\n            set._values[toDeleteIndex] = lastvalue;\n            // Update the index for the moved value\n            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        require(set._values.length > index, \"EnumerableSet: index out of bounds\");\n        return set._values[index];\n    }\n\n    // Bytes32Set\n\n    struct Bytes32Set {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _add(set._inner, value);\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _remove(set._inner, value);\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n        return _contains(set._inner, value);\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(Bytes32Set storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n        return _at(set._inner, index);\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(value)));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint256(_at(set._inner, index)));\n    }\n\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n}\n"},{"file_path":"contracts/library/ERC20.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\n\nimport \"@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 {\n    using SafeMathUpgradeable for uint256;\n\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    uint256 public totalSupply;\n\n    string public name;\n    string public symbol;\n    uint8 public decimals;\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(\n        address indexed owner,\n        address indexed spender,\n        uint256 value\n    );\n\n    /**\n     * @dev Sets the values for {name} and {symbol}, initializes {decimals} with\n     * a default value of 18.\n     *\n     * To select a different value for {decimals}, use {_setupDecimals}.\n     *\n     * All three of these values are immutable: they can only be set once during\n     * construction.\n     */\n    function __ERC20_init(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_\n    ) internal {\n        name = name_;\n        symbol = symbol_;\n        decimals = decimals_;\n    }\n\n    /**\n     * @dev See {IERC20-transfer}.\n     *\n     * Requirements:\n     *\n     * - `recipient` cannot be the zero address.\n     * - the caller must have a balance of at least `amount`.\n     */\n    function transfer(address recipient, uint256 amount)\n        public\n        virtual\n        returns (bool)\n    {\n        _transfer(msg.sender, recipient, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-approve}.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function approve(address spender, uint256 amount)\n        public\n        virtual\n        returns (bool)\n    {\n        _approve(msg.sender, spender, amount);\n        return true;\n    }\n\n    /**\n     * @dev See {IERC20-transferFrom}.\n     *\n     * Emits an {Approval} event indicating the updated allowance. This is not\n     * required by the EIP. See the note at the beginning of {ERC20}.\n     *\n     * Requirements:\n     *\n     * - `sender` and `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     * - the caller must have allowance for ``sender``'s tokens of at least\n     * `amount`.\n     */\n    function transferFrom(\n        address sender,\n        address recipient,\n        uint256 amount\n    ) public virtual returns (bool) {\n        _transfer(sender, recipient, amount);\n        _approve(sender, msg.sender, allowance[sender][msg.sender].sub(amount));\n        return true;\n    }\n\n    /**\n     * @dev Atomically increases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     */\n    function increaseAllowance(address spender, uint256 addedValue)\n        public\n        virtual\n        returns (bool)\n    {\n        _approve(\n            msg.sender,\n            spender,\n            allowance[msg.sender][spender].add(addedValue)\n        );\n        return true;\n    }\n\n    /**\n     * @dev Atomically decreases the allowance granted to `spender` by the caller.\n     *\n     * This is an alternative to {approve} that can be used as a mitigation for\n     * problems described in {IERC20-approve}.\n     *\n     * Emits an {Approval} event indicating the updated allowance.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `spender` must have allowance for the caller of at least\n     * `subtractedValue`.\n     */\n    function decreaseAllowance(address spender, uint256 subtractedValue)\n        public\n        virtual\n        returns (bool)\n    {\n        _approve(\n            msg.sender,\n            spender,\n            allowance[msg.sender][spender].sub(subtractedValue)\n        );\n        return true;\n    }\n\n    /**\n     * @dev Moves tokens `amount` from `sender` to `recipient`.\n     *\n     * This is internal function is equivalent to {transfer}, and can be used to\n     * e.g. implement automatic token fees, slashing mechanisms, etc.\n     *\n     * Emits a {Transfer} event.\n     *\n     * Requirements:\n     *\n     * - `sender` cannot be the zero address.\n     * - `recipient` cannot be the zero address.\n     * - `sender` must have a balance of at least `amount`.\n     */\n    function _transfer(\n        address sender,\n        address recipient,\n        uint256 amount\n    ) internal virtual {\n        require(sender != address(0), \"ERC20: transfer from the zero address\");\n        require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n        balanceOf[sender] = balanceOf[sender].sub(amount);\n        balanceOf[recipient] = balanceOf[recipient].add(amount);\n        emit Transfer(sender, recipient, amount);\n    }\n\n    /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n     * the total supply.\n     *\n     * Emits a {Transfer} event with `from` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `to` cannot be the zero address.\n     */\n    function _mint(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: mint to the zero address\");\n\n        totalSupply = totalSupply.add(amount);\n        balanceOf[account] = balanceOf[account].add(amount);\n        emit Transfer(address(0), account, amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, reducing the\n     * total supply.\n     *\n     * Emits a {Transfer} event with `to` set to the zero address.\n     *\n     * Requirements:\n     *\n     * - `account` cannot be the zero address.\n     * - `account` must have at least `amount` tokens.\n     */\n    function _burn(address account, uint256 amount) internal virtual {\n        require(account != address(0), \"ERC20: burn from the zero address\");\n\n        balanceOf[account] = balanceOf[account].sub(amount);\n        totalSupply = totalSupply.sub(amount);\n        emit Transfer(account, address(0), amount);\n    }\n\n    /**\n     * @dev Destroys `amount` tokens from `account`, deducting from the caller's\n     * allowance if caller is not the `account`.\n     *\n     * See {ERC20-_burn} and {ERC20-allowance}.\n     *\n     * Requirements:\n     *\n     * - the caller other than `msg.sender` must have allowance for ``accounts``'s tokens of at least\n     * `amount`.\n     */\n    function _burnFrom(address account, uint256 amount) internal virtual {\n        if (msg.sender != account)\n            _approve(\n                account,\n                msg.sender,\n                allowance[account][msg.sender].sub(amount)\n            );\n\n        _burn(account, amount);\n    }\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n     *\n     * This internal function is equivalent to `approve`, and can be used to\n     * e.g. set automatic allowances for certain subsystems, etc.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `owner` cannot be the zero address.\n     * - `spender` cannot be the zero address.\n     */\n    function _approve(\n        address owner,\n        address spender,\n        uint256 amount\n    ) internal virtual {\n        require(owner != address(0), \"ERC20: approve from the zero address\");\n        require(spender != address(0), \"ERC20: approve to the zero address\");\n\n        allowance[owner][spender] = amount;\n        emit Approval(owner, spender, amount);\n    }\n\n    uint256[50] private __gap;\n}\n"},{"file_path":"contracts/library/Initializable.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\n\n/**\n * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed\n * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an\n * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer\n * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.\n *\n * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as\n * possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.\n *\n * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure\n * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.\n */\nabstract contract Initializable {\n    /**\n     * @dev Indicates that the contract has been initialized.\n     */\n    bool private _initialized;\n\n    /**\n     * @dev Modifier to protect an initializer function from being invoked twice.\n     */\n    modifier initializer() {\n        require(\n            !_initialized,\n            \"Initializable: contract is already initialized\"\n        );\n\n        _;\n\n        _initialized = true;\n    }\n}\n"},{"file_path":"contracts/library/Ownable.sol","source_code":"//SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\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 {_setPendingOwner} and {_acceptOwner}.\n */\ncontract Ownable {\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    address payable public owner;\n\n    /**\n     * @dev Returns the address of the current pending owner.\n     */\n    address payable public pendingOwner;\n\n    event NewOwner(address indexed previousOwner, address indexed newOwner);\n    event NewPendingOwner(\n        address indexed oldPendingOwner,\n        address indexed newPendingOwner\n    );\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        require(owner == msg.sender, \"onlyOwner: caller is not the owner\");\n        _;\n    }\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    function __Ownable_init() internal {\n        owner = msg.sender;\n        emit NewOwner(address(0), msg.sender);\n    }\n\n    /**\n     * @notice Base on the inputing parameter `newPendingOwner` to check the exact error reason.\n     * @dev Transfer contract control to a new owner. The newPendingOwner must call `_acceptOwner` to finish the transfer.\n     * @param newPendingOwner New pending owner.\n     */\n    function _setPendingOwner(address payable newPendingOwner)\n        external\n        onlyOwner\n    {\n        require(\n            newPendingOwner != address(0) && newPendingOwner != pendingOwner,\n            \"_setPendingOwner: New owenr can not be zero address and owner has been set!\"\n        );\n\n        // Gets current owner.\n        address oldPendingOwner = pendingOwner;\n\n        // Sets new pending owner.\n        pendingOwner = newPendingOwner;\n\n        emit NewPendingOwner(oldPendingOwner, newPendingOwner);\n    }\n\n    /**\n     * @dev Accepts the admin rights, but only for pendingOwenr.\n     */\n    function _acceptOwner() external {\n        require(\n            msg.sender == pendingOwner,\n            \"_acceptOwner: Only for pending owner!\"\n        );\n\n        // Gets current values for events.\n        address oldOwner = owner;\n        address oldPendingOwner = pendingOwner;\n\n        // Set the new contract owner.\n        owner = pendingOwner;\n\n        // Clear the pendingOwner.\n        pendingOwner = address(0);\n\n        emit NewOwner(oldOwner, owner);\n        emit NewPendingOwner(oldPendingOwner, pendingOwner);\n    }\n\n    uint256[50] private __gap;\n}\n"},{"file_path":"contracts/msd/MSD.sol","source_code":"// SPDX-License-Identifier: MIT\npragma solidity 0.6.12;\n\nimport \"@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol\";\n\nimport \"../library/Initializable.sol\";\nimport \"../library/Ownable.sol\";\nimport \"../library/ERC20.sol\";\n\n/**\n * @title dForce's Multi-currency Stable Debt Token\n * @author dForce\n */\ncontract MSD is Initializable, Ownable, ERC20 {\n    using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;\n\n    bytes32 public DOMAIN_SEPARATOR;\n    // keccak256(\"Permit(address owner,address spender,uint256 chainId, uint256 value,uint256 nonce,uint256 deadline)\");\n    bytes32 public constant PERMIT_TYPEHASH =\n        0x576144ed657c8304561e56ca632e17751956250114636e8c01f64a7f2c6d98cf;\n    mapping(address => uint256) public nonces;\n\n    /// @dev EnumerableSet of minters\n    EnumerableSetUpgradeable.AddressSet internal minters;\n\n    /**\n     * @dev Emitted when `minter` is added as `minter`.\n     */\n    event MinterAdded(address minter);\n\n    /**\n     * @dev Emitted when `minter` is removed from `minters`.\n     */\n    event MinterRemoved(address minter);\n\n    /**\n     * @notice Expects to call only once to initialize the MSD token.\n     * @param _name Token name.\n     * @param _symbol Token symbol.\n     */\n    function initialize(\n        string memory _name,\n        string memory _symbol,\n        uint8 _decimals\n    ) external initializer {\n        __Ownable_init();\n        __ERC20_init(_name, _symbol, _decimals);\n\n        DOMAIN_SEPARATOR = keccak256(\n            abi.encode(\n                keccak256(\n                    \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"\n                ),\n                keccak256(bytes(_name)),\n                keccak256(bytes(\"1\")),\n                _getChainId(),\n                address(this)\n            )\n        );\n    }\n\n    /**\n     * @dev Throws if called by any account other than the minters.\n     */\n    modifier onlyMinter() {\n        require(\n            minters.contains(msg.sender),\n            \"onlyMinter: caller is not minter\"\n        );\n        _;\n    }\n\n    /**\n     * @notice Add `minter` into minters.\n     * If `minter` have not been a minter, emits a `MinterAdded` event.\n     *\n     * @param _minter The minter to add\n     *\n     * Requirements:\n     * - the caller must be `owner`.\n     */\n    function _addMinter(address _minter) external onlyOwner {\n        require(_minter != address(0), \"_addMinter: _minter the zero address\");\n        if (minters.add(_minter)) {\n            emit MinterAdded(_minter);\n        }\n    }\n\n    /**\n     * @notice Remove `minter` from minters.\n     * If `minter` is a minter, emits a `MinterRemoved` event.\n     *\n     * @param _minter The minter to remove\n     *\n     * Requirements:\n     * - the caller must be `owner`.\n     */\n    function _removeMinter(address _minter) external onlyOwner {\n        require(\n            _minter != address(0),\n            \"_removeMinter: _minter the zero address\"\n        );\n        if (minters.remove(_minter)) {\n            emit MinterRemoved(_minter);\n        }\n    }\n\n    function mint(address to, uint256 amount) external onlyMinter {\n        _mint(to, amount);\n    }\n\n    function burn(address from, uint256 amount) external {\n        _burnFrom(from, amount);\n    }\n\n    function _getChainId() internal pure returns (uint256) {\n        uint256 chainId;\n        assembly {\n            chainId := chainid()\n        }\n        return chainId;\n    }\n\n    /**\n     * @dev EIP2612 permit function. For more details, please look at here:\n     * https://eips.ethereum.org/EIPS/eip-2612\n     * @param _owner The owner of the funds.\n     * @param _spender The spender.\n     * @param _value The amount.\n     * @param _deadline The deadline timestamp, type(uint256).max for max deadline.\n     * @param _v Signature param.\n     * @param _s Signature param.\n     * @param _r Signature param.\n     */\n    function permit(\n        address _owner,\n        address _spender,\n        uint256 _value,\n        uint256 _deadline,\n        uint8 _v,\n        bytes32 _r,\n        bytes32 _s\n    ) external {\n        require(_deadline >= block.timestamp, \"permit: EXPIRED!\");\n        uint256 _currentNonce = nonces[_owner];\n        bytes32 _digest =\n            keccak256(\n                abi.encodePacked(\n                    \"\\x19\\x01\",\n                    DOMAIN_SEPARATOR,\n                    keccak256(\n                        abi.encode(\n                            PERMIT_TYPEHASH,\n                            _owner,\n                            _spender,\n                            _getChainId(),\n                            _value,\n                            _currentNonce,\n                            _deadline\n                        )\n                    )\n                )\n            );\n        address _recoveredAddress = ecrecover(_digest, _v, _r, _s);\n        require(\n            _recoveredAddress != address(0) && _recoveredAddress == _owner,\n            \"permit: INVALID_SIGNATURE!\"\n        );\n        nonces[_owner] = _currentNonce.add(1);\n        _approve(_owner, _spender, _value);\n    }\n\n    /**\n     * @notice Return all minters of this MSD token\n     * @return _minters The list of minter addresses\n     */\n    function getMinters() public view returns (address[] memory _minters) {\n        uint256 _len = minters.length();\n        _minters = new address[](_len);\n        for (uint256 i = 0; i < _len; i++) {\n            _minters[i] = minters.at(i);\n        }\n    }\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"MSDAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"minter","type":"address"}],"name":"MinterAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"minter","type":"address"}],"name":"MinterRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"minter","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldMintCap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMintCap","type":"uint256"}],"name":"NewMintCap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldPendingOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newPendingOwner","type":"address"}],"name":"NewPendingOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"oldTotalReserves","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalReserves","type":"uint256"}],"name":"ReservesWithdrawn","type":"event"},{"inputs":[],"name":"_acceptOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address[]","name":"_minters","type":"address[]"},{"internalType":"uint256[]","name":"_mintCaps","type":"uint256[]"}],"name":"_addMSD","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address[]","name":"_minters","type":"address[]"},{"internalType":"uint256[]","name":"_mintCaps","type":"uint256[]"}],"name":"_addMinters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address[]","name":"_minters","type":"address[]"}],"name":"_removeMinters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address[]","name":"_minters","type":"address[]"},{"internalType":"uint256[]","name":"_mintCaps","type":"uint256[]"}],"name":"_setMintCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"newPendingOwner","type":"address"}],"name":"_setPendingOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"_withdrawReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"calcEquity","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllMSDs","outputs":[{"internalType":"address[]","name":"_allMSDs","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getMSDMinters","outputs":[{"internalType":"address[]","name":"_minters","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"hasMSD","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isMSDController","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"mintCaps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mintMSD","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"msdTokenData","outputs":[{"internalType":"uint256","name":"earning","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"package_name":null,"constructor_args":null}