{"file_path":"src/SavingModule.sol","creation_status":"success","source_code":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.24;\n\nimport {Math} from \"openzeppelin-contracts/contracts/utils/math/Math.sol\";\n\nimport {ISavingModule} from \"src/interfaces/ISavingModule.sol\";\n\nimport {AccessControl} from \"openzeppelin-contracts/contracts/access/AccessControl.sol\";\n\nimport {compoundValue, dayCount} from \"src/functions/TermCalculator.sol\";\n\nimport {IToken} from \"src/interfaces/IToken.sol\";\n\ncontract SavingModule is AccessControl, ISavingModule {\n    bytes32 public constant MANAGER =\n        keccak256(abi.encode(\"saving.module.manager\"));\n\n    bytes32 public constant CONTROLLER =\n        keccak256(abi.encode(\"saving.module.controller\"));\n\n    uint256 public lastTimestamp; // timestamp of the last update\n\n    uint256 public currentRate = 0e12;\n    uint256 public compoundFactorAccum = 1e8;\n\n    uint256 public redeemFee = 0e6;\n\n    IToken public immutable rusd;\n    IToken public immutable srusd;\n\n    constructor(address admin, IToken rusd_, IToken srusd_) {\n        _grantRole(DEFAULT_ADMIN_ROLE, admin);\n\n        rusd = rusd_;\n        srusd = srusd_;\n\n        lastTimestamp = block.timestamp;\n    }\n\n    /// @notice Mint srUSD to one address and burn rUSD from the other\n    /// @param from Sender address\n    /// @param to Receiver address\n    /// @param amount Burned rUSD\n    function mint(\n        address from,\n        address to,\n        uint256 amount\n    ) external onlyRole(CONTROLLER) {\n        uint256 mintAmount = _mint(from, to, amount);\n\n        emit Mint(from, to, mintAmount, amount, block.timestamp);\n    }\n\n    function _mint(\n        address from,\n        address to,\n        uint256 amount\n    ) private returns (uint256 mintAmount) {\n        mintAmount = _previewMint(amount);\n\n        assert(amount >= (mintAmount * _currentPrice()) / 1e8);\n\n        rusd.burnFrom(from, amount);\n\n        srusd.mint(to, mintAmount);\n    }\n\n    /// @notice Calculates the amount of srUSD that will be minted\n    /// @param amount Burned rUSD\n    /// @return uint256 Minted srUSD\n    function previewMint(uint256 amount) external view returns (uint256) {\n        return _previewMint(amount);\n    }\n\n    function _previewMint(uint256 amount) private view returns (uint256) {\n        return (amount * 1e8) / _currentPrice();\n    }\n\n    /// @notice Burn srUSD from the sender address and mint rUSD to it\n    /// @param amount Minted rUSD\n    function redeem(uint256 amount) external {\n        uint256 burnAmount = _redeem(msg.sender, msg.sender, amount);\n\n        emit Redeem(\n            msg.sender,\n            msg.sender,\n            amount,\n            burnAmount,\n            block.timestamp\n        );\n    }\n\n    /// @notice Burn srUSD from the sender address and mint rUSD to the other\n    /// @param to Receiver address\n    /// @param amount Minted rUSD\n    function redeem(address to, uint256 amount) external {\n        uint256 burnAmount = _redeem(msg.sender, to, amount);\n\n        emit Redeem(msg.sender, to, amount, burnAmount, block.timestamp);\n    }\n\n    function _redeem(\n        address from,\n        address to,\n        uint256 amount\n    ) private returns (uint256 burnAmount) {\n        burnAmount = _previewRedeem(amount);\n\n        assert((burnAmount * _currentPrice()) / 1e8 >= amount);\n\n        srusd.burnFrom(from, (burnAmount * (1e6 + redeemFee)) / 1e6);\n\n        rusd.mint(to, amount);\n    }\n\n    /// @notice Calculates the amount of srUSD that will be burned\n    /// @param amount Minted rUSD\n    /// @return uint256 Burned srUSD\n    function previewRedeem(uint256 amount) external view returns (uint256) {\n        return _previewRedeem(amount);\n    }\n\n    function _previewRedeem(uint256 amount) private view returns (uint256) {\n        return Math.ceilDiv(amount * 1e8, _currentPrice());\n    }\n\n    /// @notice Total rUSD in circulation\n    /// @return uint256 Total rUSD liability\n    function rusdTotalLiability() external view returns (uint256) {\n        return _rusdTotalLiability();\n    }\n\n    function _rusdTotalLiability() private view returns (uint256) {\n        return\n            rusd.totalSupply() + (srusd.totalSupply() * _currentPrice()) / 1e8;\n    }\n\n    /// @notice Total srUSD supply\n    /// @return uint256 Total debt\n    function totalDebt() external view returns (uint256) {\n        return _totalDebt();\n    }\n\n    function _totalDebt() private view returns (uint256) {\n        return srusd.totalSupply();\n    }\n\n    /// @notice Current price of srUSD in rUSD (always >= 1e8)\n    /// @return uint256 Price\n    function currentPrice() external view returns (uint256) {\n        return _currentPrice();\n    }\n\n    function _currentPrice() private view returns (uint256) {\n        return\n            (compoundFactorAccum *\n                _compoundFactor(1e8, block.timestamp, currentRate)) / 1e8;\n    }\n\n    /// @notice Compound factor calculation based on the initial time stamp\n    /// @return uint256 Current compound factor\n    function compoundFactor() external view returns (uint256) {\n        return _compoundFactor(1e8, block.timestamp, currentRate);\n    }\n\n    function _compoundFactor(\n        uint256 value,\n        uint256 blockTimestamp,\n        uint256 rate\n    ) private view returns (uint256) {\n        uint256 daysCount = dayCount(lastTimestamp, blockTimestamp);\n\n        return compoundValue(value, daysCount, rate);\n    }\n\n    /// @notice Set the redemption fee for srUSD\n    /// fee The percentage of the srUSD burned\n    function setRedeemFee(uint256 fee) external onlyRole(MANAGER) {\n        require(1e6 > fee, \"SM: Fee can not be above 100%\");\n\n        redeemFee = fee;\n    }\n\n    /// @notice Set the interest for srUSD\n    /// @param rate New value for the interest rate\n    function update(uint256 rate) external onlyRole(MANAGER) {\n        require(1e12 > rate, \"SM: Savings rate can not be above 100% per anum\");\n\n        compoundFactorAccum =\n            (compoundFactorAccum *\n                _compoundFactor(1e8, block.timestamp, currentRate)) /\n            1e8;\n\n        emit Update(compoundFactorAccum, currentRate, rate, block.timestamp);\n\n        currentRate = rate;\n        lastTimestamp = block.timestamp;\n    }\n}\n","deployed_bytecode":"0x608060405234801561001057600080fd5b506004361061018e5760003560e01c80639d1b464a116100de578063c6c3bbe611610097578063db006a7511610071578063db006a7514610355578063ee0fc12114610368578063f9f8bdb714610370578063fc7b9c181461037957600080fd5b8063c6c3bbe614610326578063c6d46ab114610339578063d547741f1461034257600080fd5b80639d1b464a14610295578063a217fddf1461029d578063a5a4dcf7146102a5578063b3d7f6b9146102e4578063c13dbfed146102f7578063c5717fa8146102ff57600080fd5b80632f2ff15d1161014b5780635d841af5116101255780635d841af51461025357806382ab890a1461026657806391d1485414610279578063965fa21e1461028c57600080fd5b80632f2ff15d1461021a57806336568abe1461022d5780634cdad5061461024057600080fd5b806301ffc9a71461019357806311ba7f31146101bb57806319d8ac61146101d15780631b2df850146101da5780631e9a6950146101e2578063248a9ca3146101f7575b600080fd5b6101a66101a136600461124e565b610381565b60405190151581526020015b60405180910390f35b6101c36103b8565b6040519081526020016101b2565b6101c360015481565b6101c36103d0565b6101f56101f0366004611294565b6103f8565b005b6101c36102053660046112be565b60009081526020819052604090206001015490565b6101f56102283660046112d7565b61045a565b6101f561023b3660046112d7565b610484565b6101c361024e3660046112be565b610507565b6101f56102613660046112be565b610512565b6101f56102743660046112be565b610598565b6101a66102873660046112d7565b6106b8565b6101c360045481565b6101c36106e1565b6101c3600081565b6102cc7f00000000000000000000000009d4214c03d01f49544c0448dbe3a27f768f2b3481565b6040516001600160a01b0390911681526020016101b2565b6101c36102f23660046112be565b6106eb565b6101c36106f6565b6102cc7f000000000000000000000000738d1115b90efa71ae468f1287fc864775e23a3181565b6101f5610334366004611303565b610700565b6101c360035481565b6101f56103503660046112d7565b6107d1565b6101f56103633660046112be565b6107f6565b6101c361084e565b6101c360025481565b6101c361088a565b60006001600160e01b03198216637965db0b60e01b14806103b257506301ffc9a760e01b6001600160e01b03198316145b92915050565b60006103cb6305f5e10042600254610894565b905090565b6040516020016103df9061133f565b6040516020818303038152906040528051906020012081565b60006104053384846108b9565b6040805184815260208101839052428183015290519192506001600160a01b0385169133917f215abfcd108b85fbee47f26fda2de66f90f14fa5fcaf0201698ad8ac9323545f919081900360600190a3505050565b60008281526020819052604090206001015461047581610a37565b61047f8383610a44565b505050565b6001600160a01b03811633146104f95760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b6105038282610ac8565b5050565b60006103b282610b2d565b6040516020016105219061133f565b6040516020818303038152906040528051906020012061054081610a37565b81620f4240116105925760405162461bcd60e51b815260206004820152601d60248201527f534d3a204665652063616e206e6f742062652061626f7665203130302500000060448201526064016104f0565b50600455565b6040516020016105a79061133f565b604051602081830303815290604052805190602001206105c681610a37565b8164e8d4a51000116106325760405162461bcd60e51b815260206004820152602f60248201527f534d3a20536176696e677320726174652063616e206e6f742062652061626f7660448201526e6520313030252070657220616e756d60881b60648201526084016104f0565b6305f5e1006106486305f5e10042600254610894565b6003546106559190611384565b61065f919061139b565b600381905560025460408051928352602083019190915281018390524260608201527fce49b138f2621300a7043a61f472ccfd643162bdf3efb11354e22d5273b77a719060800160405180910390a15060025542600155565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006103cb610b4d565b60006103b282610b7c565b60006103cb610b9e565b60405160200161073c9060208082526018908201527739b0bb34b7339736b7b23ab6329731b7b73a3937b63632b960411b604082015260600190565b6040516020818303038152906040528051906020012061075b81610a37565b6000610768858585610ccf565b9050836001600160a01b0316856001600160a01b03167f458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b861968386426040516107c2939291909283526020830191909152604082015260600190565b60405180910390a35050505050565b6000828152602081905260409020600101546107ec81610a37565b61047f8383610ac8565b60006108033333846108b9565b604080518481526020810183905242818301529051919250339182917f215abfcd108b85fbee47f26fda2de66f90f14fa5fcaf0201698ad8ac9323545f919081900360600190a35050565b6040516020016103df9060208082526018908201527739b0bb34b7339736b7b23ab6329731b7b73a3937b63632b960411b604082015260600190565b60006103cb610de3565b6000806108a360015485610e67565b90506108b0858285610e98565b95945050505050565b60006108c482610b2d565b9050816305f5e1006108d4610b4d565b6108de9084611384565b6108e8919061139b565b10156108f6576108f66113bd565b7f000000000000000000000000738d1115b90efa71ae468f1287fc864775e23a316001600160a01b03166379cc679085620f4240600454620f424061093b91906113d3565b6109459086611384565b61094f919061139b565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801561099557600080fd5b505af11580156109a9573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b038681166004830152602482018690527f00000000000000000000000009d4214c03d01f49544c0448dbe3a27f768f2b341692506340c10f1991506044015b600060405180830381600087803b158015610a1857600080fd5b505af1158015610a2c573d6000803e3d6000fd5b505050509392505050565b610a418133610ed0565b50565b610a4e82826106b8565b610503576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610a843390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610ad282826106b8565b15610503576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006103b2610b40836305f5e100611384565b610b48610b4d565b610f29565b60006305f5e100610b656305f5e10042600254610894565b600354610b729190611384565b6103cb919061139b565b6000610b86610b4d565b610b94836305f5e100611384565b6103b2919061139b565b60006305f5e100610bad610b4d565b7f000000000000000000000000738d1115b90efa71ae468f1287fc864775e23a316001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2f91906113e6565b610c399190611384565b610c43919061139b565b7f00000000000000000000000009d4214c03d01f49544c0448dbe3a27f768f2b346001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ca1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cc591906113e6565b6103cb91906113d3565b6000610cda82610b7c565b90506305f5e100610ce9610b4d565b610cf39083611384565b610cfd919061139b565b821015610d0c57610d0c6113bd565b60405163079cc67960e41b81526001600160a01b038581166004830152602482018490527f00000000000000000000000009d4214c03d01f49544c0448dbe3a27f768f2b3416906379cc679090604401600060405180830381600087803b158015610d7657600080fd5b505af1158015610d8a573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b038681166004830152602482018590527f000000000000000000000000738d1115b90efa71ae468f1287fc864775e23a311692506340c10f1991506044016109fe565b60007f000000000000000000000000738d1115b90efa71ae468f1287fc864775e23a316001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e43573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103cb91906113e6565b600081831115610e79575060006103b2565b62015180610e8784846113ff565b610e91919061139b565b9392505050565b60006ec097ce7bc90715b34b9f1000000000610eb48484610f60565b610ebe9086611384565b610ec8919061139b565b949350505050565b610eda82826106b8565b61050357610ee7816110a0565b610ef28360206110b2565b604051602001610f03929190611436565b60408051601f198184030181529082905262461bcd60e51b82526104f0916004016114ab565b60008215610f575781610f3d6001856113ff565b610f47919061139b565b610f529060016113d3565b610e91565b50600092915050565b600082826ec097ce7bc90715b34b9f10000000008382610f8a8569d3c21bcecceda1000000611384565b610f949190611384565b905083600003610fb357610fa881836113d3565b9450505050506103b2565b60006002610fc181866115c2565b610fcc6001886113ff565b610fd69088611384565b610fe09190611384565b610fef9064e8d4a51000611384565b610ff9919061139b565b905084600103611024578061100e83856113d3565b61101891906113d3565b955050505050506103b2565b600060066110336003876115c2565b61103e6002896113ff565b61104960018a6113ff565b611053908a611384565b61105d9190611384565b6110679190611384565b611071919061139b565b9050808261107f85876113d3565b61108991906113d3565b61109391906113d3565b9998505050505050505050565b60606103b26001600160a01b03831660145b606060006110c1836002611384565b6110cc9060026113d3565b67ffffffffffffffff8111156110e4576110e46115d1565b6040519080825280601f01601f19166020018201604052801561110e576020820181803683370190505b509050600360fc1b81600081518110611129576111296115e7565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110611158576111586115e7565b60200101906001600160f81b031916908160001a905350600061117c846002611384565b6111879060016113d3565b90505b60018111156111ff576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106111bb576111bb6115e7565b1a60f81b8282815181106111d1576111d16115e7565b60200101906001600160f81b031916908160001a90535060049490941c936111f8816115fd565b905061118a565b508315610e915760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016104f0565b60006020828403121561126057600080fd5b81356001600160e01b031981168114610e9157600080fd5b80356001600160a01b038116811461128f57600080fd5b919050565b600080604083850312156112a757600080fd5b6112b083611278565b946020939093013593505050565b6000602082840312156112d057600080fd5b5035919050565b600080604083850312156112ea57600080fd5b823591506112fa60208401611278565b90509250929050565b60008060006060848603121561131857600080fd5b61132184611278565b925061132f60208501611278565b9150604084013590509250925092565b60208082526015908201527439b0bb34b7339736b7b23ab6329736b0b730b3b2b960591b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176103b2576103b261136e565b6000826113b857634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052600160045260246000fd5b808201808211156103b2576103b261136e565b6000602082840312156113f857600080fd5b5051919050565b818103818111156103b2576103b261136e565b60005b8381101561142d578181015183820152602001611415565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161146e816017850160208801611412565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161149f816028840160208801611412565b01602801949350505050565b60208152600082518060208401526114ca816040850160208701611412565b601f01601f19169190910160400192915050565b600181815b808511156115195781600019048211156114ff576114ff61136e565b8085161561150c57918102915b93841c93908002906114e3565b509250929050565b600082611530575060016103b2565b8161153d575060006103b2565b8160018114611553576002811461155d57611579565b60019150506103b2565b60ff84111561156e5761156e61136e565b50506001821b6103b2565b5060208310610133831016604e8410600b841016171561159c575081810a6103b2565b6115a683836114de565b80600019048211156115ba576115ba61136e565b029392505050565b6000610e9160ff841683611521565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161160c5761160c61136e565b50600019019056fea2646970667358221220dbf10ff146fb80c00fb30d70873a9fd352fee9bf99c485b9a6d0c22978a67e9a64736f6c63430008180033","optimization_enabled":true,"verified_twin_address_hash":null,"is_verified":true,"compiler_settings":{"evmVersion":"paris","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":true,"runs":200},"remappings":[":chainlink/=lib/chainlink/",":ds-test/=lib/forge-std/lib/ds-test/src/",":erc4626-tests/=lib/offchain-fund/lib/openzeppelin-contracts/lib/erc4626-tests/",":forge-std/=lib/forge-std/src/",":offchain-fund/=lib/offchain-fund/",":openzeppelin-contracts/=lib/openzeppelin-contracts/"]},"optimization_runs":200,"sourcify_repo_url":"https://repo.sourcify.dev/contracts/full_match/1/0x5475611Dffb8ef4d697Ae39df9395513b6E947d7/","decoded_constructor_args":[["0x6Ed13690efCc6f10217011E35Cffef4516F458bb",{"internalType":"address","name":"admin","type":"address"}],["0x09D4214C03D01F49544C0448DBE3A27f768F2b34",{"internalType":"contract IToken","name":"rusd_","type":"address"}],["0x738d1115B90efa71AE468F1287fc864775e23a31",{"internalType":"contract IToken","name":"srusd_","type":"address"}]],"compiler_version":"0.8.24+commit.e11b9ed9","is_verified_via_verifier_alliance":false,"verified_at":"2024-09-09T06:02:52.349039Z","implementations":[],"proxy_type":null,"external_libraries":[],"creation_bytecode":"0x60c060405260006002556305f5e10060035560006004553480156200002357600080fd5b506040516200181f3803806200181f83398101604081905262000046916200012a565b6200005360008462000070565b6001600160a01b039182166080521660a05250426001556200017e565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166200010d576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620000cc3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45b5050565b6001600160a01b03811681146200012757600080fd5b50565b6000806000606084860312156200014057600080fd5b83516200014d8162000111565b6020850151909350620001608162000111565b6040850151909250620001738162000111565b809150509250925092565b60805160a05161164a620001d560003960008181610304015281816108f801528181610baf01528181610db20152610de70152600081816102aa015281816109d101528181610c450152610d32015261164a6000f3fe608060405234801561001057600080fd5b506004361061018e5760003560e01c80639d1b464a116100de578063c6c3bbe611610097578063db006a7511610071578063db006a7514610355578063ee0fc12114610368578063f9f8bdb714610370578063fc7b9c181461037957600080fd5b8063c6c3bbe614610326578063c6d46ab114610339578063d547741f1461034257600080fd5b80639d1b464a14610295578063a217fddf1461029d578063a5a4dcf7146102a5578063b3d7f6b9146102e4578063c13dbfed146102f7578063c5717fa8146102ff57600080fd5b80632f2ff15d1161014b5780635d841af5116101255780635d841af51461025357806382ab890a1461026657806391d1485414610279578063965fa21e1461028c57600080fd5b80632f2ff15d1461021a57806336568abe1461022d5780634cdad5061461024057600080fd5b806301ffc9a71461019357806311ba7f31146101bb57806319d8ac61146101d15780631b2df850146101da5780631e9a6950146101e2578063248a9ca3146101f7575b600080fd5b6101a66101a136600461124e565b610381565b60405190151581526020015b60405180910390f35b6101c36103b8565b6040519081526020016101b2565b6101c360015481565b6101c36103d0565b6101f56101f0366004611294565b6103f8565b005b6101c36102053660046112be565b60009081526020819052604090206001015490565b6101f56102283660046112d7565b61045a565b6101f561023b3660046112d7565b610484565b6101c361024e3660046112be565b610507565b6101f56102613660046112be565b610512565b6101f56102743660046112be565b610598565b6101a66102873660046112d7565b6106b8565b6101c360045481565b6101c36106e1565b6101c3600081565b6102cc7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101b2565b6101c36102f23660046112be565b6106eb565b6101c36106f6565b6102cc7f000000000000000000000000000000000000000000000000000000000000000081565b6101f5610334366004611303565b610700565b6101c360035481565b6101f56103503660046112d7565b6107d1565b6101f56103633660046112be565b6107f6565b6101c361084e565b6101c360025481565b6101c361088a565b60006001600160e01b03198216637965db0b60e01b14806103b257506301ffc9a760e01b6001600160e01b03198316145b92915050565b60006103cb6305f5e10042600254610894565b905090565b6040516020016103df9061133f565b6040516020818303038152906040528051906020012081565b60006104053384846108b9565b6040805184815260208101839052428183015290519192506001600160a01b0385169133917f215abfcd108b85fbee47f26fda2de66f90f14fa5fcaf0201698ad8ac9323545f919081900360600190a3505050565b60008281526020819052604090206001015461047581610a37565b61047f8383610a44565b505050565b6001600160a01b03811633146104f95760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b6105038282610ac8565b5050565b60006103b282610b2d565b6040516020016105219061133f565b6040516020818303038152906040528051906020012061054081610a37565b81620f4240116105925760405162461bcd60e51b815260206004820152601d60248201527f534d3a204665652063616e206e6f742062652061626f7665203130302500000060448201526064016104f0565b50600455565b6040516020016105a79061133f565b604051602081830303815290604052805190602001206105c681610a37565b8164e8d4a51000116106325760405162461bcd60e51b815260206004820152602f60248201527f534d3a20536176696e677320726174652063616e206e6f742062652061626f7660448201526e6520313030252070657220616e756d60881b60648201526084016104f0565b6305f5e1006106486305f5e10042600254610894565b6003546106559190611384565b61065f919061139b565b600381905560025460408051928352602083019190915281018390524260608201527fce49b138f2621300a7043a61f472ccfd643162bdf3efb11354e22d5273b77a719060800160405180910390a15060025542600155565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006103cb610b4d565b60006103b282610b7c565b60006103cb610b9e565b60405160200161073c9060208082526018908201527739b0bb34b7339736b7b23ab6329731b7b73a3937b63632b960411b604082015260600190565b6040516020818303038152906040528051906020012061075b81610a37565b6000610768858585610ccf565b9050836001600160a01b0316856001600160a01b03167f458f5fa412d0f69b08dd84872b0215675cc67bc1d5b6fd93300a1c3878b861968386426040516107c2939291909283526020830191909152604082015260600190565b60405180910390a35050505050565b6000828152602081905260409020600101546107ec81610a37565b61047f8383610ac8565b60006108033333846108b9565b604080518481526020810183905242818301529051919250339182917f215abfcd108b85fbee47f26fda2de66f90f14fa5fcaf0201698ad8ac9323545f919081900360600190a35050565b6040516020016103df9060208082526018908201527739b0bb34b7339736b7b23ab6329731b7b73a3937b63632b960411b604082015260600190565b60006103cb610de3565b6000806108a360015485610e67565b90506108b0858285610e98565b95945050505050565b60006108c482610b2d565b9050816305f5e1006108d4610b4d565b6108de9084611384565b6108e8919061139b565b10156108f6576108f66113bd565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166379cc679085620f4240600454620f424061093b91906113d3565b6109459086611384565b61094f919061139b565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526024820152604401600060405180830381600087803b15801561099557600080fd5b505af11580156109a9573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b038681166004830152602482018690527f00000000000000000000000000000000000000000000000000000000000000001692506340c10f1991506044015b600060405180830381600087803b158015610a1857600080fd5b505af1158015610a2c573d6000803e3d6000fd5b505050509392505050565b610a418133610ed0565b50565b610a4e82826106b8565b610503576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610a843390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610ad282826106b8565b15610503576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60006103b2610b40836305f5e100611384565b610b48610b4d565b610f29565b60006305f5e100610b656305f5e10042600254610894565b600354610b729190611384565b6103cb919061139b565b6000610b86610b4d565b610b94836305f5e100611384565b6103b2919061139b565b60006305f5e100610bad610b4d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2f91906113e6565b610c399190611384565b610c43919061139b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ca1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cc591906113e6565b6103cb91906113d3565b6000610cda82610b7c565b90506305f5e100610ce9610b4d565b610cf39083611384565b610cfd919061139b565b821015610d0c57610d0c6113bd565b60405163079cc67960e41b81526001600160a01b038581166004830152602482018490527f000000000000000000000000000000000000000000000000000000000000000016906379cc679090604401600060405180830381600087803b158015610d7657600080fd5b505af1158015610d8a573d6000803e3d6000fd5b50506040516340c10f1960e01b81526001600160a01b038681166004830152602482018590527f00000000000000000000000000000000000000000000000000000000000000001692506340c10f1991506044016109fe565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e43573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103cb91906113e6565b600081831115610e79575060006103b2565b62015180610e8784846113ff565b610e91919061139b565b9392505050565b60006ec097ce7bc90715b34b9f1000000000610eb48484610f60565b610ebe9086611384565b610ec8919061139b565b949350505050565b610eda82826106b8565b61050357610ee7816110a0565b610ef28360206110b2565b604051602001610f03929190611436565b60408051601f198184030181529082905262461bcd60e51b82526104f0916004016114ab565b60008215610f575781610f3d6001856113ff565b610f47919061139b565b610f529060016113d3565b610e91565b50600092915050565b600082826ec097ce7bc90715b34b9f10000000008382610f8a8569d3c21bcecceda1000000611384565b610f949190611384565b905083600003610fb357610fa881836113d3565b9450505050506103b2565b60006002610fc181866115c2565b610fcc6001886113ff565b610fd69088611384565b610fe09190611384565b610fef9064e8d4a51000611384565b610ff9919061139b565b905084600103611024578061100e83856113d3565b61101891906113d3565b955050505050506103b2565b600060066110336003876115c2565b61103e6002896113ff565b61104960018a6113ff565b611053908a611384565b61105d9190611384565b6110679190611384565b611071919061139b565b9050808261107f85876113d3565b61108991906113d3565b61109391906113d3565b9998505050505050505050565b60606103b26001600160a01b03831660145b606060006110c1836002611384565b6110cc9060026113d3565b67ffffffffffffffff8111156110e4576110e46115d1565b6040519080825280601f01601f19166020018201604052801561110e576020820181803683370190505b509050600360fc1b81600081518110611129576111296115e7565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110611158576111586115e7565b60200101906001600160f81b031916908160001a905350600061117c846002611384565b6111879060016113d3565b90505b60018111156111ff576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106111bb576111bb6115e7565b1a60f81b8282815181106111d1576111d16115e7565b60200101906001600160f81b031916908160001a90535060049490941c936111f8816115fd565b905061118a565b508315610e915760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016104f0565b60006020828403121561126057600080fd5b81356001600160e01b031981168114610e9157600080fd5b80356001600160a01b038116811461128f57600080fd5b919050565b600080604083850312156112a757600080fd5b6112b083611278565b946020939093013593505050565b6000602082840312156112d057600080fd5b5035919050565b600080604083850312156112ea57600080fd5b823591506112fa60208401611278565b90509250929050565b60008060006060848603121561131857600080fd5b61132184611278565b925061132f60208501611278565b9150604084013590509250925092565b60208082526015908201527439b0bb34b7339736b7b23ab6329736b0b730b3b2b960591b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176103b2576103b261136e565b6000826113b857634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052600160045260246000fd5b808201808211156103b2576103b261136e565b6000602082840312156113f857600080fd5b5051919050565b818103818111156103b2576103b261136e565b60005b8381101561142d578181015183820152602001611415565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161146e816017850160208801611412565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161149f816028840160208801611412565b01602801949350505050565b60208152600082518060208401526114ca816040850160208701611412565b601f01601f19169190910160400192915050565b600181815b808511156115195781600019048211156114ff576114ff61136e565b8085161561150c57918102915b93841c93908002906114e3565b509250929050565b600082611530575060016103b2565b8161153d575060006103b2565b8160018114611553576002811461155d57611579565b60019150506103b2565b60ff84111561156e5761156e61136e565b50506001821b6103b2565b5060208310610133831016604e8410600b841016171561159c575081810a6103b2565b6115a683836114de565b80600019048211156115ba576115ba61136e565b029392505050565b6000610e9160ff841683611521565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161160c5761160c61136e565b50600019019056fea2646970667358221220dbf10ff146fb80c00fb30d70873a9fd352fee9bf99c485b9a6d0c22978a67e9a64736f6c634300081800330000000000000000000000006ed13690efcc6f10217011e35cffef4516f458bb00000000000000000000000009d4214c03d01f49544c0448dbe3a27f768f2b34000000000000000000000000738d1115b90efa71ae468f1287fc864775e23a31","name":"SavingModule","is_blueprint":false,"license_type":"none","is_fully_verified":true,"is_verified_via_eth_bytecode_db":true,"language":"solidity","evm_version":"paris","can_be_visualized_via_sol2uml":true,"is_verified_via_sourcify":true,"additional_sources":[{"file_path":"lib/openzeppelin-contracts/contracts/access/AccessControl.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IAccessControl.sol\";\nimport \"../utils/Context.sol\";\nimport \"../utils/Strings.sol\";\nimport \"../utils/introspection/ERC165.sol\";\n\n/**\n * @dev Contract module that allows children to implement role-based access\n * control mechanisms. This is a lightweight version that doesn't allow enumerating role\n * members except through off-chain means by accessing the contract event logs. Some\n * applications may benefit from on-chain enumerability, for those cases see\n * {AccessControlEnumerable}.\n *\n * Roles are referred to by their `bytes32` identifier. These should be exposed\n * in the external API and be unique. The best way to achieve this is by\n * using `public constant` hash digests:\n *\n * ```\n * bytes32 public constant MY_ROLE = keccak256(\"MY_ROLE\");\n * ```\n *\n * Roles can be used to represent a set of permissions. To restrict access to a\n * function call, use {hasRole}:\n *\n * ```\n * function foo() public {\n *     require(hasRole(MY_ROLE, msg.sender));\n *     ...\n * }\n * ```\n *\n * Roles can be granted and revoked dynamically via the {grantRole} and\n * {revokeRole} functions. Each role has an associated admin role, and only\n * accounts that have a role's admin role can call {grantRole} and {revokeRole}.\n *\n * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means\n * that only accounts with this role will be able to grant or revoke other\n * roles. More complex role relationships can be created by using\n * {_setRoleAdmin}.\n *\n * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to\n * grant and revoke this role. Extra precautions should be taken to secure\n * accounts that have been granted it.\n */\nabstract contract AccessControl is Context, IAccessControl, ERC165 {\n    struct RoleData {\n        mapping(address => bool) members;\n        bytes32 adminRole;\n    }\n\n    mapping(bytes32 => RoleData) private _roles;\n\n    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;\n\n    /**\n     * @dev Modifier that checks that an account has a specific role. Reverts\n     * with a standardized message including the required role.\n     *\n     * The format of the revert reason is given by the following regular expression:\n     *\n     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n     *\n     * _Available since v4.1._\n     */\n    modifier onlyRole(bytes32 role) {\n        _checkRole(role);\n        _;\n    }\n\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);\n    }\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) public view virtual override returns (bool) {\n        return _roles[role].members[account];\n    }\n\n    /**\n     * @dev Revert with a standard message if `_msgSender()` is missing `role`.\n     * Overriding this function changes the behavior of the {onlyRole} modifier.\n     *\n     * Format of the revert message is described in {_checkRole}.\n     *\n     * _Available since v4.6._\n     */\n    function _checkRole(bytes32 role) internal view virtual {\n        _checkRole(role, _msgSender());\n    }\n\n    /**\n     * @dev Revert with a standard message if `account` is missing `role`.\n     *\n     * The format of the revert reason is given by the following regular expression:\n     *\n     *  /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/\n     */\n    function _checkRole(bytes32 role, address account) internal view virtual {\n        if (!hasRole(role, account)) {\n            revert(\n                string(\n                    abi.encodePacked(\n                        \"AccessControl: account \",\n                        Strings.toHexString(account),\n                        \" is missing role \",\n                        Strings.toHexString(uint256(role), 32)\n                    )\n                )\n            );\n        }\n    }\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {\n        return _roles[role].adminRole;\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been revoked `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function renounceRole(bytes32 role, address account) public virtual override {\n        require(account == _msgSender(), \"AccessControl: can only renounce roles for self\");\n\n        _revokeRole(role, account);\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event. Note that unlike {grantRole}, this function doesn't perform any\n     * checks on the calling account.\n     *\n     * May emit a {RoleGranted} event.\n     *\n     * [WARNING]\n     * ====\n     * This function should only be called from the constructor when setting\n     * up the initial roles for the system.\n     *\n     * Using this function in any other way is effectively circumventing the admin\n     * system imposed by {AccessControl}.\n     * ====\n     *\n     * NOTE: This function is deprecated in favor of {_grantRole}.\n     */\n    function _setupRole(bytes32 role, address account) internal virtual {\n        _grantRole(role, account);\n    }\n\n    /**\n     * @dev Sets `adminRole` as ``role``'s admin role.\n     *\n     * Emits a {RoleAdminChanged} event.\n     */\n    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {\n        bytes32 previousAdminRole = getRoleAdmin(role);\n        _roles[role].adminRole = adminRole;\n        emit RoleAdminChanged(role, previousAdminRole, adminRole);\n    }\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleGranted} event.\n     */\n    function _grantRole(bytes32 role, address account) internal virtual {\n        if (!hasRole(role, account)) {\n            _roles[role].members[account] = true;\n            emit RoleGranted(role, account, _msgSender());\n        }\n    }\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * Internal function without access restriction.\n     *\n     * May emit a {RoleRevoked} event.\n     */\n    function _revokeRole(bytes32 role, address account) internal virtual {\n        if (hasRole(role, account)) {\n            _roles[role].members[account] = false;\n            emit RoleRevoked(role, account, _msgSender());\n        }\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev External interface of AccessControl declared to support ERC165 detection.\n */\ninterface IAccessControl {\n    /**\n     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`\n     *\n     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite\n     * {RoleAdminChanged} not being emitted signaling this.\n     *\n     * _Available since v3.1._\n     */\n    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);\n\n    /**\n     * @dev Emitted when `account` is granted `role`.\n     *\n     * `sender` is the account that originated the contract call, an admin role\n     * bearer except when using {AccessControl-_setupRole}.\n     */\n    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Emitted when `account` is revoked `role`.\n     *\n     * `sender` is the account that originated the contract call:\n     *   - if using `revokeRole`, it is the admin role bearer\n     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)\n     */\n    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);\n\n    /**\n     * @dev Returns `true` if `account` has been granted `role`.\n     */\n    function hasRole(bytes32 role, address account) external view returns (bool);\n\n    /**\n     * @dev Returns the admin role that controls `role`. See {grantRole} and\n     * {revokeRole}.\n     *\n     * To change a role's admin, use {AccessControl-_setRoleAdmin}.\n     */\n    function getRoleAdmin(bytes32 role) external view returns (bytes32);\n\n    /**\n     * @dev Grants `role` to `account`.\n     *\n     * If `account` had not been already granted `role`, emits a {RoleGranted}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function grantRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from `account`.\n     *\n     * If `account` had been granted `role`, emits a {RoleRevoked} event.\n     *\n     * Requirements:\n     *\n     * - the caller must have ``role``'s admin role.\n     */\n    function revokeRole(bytes32 role, address account) external;\n\n    /**\n     * @dev Revokes `role` from the calling account.\n     *\n     * Roles are often managed via {grantRole} and {revokeRole}: this function's\n     * purpose is to provide a mechanism for accounts to lose their privileges\n     * if they are compromised (such as when a trusted device is misplaced).\n     *\n     * If the calling account had been granted `role`, emits a {RoleRevoked}\n     * event.\n     *\n     * Requirements:\n     *\n     * - the caller must be `account`.\n     */\n    function renounceRole(bytes32 role, address account) external;\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\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(address indexed owner, address indexed spender, uint256 value);\n\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller's account to `to`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address to, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender's allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `from` to `to` using the\n     * allowance mechanism. `amount` is then deducted from the caller's\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(\n        address from,\n        address to,\n        uint256 amount\n    ) external returns (bool);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/Context.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (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"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/Strings.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./math/Math.sol\";\n\n/**\n * @dev String operations.\n */\nlibrary Strings {\n    bytes16 private constant _SYMBOLS = \"0123456789abcdef\";\n    uint8 private constant _ADDRESS_LENGTH = 20;\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` decimal representation.\n     */\n    function toString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            uint256 length = Math.log10(value) + 1;\n            string memory buffer = new string(length);\n            uint256 ptr;\n            /// @solidity memory-safe-assembly\n            assembly {\n                ptr := add(buffer, add(32, length))\n            }\n            while (true) {\n                ptr--;\n                /// @solidity memory-safe-assembly\n                assembly {\n                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))\n                }\n                value /= 10;\n                if (value == 0) break;\n            }\n            return buffer;\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.\n     */\n    function toHexString(uint256 value) internal pure returns (string memory) {\n        unchecked {\n            return toHexString(value, Math.log256(value) + 1);\n        }\n    }\n\n    /**\n     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.\n     */\n    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {\n        bytes memory buffer = new bytes(2 * length + 2);\n        buffer[0] = \"0\";\n        buffer[1] = \"x\";\n        for (uint256 i = 2 * length + 1; i > 1; --i) {\n            buffer[i] = _SYMBOLS[value & 0xf];\n            value >>= 4;\n        }\n        require(value == 0, \"Strings: hex length insufficient\");\n        return string(buffer);\n    }\n\n    /**\n     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.\n     */\n    function toHexString(address addr) internal pure returns (string memory) {\n        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n    /**\n     * @dev See {IERC165-supportsInterface}.\n     */\n    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n        return interfaceId == type(IERC165).interfaceId;\n    }\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n    /**\n     * @dev Returns true if this contract implements the interface defined by\n     * `interfaceId`. See the corresponding\n     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n     * to learn more about how these ids are created.\n     *\n     * This function call must use less than 30 000 gas.\n     */\n    function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"},{"file_path":"lib/openzeppelin-contracts/contracts/utils/math/Math.sol","source_code":"// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Standard math utilities missing in the Solidity language.\n */\nlibrary Math {\n    enum Rounding {\n        Down, // Toward negative infinity\n        Up, // Toward infinity\n        Zero // Toward zero\n    }\n\n    /**\n     * @dev Returns the largest of two numbers.\n     */\n    function max(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a > b ? a : b;\n    }\n\n    /**\n     * @dev Returns the smallest of two numbers.\n     */\n    function min(uint256 a, uint256 b) internal pure returns (uint256) {\n        return a < b ? a : b;\n    }\n\n    /**\n     * @dev Returns the average of two numbers. The result is rounded towards\n     * zero.\n     */\n    function average(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b) / 2 can overflow.\n        return (a & b) + (a ^ b) / 2;\n    }\n\n    /**\n     * @dev Returns the ceiling of the division of two numbers.\n     *\n     * This differs from standard division with `/` in that it rounds up instead\n     * of rounding down.\n     */\n    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {\n        // (a + b - 1) / b can overflow on addition, so we distribute.\n        return a == 0 ? 0 : (a - 1) / b + 1;\n    }\n\n    /**\n     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\n     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)\n     * with further edits by Uniswap Labs also under MIT license.\n     */\n    function mulDiv(\n        uint256 x,\n        uint256 y,\n        uint256 denominator\n    ) internal pure returns (uint256 result) {\n        unchecked {\n            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use\n            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256\n            // variables such that product = prod1 * 2^256 + prod0.\n            uint256 prod0; // Least significant 256 bits of the product\n            uint256 prod1; // Most significant 256 bits of the product\n            assembly {\n                let mm := mulmod(x, y, not(0))\n                prod0 := mul(x, y)\n                prod1 := sub(sub(mm, prod0), lt(mm, prod0))\n            }\n\n            // Handle non-overflow cases, 256 by 256 division.\n            if (prod1 == 0) {\n                return prod0 / denominator;\n            }\n\n            // Make sure the result is less than 2^256. Also prevents denominator == 0.\n            require(denominator > prod1);\n\n            ///////////////////////////////////////////////\n            // 512 by 256 division.\n            ///////////////////////////////////////////////\n\n            // Make division exact by subtracting the remainder from [prod1 prod0].\n            uint256 remainder;\n            assembly {\n                // Compute remainder using mulmod.\n                remainder := mulmod(x, y, denominator)\n\n                // Subtract 256 bit number from 512 bit number.\n                prod1 := sub(prod1, gt(remainder, prod0))\n                prod0 := sub(prod0, remainder)\n            }\n\n            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.\n            // See https://cs.stackexchange.com/q/138556/92363.\n\n            // Does not overflow because the denominator cannot be zero at this stage in the function.\n            uint256 twos = denominator & (~denominator + 1);\n            assembly {\n                // Divide denominator by twos.\n                denominator := div(denominator, twos)\n\n                // Divide [prod1 prod0] by twos.\n                prod0 := div(prod0, twos)\n\n                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.\n                twos := add(div(sub(0, twos), twos), 1)\n            }\n\n            // Shift in bits from prod1 into prod0.\n            prod0 |= prod1 * twos;\n\n            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such\n            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for\n            // four bits. That is, denominator * inv = 1 mod 2^4.\n            uint256 inverse = (3 * denominator) ^ 2;\n\n            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works\n            // in modular arithmetic, doubling the correct bits in each step.\n            inverse *= 2 - denominator * inverse; // inverse mod 2^8\n            inverse *= 2 - denominator * inverse; // inverse mod 2^16\n            inverse *= 2 - denominator * inverse; // inverse mod 2^32\n            inverse *= 2 - denominator * inverse; // inverse mod 2^64\n            inverse *= 2 - denominator * inverse; // inverse mod 2^128\n            inverse *= 2 - denominator * inverse; // inverse mod 2^256\n\n            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.\n            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is\n            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1\n            // is no longer required.\n            result = prod0 * inverse;\n            return result;\n        }\n    }\n\n    /**\n     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.\n     */\n    function mulDiv(\n        uint256 x,\n        uint256 y,\n        uint256 denominator,\n        Rounding rounding\n    ) internal pure returns (uint256) {\n        uint256 result = mulDiv(x, y, denominator);\n        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {\n            result += 1;\n        }\n        return result;\n    }\n\n    /**\n     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.\n     *\n     * Inspired by Henry S. Warren, Jr.'s \"Hacker's Delight\" (Chapter 11).\n     */\n    function sqrt(uint256 a) internal pure returns (uint256) {\n        if (a == 0) {\n            return 0;\n        }\n\n        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.\n        //\n        // We know that the \"msb\" (most significant bit) of our target number `a` is a power of 2 such that we have\n        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.\n        //\n        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`\n        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`\n        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`\n        //\n        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.\n        uint256 result = 1 << (log2(a) >> 1);\n\n        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,\n        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at\n        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision\n        // into the expected uint128 result.\n        unchecked {\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            result = (result + a / result) >> 1;\n            return min(result, a / result);\n        }\n    }\n\n    /**\n     * @notice Calculates sqrt(a), following the selected rounding direction.\n     */\n    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = sqrt(a);\n            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 2, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 128;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 64;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 32;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 16;\n            }\n            if (value >> 8 > 0) {\n                value >>= 8;\n                result += 8;\n            }\n            if (value >> 4 > 0) {\n                value >>= 4;\n                result += 4;\n            }\n            if (value >> 2 > 0) {\n                value >>= 2;\n                result += 2;\n            }\n            if (value >> 1 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log2(value);\n            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 10, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >= 10**64) {\n                value /= 10**64;\n                result += 64;\n            }\n            if (value >= 10**32) {\n                value /= 10**32;\n                result += 32;\n            }\n            if (value >= 10**16) {\n                value /= 10**16;\n                result += 16;\n            }\n            if (value >= 10**8) {\n                value /= 10**8;\n                result += 8;\n            }\n            if (value >= 10**4) {\n                value /= 10**4;\n                result += 4;\n            }\n            if (value >= 10**2) {\n                value /= 10**2;\n                result += 2;\n            }\n            if (value >= 10**1) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log10(value);\n            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);\n        }\n    }\n\n    /**\n     * @dev Return the log in base 256, rounded down, of a positive value.\n     * Returns 0 if given 0.\n     *\n     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.\n     */\n    function log256(uint256 value) internal pure returns (uint256) {\n        uint256 result = 0;\n        unchecked {\n            if (value >> 128 > 0) {\n                value >>= 128;\n                result += 16;\n            }\n            if (value >> 64 > 0) {\n                value >>= 64;\n                result += 8;\n            }\n            if (value >> 32 > 0) {\n                value >>= 32;\n                result += 4;\n            }\n            if (value >> 16 > 0) {\n                value >>= 16;\n                result += 2;\n            }\n            if (value >> 8 > 0) {\n                result += 1;\n            }\n        }\n        return result;\n    }\n\n    /**\n     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.\n     * Returns 0 if given 0.\n     */\n    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {\n        unchecked {\n            uint256 result = log256(value);\n            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);\n        }\n    }\n}\n"},{"file_path":"src/functions/TermCalculator.sol","source_code":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.24;\n\nfunction compoundValue(\n    uint256 value,\n    uint256 daysCount,\n    uint256 discountRate\n) pure returns (uint256) {\n    return (value * compoundFactor(daysCount, discountRate)) / 1e36;\n}\n\nfunction compoundFactor(\n    uint256 daysCount,\n    uint256 discountRate\n) pure returns (uint256) {\n    uint256 n = daysCount;\n    uint256 r = discountRate;\n\n    uint256 term1 = 1e36;\n    uint256 term2 = 1e24 * n * r;\n\n    if (n == 0) return term1 + term2;\n\n    uint256 term3 = (1e12 * (n * (n - 1) * r ** 2)) / 2;\n\n    if (n == 1) return term1 + term2 + term3;\n\n    uint256 term4 = (n * (n - 1) * (n - 2) * r ** 3) / 6;\n\n    return term1 + term2 + term3 + term4;\n}\n\nfunction discountValue(\n    uint256 value,\n    uint256 daysCount,\n    uint256 discountRate\n) pure returns (uint256) {\n    return ((value * 1e36) / compoundFactor(daysCount, discountRate));\n}\n\nfunction dayCount(\n    uint256 openTimestamp,\n    uint256 closeTimestamp\n) pure returns (uint256) {\n    if (openTimestamp > closeTimestamp) return 0;\n\n    return (closeTimestamp - openTimestamp) / 1 days;\n}\n"},{"file_path":"src/interfaces/ISavingModule.sol","source_code":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.24;\n\ninterface ISavingModule {\n    event Mint(\n        address indexed from,\n        address indexed to,\n        uint256 mintAmount,\n        uint256 burnAmount,\n        uint256 timestamp\n    );\n\n    event Redeem(\n        address indexed from,\n        address indexed to,\n        uint256 redeemAmount,\n        uint256 burnAmount,\n        uint256 timestamp\n    );\n\n    event Update(\n        uint256 compoundFactorAccum,\n        uint256 currentRate,\n        uint256 rate,\n        uint256 timestamp\n    );\n\n    function mint(address, address, uint256) external;\n\n    function rusdTotalLiability() external view returns (uint256);\n\n    function totalDebt() external view returns (uint256);\n}\n"},{"file_path":"src/interfaces/IToken.sol","source_code":"// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.24;\n\nimport {IERC20} from \"openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\";\n\ninterface IToken is IERC20 {\n    function mint(address, uint256) external;\n\n    function burnFrom(address, uint256) external;\n}\n"}],"certified":false,"conflicting_implementations":null,"abi":[{"inputs":[{"internalType":"address","name":"admin","type":"address"},{"internalType":"contract IToken","name":"rusd_","type":"address"},{"internalType":"contract IToken","name":"srusd_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burnAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burnAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"compoundFactorAccum","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"currentRate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Update","type":"event"},{"inputs":[],"name":"CONTROLLER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"compoundFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"compoundFactorAccum","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rusd","outputs":[{"internalType":"contract IToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rusdTotalLiability","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setRedeemFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"srusd","outputs":[{"internalType":"contract IToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rate","type":"uint256"}],"name":"update","outputs":[],"stateMutability":"nonpayable","type":"function"}],"is_changed_bytecode":false,"is_partially_verified":false,"constructor_args":"0x0000000000000000000000006ed13690efcc6f10217011e35cffef4516f458bb00000000000000000000000009d4214c03d01f49544c0448dbe3a27f768f2b34000000000000000000000000738d1115b90efa71ae468f1287fc864775e23a31"}