creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in\n * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].\n *\n * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by\n * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't\n * need to send a transaction, and thus is not required to hold Ether at all.\n *\n * ==== Security Considerations\n *\n * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature\n * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be\n * considered as an intention to spend the allowance in any specific way. The second is that because permits have\n * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should\n * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be\n * generally recommended is:\n *\n * ```solidity\n * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {\n *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}\n *     doThing(..., value);\n * }\n *\n * function doThing(..., uint256 value) public {\n *     token.safeTransferFrom(msg.sender, address(this), value);\n *     ...\n * }\n * ```\n *\n * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of\n * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also\n * {SafeERC20-safeTransferFrom}).\n *\n * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so\n * contracts should have entry points that don't rely on permit.\n */\ninterface IERC20Permit {\n    /**\n     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,\n     * given ``owner``'s signed approval.\n     *\n     * IMPORTANT: The same issues {IERC20-approve} has related to transaction\n     * ordering also apply here.\n     *\n     * Emits an {Approval} event.\n     *\n     * Requirements:\n     *\n     * - `spender` cannot be the zero address.\n     * - `deadline` must be a timestamp in the future.\n     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`\n     * over the EIP712-formatted function arguments.\n     * - the signature must use ``owner``'s current nonce (see {nonces}).\n     *\n     * For more information on the signature format, see the\n     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP\n     * section].\n     *\n     * CAUTION: See Security Considerations above.\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\n    /**\n     * @dev Returns the current nonce for `owner`. This value must be\n     * included whenever a signature is generated for {permit}.\n     *\n     * Every successful call to {permit} increases ``owner``'s nonce by one. This\n     * prevents a signature from being used multiple times.\n     */\n    function nonces(address owner) external view returns (uint256);\n\n    /**\n     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.\n     */\n    // solhint-disable-next-line func-name-mixedcase\n    function DOMAIN_SEPARATOR() external view returns (bytes32);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/IERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)\n\npragma solidity ^0.8.20;\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 value of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the value of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens as the allowance of `spender` over the\n     * 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 value) external returns (bool);\n\n    /**\n     * @dev Moves a `value` amount of tokens from `from` to `to` using the\n     * allowance mechanism. `value` 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(address from, address to, uint256 value) external returns (bool);\n}\n"
      },
      "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)\n\npragma solidity ^0.8.20;\n\nimport {IERC20} from \"../IERC20.sol\";\nimport {IERC20Permit} from \"../extensions/IERC20Permit.sol\";\nimport {Address} from \"../../../utils/Address.sol\";\n\n/**\n * @title SafeERC20\n * @dev Wrappers around ERC20 operations that throw on failure (when the token\n * contract returns false). Tokens that return no value (and instead revert or\n * throw on failure) are also supported, non-reverting calls are assumed to be\n * successful.\n * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,\n * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.\n */\nlibrary SafeERC20 {\n    using Address for address;\n\n    /**\n     * @dev An operation with an ERC20 token failed.\n     */\n    error SafeERC20FailedOperation(address token);\n\n    /**\n     * @dev Indicates a failed `decreaseAllowance` request.\n     */\n    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);\n\n    /**\n     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeTransfer(IERC20 token, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));\n    }\n\n    /**\n     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the\n     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.\n     */\n    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {\n        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));\n    }\n\n    /**\n     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful.\n     */\n    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {\n        uint256 oldAllowance = token.allowance(address(this), spender);\n        forceApprove(token, spender, oldAllowance + value);\n    }\n\n    /**\n     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no\n     * value, non-reverting calls are assumed to be successful.\n     */\n    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {\n        unchecked {\n            uint256 currentAllowance = token.allowance(address(this), spender);\n            if (currentAllowance < requestedDecrease) {\n                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);\n            }\n            forceApprove(token, spender, currentAllowance - requestedDecrease);\n        }\n    }\n\n    /**\n     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,\n     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval\n     * to be set to zero before setting it to a non-zero value, such as USDT.\n     */\n    function forceApprove(IERC20 token, address spender, uint256 value) internal {\n        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));\n\n        if (!_callOptionalReturnBool(token, approvalCall)) {\n            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));\n            _callOptionalReturn(token, approvalCall);\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     */\n    function _callOptionalReturn(IERC20 token, bytes memory data) private {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that\n        // the target address contains contract code and also asserts for success in the low-level call.\n\n        bytes memory returndata = address(token).functionCall(data);\n        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {\n            revert SafeERC20FailedOperation(address(token));\n        }\n    }\n\n    /**\n     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement\n     * on the return value: the return value is optional (but if data is returned, it must not be false).\n     * @param token The token targeted by the call.\n     * @param data The call data (encoded using abi.encode or one of its variants).\n     *\n     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.\n     */\n    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {\n        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since\n        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false\n        // and not revert is the subcall reverts.\n\n        (bool success, bytes memory returndata) = address(token).call(data);\n        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Address.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Collection of functions related to the address type\n */\nlibrary Address {\n    /**\n     * @dev The ETH balance of the account is not enough to perform the operation.\n     */\n    error AddressInsufficientBalance(address account);\n\n    /**\n     * @dev There's no code at `target` (it is not a contract).\n     */\n    error AddressEmptyCode(address target);\n\n    /**\n     * @dev A call to an address target failed. The target may have reverted.\n     */\n    error FailedInnerCall();\n\n    /**\n     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to\n     * `recipient`, forwarding all available gas and reverting on errors.\n     *\n     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost\n     * of certain opcodes, possibly making contracts go over the 2300 gas limit\n     * imposed by `transfer`, making them unable to receive funds via\n     * `transfer`. {sendValue} removes this limitation.\n     *\n     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].\n     *\n     * IMPORTANT: because control is transferred to `recipient`, care must be\n     * taken to not create reentrancy vulnerabilities. Consider using\n     * {ReentrancyGuard} or the\n     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].\n     */\n    function sendValue(address payable recipient, uint256 amount) internal {\n        if (address(this).balance < amount) {\n            revert AddressInsufficientBalance(address(this));\n        }\n\n        (bool success, ) = recipient.call{value: amount}(\"\");\n        if (!success) {\n            revert FailedInnerCall();\n        }\n    }\n\n    /**\n     * @dev Performs a Solidity function call using a low level `call`. A\n     * plain `call` is an unsafe replacement for a function call: use this\n     * function instead.\n     *\n     * If `target` reverts with a revert reason or custom error, it is bubbled\n     * up by this function (like regular Solidity function calls). However, if\n     * the call reverted with no returned reason, this function reverts with a\n     * {FailedInnerCall} error.\n     *\n     * Returns the raw returned data. To convert to the expected return value,\n     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].\n     *\n     * Requirements:\n     *\n     * - `target` must be a contract.\n     * - calling `target` with `data` must not revert.\n     */\n    function functionCall(address target, bytes memory data) internal returns (bytes memory) {\n        return functionCallWithValue(target, data, 0);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but also transferring `value` wei to `target`.\n     *\n     * Requirements:\n     *\n     * - the calling contract must have an ETH balance of at least `value`.\n     * - the called Solidity function must be `payable`.\n     */\n    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {\n        if (address(this).balance < value) {\n            revert AddressInsufficientBalance(address(this));\n        }\n        (bool success, bytes memory returndata) = target.call{value: value}(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a static call.\n     */\n    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.staticcall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],\n     * but performing a delegate call.\n     */\n    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {\n        (bool success, bytes memory returndata) = target.delegatecall(data);\n        return verifyCallResultFromTarget(target, success, returndata);\n    }\n\n    /**\n     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target\n     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an\n     * unsuccessful call.\n     */\n    function verifyCallResultFromTarget(\n        address target,\n        bool success,\n        bytes memory returndata\n    ) internal view returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            // only check if target is a contract if the call was successful and the return data is empty\n            // otherwise we already know that it was a contract\n            if (returndata.length == 0 && target.code.length == 0) {\n                revert AddressEmptyCode(target);\n            }\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the\n     * revert reason or with a default {FailedInnerCall} error.\n     */\n    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {\n        if (!success) {\n            _revert(returndata);\n        } else {\n            return returndata;\n        }\n    }\n\n    /**\n     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.\n     */\n    function _revert(bytes memory returndata) private pure {\n        // Look for revert reason and bubble it up if present\n        if (returndata.length > 0) {\n            // The easiest way to bubble the revert reason is using memory via assembly\n            /// @solidity memory-safe-assembly\n            assembly {\n                let returndata_size := mload(returndata)\n                revert(add(32, returndata), returndata_size)\n            }\n        } else {\n            revert FailedInnerCall();\n        }\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/ReentrancyGuard.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)\n\npragma solidity ^0.8.20;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot's contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler's defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction's gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant NOT_ENTERED = 1;\n    uint256 private constant ENTERED = 2;\n\n    uint256 private _status;\n\n    /**\n     * @dev Unauthorized reentrant call.\n     */\n    error ReentrancyGuardReentrantCall();\n\n    constructor() {\n        _status = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and making it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        _nonReentrantBefore();\n        _;\n        _nonReentrantAfter();\n    }\n\n    function _nonReentrantBefore() private {\n        // On the first call to nonReentrant, _status will be NOT_ENTERED\n        if (_status == ENTERED) {\n            revert ReentrancyGuardReentrantCall();\n        }\n\n        // Any calls to nonReentrant after this point will fail\n        _status = ENTERED;\n    }\n\n    function _nonReentrantAfter() private {\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = NOT_ENTERED;\n    }\n\n    /**\n     * @dev Returns true if the reentrancy guard is currently set to \"entered\", which indicates there is a\n     * `nonReentrant` function in the call stack.\n     */\n    function _reentrancyGuardEntered() internal view returns (bool) {\n        return _status == ENTERED;\n    }\n}\n"
      },
      "contracts/rewards/SDAOSimpleRewardAPI.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.25;\r\n\r\n// Interface for holding and managing emission of rewards for single reward token\r\n// - reward contract can collect accrued reward tokens\r\ninterface SDAOSimpleRewardAPI {\r\n\r\n    event UpdatedRewardEmission(uint256 amount, uint256 start, uint256 end);\r\n    event ReservedForUser(address user, address token, uint256 rewards);\r\n    event ClaimedByUser(address user, address token, uint256 amount);\r\n\r\n\r\n    struct UserInfo {\r\n        uint256 shares; // user shares\r\n        uint256 rewardFloor; // reward floor to calculate pending\r\n        uint256 reserved; // reserved for user\r\n    }\r\n    \r\n    struct RewardTokenInfo {\r\n          address rewardToken; // token to be distributed as rewards\r\n          uint256 balance; // total claimed and held for reward contract to collect\r\n          uint256 totalAmount; // total amount to be distributed during emissionPeriod\r\n          uint256 emissionPeriod; // emission period in seconds to distribute these reward tokens\r\n          uint256 startOfEmission; // start time of emissions\r\n          uint256 endOfEmission; // last time when these rewards are emitted\r\n          uint256 lastClaim; // last time when rewards have been claimed\r\n    }\r\n\r\n    function depositContract() external view returns (address);\r\n    function rewardToken() external view returns (address);\r\n    function getRewardInfo() external view returns (RewardTokenInfo memory);\r\n    \r\n    function changeUserShares(address _user, uint256 _newShares) external;\r\n    function pendingForUser(address _user) external view returns (uint256 pendingRewards);\r\n    function claimableForUser(address _user) external view returns (uint256 claimable);\r\n    function claimForUser(address _user) external returns (uint256 claimed);\r\n}\r\n"
      },
      "contracts/SDAOLockedStaking.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.25;\r\n\r\nimport \"@openzeppelin/contracts/utils/ReentrancyGuard.sol\";\r\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\r\nimport \"./utils/Clonable.sol\";\r\nimport \"./rewards/SDAOSimpleRewardAPI.sol\";\r\n\r\n/*\r\n * @title SDAO Locked Staking contract\r\n * @notice requirements:\r\n *  1. users lock their tokens for a certain period\r\n *  2. users can extend their locking period to increase their score\r\n *  3. users can withdraw after their tokens unlock or withdraw immediately deducting an early unlock fee\r\n *  4. protocol should be able to query per wallet the score calculated by locked amount times locking period\r\n *  5. users can claim rewards proportionaly in the ratio of their score in respect to totalScore\r\n */\r\ncontract SDAOLockedStaking is Clonable, ReentrancyGuard {\r\n    using SafeERC20 for IERC20;\r\n\r\n    uint256 constant public MAX_PERCENTAGE = 10000; // 100.00%\r\n    uint256 constant public MAX_EARLY_UNLOCK_FEE = 5000; // 50.00%\r\n    uint256 constant public MAX_LOCKING_PERIOD = 360 days;\r\n    uint256 constant public MAX_EARLY_UNLOCK_FEE_PER_DAY = 5; // 0.05%\r\n\r\n    // Info of each user.\r\n    struct UserInfo {\r\n        uint256 amount; // How many tokens the user has provided.\r\n        uint256 lockDate; // Last date when user locked funds\r\n        uint256 unlockDate; // Unlock date for user funds\r\n        uint256 score; // Aggregation of locked amount times locked days\r\n    }\r\n    // Info of each user that locks tokens.\r\n    mapping(address => UserInfo) public userInfo;\r\n\r\n    bool public depositsEnabled; // deposits are enabled\r\n    address public depositToken; // Address of deposit token contract.\r\n    address public rewardToken; // Address of reward token contract.\r\n    address public rewardsAPI;  // Rewards API module\r\n    address public zapperContract; // Zapper contract allowed to deposit on behalf of a user\r\n    uint256 public totalScore; // total score of all users\r\n    uint256 public earlyUnlockFees; // accumulated fees for early withdrawals\r\n    uint256 public earlyUnlockFeePerDay; // Default unlockFeePerDay 0.05%\r\n\r\n    event Deposit(address indexed user, uint256 amount, uint256 lockingPeriod);\r\n    event Withdraw(address indexed user, uint256 amount);\r\n    event Claimed(address indexed user, uint256 claimed);\r\n    event PaidEarlyUnlockFee(address indexed user, uint256 fee, uint256 secondsUntilUnlock);\r\n    event CollectedFees(address admin, uint256 fees);\r\n    event SetDepositsEnabled(address admin, bool depositsEnabled);\r\n    event SetEarlyUnlockFeePerDay(address admin, uint256 earlyUnlockFeePerDay);\r\n    event SetZapperContract(address admin, address zapperContract);\r\n    \r\n    error AlreadyInitialized();\r\n    error MissingToken();\r\n    error MissingAmount();\r\n    error MissingDepositToken();\r\n    error MissingRewardsAPI();\r\n    error MissingZapperContract();\r\n    error DepositsDisabled();\r\n    error DepositTokenRecoveryNotAllowed();\r\n    error SenderIsNotZapper(address sender, address zapper);\r\n    error ExceedsMaxEarlyUnlockFeePerDay(uint256 fee, uint maxFee);\r\n    error ExceedsMaxLockingPeriod(uint256 period, uint256 maxPeriod);\r\n    error WithdrawalRequestExceedsDeposited(uint256 requestedWithdrawal, uint256 currentBalance);\r\n    error RequestedUnlockDateBeforeCurrent(uint256 requestedUnlockDate, uint256 currentUnlockDate);\r\n\r\n    /*\r\n     * @dev initialize function to setup cloned instance\r\n     * @notice marked the initialize function as payable, because it costs less gas to execute,\r\n     * since the compiler does not have to add extra checks to ensure that a payment wasn't provided.\r\n     */\r\n    function initialize(address _depositToken, address _rewardsAPI) external payable onlyOwner {\r\n        if (depositToken != address(0)) {\r\n            revert AlreadyInitialized();\r\n        }\r\n        if (_depositToken == address(0)) {\r\n            revert MissingDepositToken();\r\n        }\r\n        if (_rewardsAPI == address(0)) {\r\n            revert MissingRewardsAPI();\r\n        }\r\n        depositToken = _depositToken;\r\n        rewardsAPI = _rewardsAPI;      \r\n        rewardToken = SDAOSimpleRewardAPI(_rewardsAPI).rewardToken();\r\n        earlyUnlockFeePerDay = 5;\r\n    }\r\n\r\n\r\n    /*\r\n     * @dev Deposit tokens\r\n     */\r\n    function deposit(uint256 _amount, uint256 _lockingPeriod) external nonReentrant {\r\n        uint256 _tokens_deposited = _deposit(_amount, msg.sender, msg.sender, _lockingPeriod);\r\n        emit Deposit(msg.sender, _tokens_deposited, _lockingPeriod);\r\n    }\r\n\r\n    /*\r\n     * @dev Deposit tokens from zapper contract on behalf of the user\r\n     */\r\n    function depositFor(address _recipient, uint256 _amount, uint256 _lockingPeriod) external nonReentrant {\r\n        if (msg.sender != zapperContract) {\r\n            revert SenderIsNotZapper(msg.sender, zapperContract);\r\n        }\r\n        uint256 _tokens_deposited = _deposit(_amount, msg.sender, _recipient, _lockingPeriod);\r\n        emit Deposit(msg.sender, _tokens_deposited, _lockingPeriod);\r\n    }\r\n\r\n    /*\r\n     * @dev Withdraw tokens\r\n     */\r\n    function withdraw(uint256 _amount) external nonReentrant {\r\n        _withdraw(_amount, msg.sender);\r\n        emit Withdraw(msg.sender, _amount);\r\n    }\r\n\r\n    /*\r\n     * @dev Pending rewards\r\n     */\r\n    function pending() external view returns(uint256) {\r\n        return SDAOSimpleRewardAPI(rewardsAPI).claimableForUser(msg.sender);\r\n    }\r\n\r\n    /*\r\n     * @dev Pending rewards for user\r\n     */\r\n    function pendingFor(address _user) external view returns(uint256) {\r\n        return SDAOSimpleRewardAPI(rewardsAPI).claimableForUser(_user);\r\n    }\r\n\r\n    /*\r\n     * @dev Claim rewards\r\n     */\r\n    function claim() external {\r\n        uint256 _claimed = SDAOSimpleRewardAPI(rewardsAPI).claimForUser(msg.sender);\r\n        emit Claimed(msg.sender, _claimed);\r\n    }\r\n\r\n    /*\r\n     * @dev withdraw and claim in one transaction\r\n     */\r\n    function withdrawAndClaim(uint256 _amount) external nonReentrant {\r\n        _withdraw(_amount, msg.sender);\r\n        emit Withdraw(msg.sender, _amount);\r\n        SDAOSimpleRewardAPI(rewardsAPI).claimForUser(msg.sender);\r\n    }\r\n  \r\n    /*\r\n     * @dev enable/disable new deposits\r\n     */\r\n    function setDepositsEnabled(bool _depositsEnabled) external onlyOwner {\r\n        depositsEnabled = _depositsEnabled;\r\n        emit SetDepositsEnabled(msg.sender, _depositsEnabled);\r\n    }\r\n\r\n    /**\r\n      * @dev change earlyUnlockFeePerDay\r\n      */\r\n    function setEarlyUnlockFeePerDay(uint256 _earlyUnlockFeePerDay) external onlyOwner {\r\n        if (_earlyUnlockFeePerDay > MAX_EARLY_UNLOCK_FEE_PER_DAY) {\r\n            revert ExceedsMaxEarlyUnlockFeePerDay(_earlyUnlockFeePerDay, MAX_EARLY_UNLOCK_FEE_PER_DAY);\r\n        }\r\n        earlyUnlockFeePerDay = _earlyUnlockFeePerDay;\r\n        emit SetEarlyUnlockFeePerDay(msg.sender, _earlyUnlockFeePerDay);\r\n    }\r\n  \r\n    /*\r\n     * @dev Register zapper contract\r\n     */\r\n    function setZapperContract(address _zapperContract) external onlyOwner {\r\n        if (_zapperContract == address(0)) {\r\n            revert MissingZapperContract();\r\n        }\r\n        zapperContract = _zapperContract;\r\n        emit SetZapperContract(msg.sender, _zapperContract);\r\n    }\r\n\r\n    /**\r\n      * @dev recover unsupported tokens\r\n      */\r\n    function recoverUnsupportedTokens(address _token, uint256 amount, address to) external onlyOwner {\r\n        if (_token == address(0)) {\r\n            revert MissingToken();\r\n        }\r\n        if (_token == depositToken) {\r\n            revert DepositTokenRecoveryNotAllowed();\r\n        }\r\n        IERC20(_token).safeTransfer(to, amount);\r\n    }\r\n  \r\n    /**\r\n      * @dev collect accumulated early unlock fees\r\n      */\r\n    function collectFees() external onlyOwner {\r\n        uint256 fees = earlyUnlockFees;\r\n        earlyUnlockFees = 0;\r\n        IERC20(depositToken).safeTransfer(msg.sender, fees);\r\n        emit CollectedFees(msg.sender, fees);\r\n    }\r\n\r\n    /*\r\n     * @dev internal deposit function\r\n     */\r\n    function _deposit(uint256 _amount, \r\n                      address _depositor, \r\n                      address _recipient, \r\n                      uint256 _lockingPeriod) internal returns (uint256 tokensDeposited) {\r\n        if (_lockingPeriod > MAX_LOCKING_PERIOD) {\r\n            revert ExceedsMaxLockingPeriod(_lockingPeriod, MAX_LOCKING_PERIOD);\r\n        }\r\n        if (!depositsEnabled) {\r\n            revert DepositsDisabled();\r\n        }\r\n        UserInfo memory user = userInfo[_recipient];\r\n        if (_amount == 0 && user.amount == 0) {\r\n            revert MissingAmount();\r\n        }\r\n        uint256 newEndPeriod = block.timestamp + _lockingPeriod;\r\n        if (newEndPeriod < user.unlockDate) {\r\n            revert RequestedUnlockDateBeforeCurrent(newEndPeriod, user.unlockDate);\r\n        }\r\n        uint256 deltaScore;\r\n        \r\n        if (_amount > 0) {\r\n            IERC20 _depositToken = IERC20(depositToken);\r\n            uint256 _before = _depositToken.balanceOf(address(this));\r\n            _depositToken.safeTransferFrom(_depositor, address(this), _amount);\r\n            tokensDeposited = _depositToken.balanceOf(address(this)) - _before;\r\n        } \r\n\r\n        if (user.amount > 0) {\r\n            // extend unlock date\r\n            uint256 extensionPeriod = newEndPeriod - user.unlockDate;\r\n            deltaScore += user.amount * extensionPeriod;\r\n        }\r\n\r\n        // handle new deposit\r\n        deltaScore += tokensDeposited * _lockingPeriod;\r\n      \r\n        totalScore += deltaScore;\r\n        user.score += deltaScore;\r\n        SDAOSimpleRewardAPI(rewardsAPI).changeUserShares(_recipient, user.score);\r\n        user.amount += tokensDeposited;\r\n        user.lockDate = block.timestamp;\r\n        user.unlockDate = newEndPeriod;\r\n        userInfo[_recipient] = user;\r\n    }\r\n\r\n    /*\r\n     * @dev internal withdraw function\r\n     */\r\n    function _withdraw(uint256 _amount, address _user) internal {\r\n        UserInfo storage user = userInfo[_user];\r\n        if (user.amount < _amount) {\r\n            revert WithdrawalRequestExceedsDeposited(_amount, user.amount);\r\n        }\r\n        if (_amount == 0) {\r\n            revert MissingAmount();\r\n        }\r\n        uint256 originalUnlockDate = user.unlockDate;\r\n        uint256 deltaScore;\r\n        // when unlock date has passed\r\n        if (originalUnlockDate < block.timestamp) {\r\n            // extend unlock date\r\n            uint256 extensionPeriod = block.timestamp - originalUnlockDate; \r\n            deltaScore = user.amount * extensionPeriod;\r\n            totalScore += deltaScore;\r\n            user.score += deltaScore;\r\n            user.unlockDate = block.timestamp;\r\n        }\r\n        uint256 withdrawalAmount = _amount;\r\n        // score will be reduced proportional to the amount withdrawn\r\n        deltaScore = user.score * withdrawalAmount / user.amount;\r\n        // apply withdrawal amount\r\n        user.amount -= withdrawalAmount;\r\n        // update scores\r\n        totalScore -= deltaScore;\r\n        user.score -= deltaScore;\r\n        SDAOSimpleRewardAPI(rewardsAPI).changeUserShares(_user, user.score);\r\n        // when not yet completely unlocked, apply early unlock fee\r\n        if (user.unlockDate > block.timestamp) {\r\n            uint256 earlyUnlockFee = withdrawalAmount * (originalUnlockDate - block.timestamp) * earlyUnlockFeePerDay \r\n                                                      / 1 days                                 / MAX_PERCENTAGE;\r\n            earlyUnlockFees += earlyUnlockFee;\r\n            withdrawalAmount -= earlyUnlockFee;\r\n            emit PaidEarlyUnlockFee(_user, earlyUnlockFee, originalUnlockDate - block.timestamp);\r\n        }\r\n        // when completely withdrawn, reset unlockdate\r\n        if (user.amount == 0) {\r\n            user.unlockDate = block.timestamp;\r\n        }\r\n        IERC20(depositToken).safeTransfer(_user, withdrawalAmount);\r\n    }\r\n  \r\n}\r\n"
      },
      "contracts/utils/Clonable.sol": {
        "content": "// SPDX-License-Identifier: MIT\r\npragma solidity 0.8.25;\r\n\r\ncontract Clonable {\r\n    address private _owner;\r\n\r\n    event Cloned(address newInstance);\r\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\r\n\r\n    error CallerIsNotOwner();\r\n    error AlreadyInitializedOwner();\r\n    error MissingOwner();\r\n    \r\n    /*\r\n     * @notice marked the constructor function as payable, because it costs less gas to execute,\r\n     * since the compiler does not have to add extra checks to ensure that a payment wasn't provided.\r\n     * A constructor can safely be marked as payable, since only the deployer would be able to pass funds, \r\n     * and the project itself would not pass any funds.\r\n     */\r\n    constructor() payable {\r\n        _owner = msg.sender;\r\n    }\r\n    \r\n    function owner() external view returns(address) {\r\n        return _owner;\r\n    }\r\n\r\n    modifier onlyOwner() {\r\n        if (_owner != msg.sender) {\r\n            revert CallerIsNotOwner();\r\n        }\r\n        _;\r\n    }\r\n\r\n    function setOwnerAfterClone(address initialOwner) external {\r\n        if (_owner != address(0)) {\r\n            revert AlreadyInitializedOwner();\r\n        }\r\n        _owner = initialOwner;\r\n        emit OwnershipTransferred(address(0), initialOwner);\r\n    }\r\n\r\n    function transferOwnership(address newOwner) external onlyOwner {\r\n        if (newOwner == address(0)) {\r\n            revert MissingOwner();\r\n        }\r\n        emit OwnershipTransferred(_owner, newOwner);\r\n        _owner = newOwner;\r\n    }\r\n\r\n    function clone(address newOwner) public returns (address newInstance){\r\n        if (newOwner == address(0)) {\r\n            revert MissingOwner();\r\n        }\r\n        // Copied from https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol\r\n        bytes20 addressBytes = bytes20(address(this));\r\n        assembly {\r\n            // EIP-1167 bytecode\r\n            let clone_code := mload(0x40)\r\n            mstore(clone_code, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)\r\n            mstore(add(clone_code, 0x14), addressBytes)\r\n            mstore(add(clone_code, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)\r\n            newInstance := create(0, clone_code, 0x37)\r\n        }\r\n        emit Cloned(newInstance);\r\n        Clonable(newInstance).setOwnerAfterClone(newOwner);\r\n    }\r\n    \r\n    function getClone() external returns (address) {\r\n        return clone(msg.sender);\r\n    }\r\n}\r\n\r\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 1000
      },
      "evmVersion": "paris",
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}