creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/TokenUtilityAccounting.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity 0.8.19;\n\nimport \"./utils/Ownable.sol\";\n\n/**\n * @title TokenUtilityAccounting\n * @author NFTfi\n * @dev\n */\ncontract TokenUtilityAccounting is Ownable {\n    mapping(address => bool) public tokenLocks;\n\n    mapping(address => uint256) public weightedAvgLockTimes;\n    mapping(address => uint256) public amounts;\n\n    // these two only needed if we wanted to distribute a finite,\n    // given amount of rewards proportionally for locking times and amounts acrued\n    uint256 public totalWeightedAvgLockTime;\n    uint256 public totalAmount;\n\n    event Update(\n        address indexed _user,\n        uint256 _weightedAvgLockTime,\n        uint256 _acruedUserAmount,\n        uint256 _totalWeightedAvgLockTime,\n        uint256 _totalAmount\n    );\n\n    constructor(address _admin, address[] memory _tokenLockAddresses) Ownable(_admin) {\n        _addTokenLocks(_tokenLockAddresses);\n    }\n\n    modifier onlyTokenLock() {\n        require(tokenLocks[msg.sender], \"Only token lock\");\n        _;\n    }\n\n    function lock(address _user, uint256 _amount) external onlyTokenLock {\n        _updateUserWeightedAvgLockTime(_user, _amount);\n        _updateTotalWeightedAvgLockTime(_amount);\n        amounts[_user] += _amount;\n        totalAmount += _amount;\n        emit Update(_user, weightedAvgLockTimes[_user], amounts[_user], totalWeightedAvgLockTime, totalAmount);\n    }\n\n    function unlock(address _user, uint256 _amount) external onlyTokenLock {\n        amounts[_user] -= _amount;\n        totalAmount -= _amount;\n        emit Update(_user, weightedAvgLockTimes[_user], amounts[_user], totalWeightedAvgLockTime, totalAmount);\n    }\n\n    /**\n     * @dev updates weighted avg lock time for a given user based on the added amount\n     * @param _user -\n     * @param _amount - amount added\n     */\n    function _updateUserWeightedAvgLockTime(address _user, uint256 _amount) internal {\n        weightedAvgLockTimes[_user] = _calculateWeightedAvgLockTime(\n            _amount,\n            amounts[_user],\n            weightedAvgLockTimes[_user]\n        );\n    }\n\n    /**\n     * @dev updates weighted avg lock time for the whole system based on the added amount\n     * @param _amount - amount added\n     */\n    function _updateTotalWeightedAvgLockTime(uint256 _amount) internal {\n        totalWeightedAvgLockTime = _calculateWeightedAvgLockTime(_amount, totalAmount, totalWeightedAvgLockTime);\n    }\n\n    /**\n     * @dev calculates weightedAvgMultiplier virtual timestamp value with\n     * a new data point of token _amount weight and the current time\n     * This function is either called by _updateAvgMultiplierStart or has to be called after\n     * an explicit stake() or a deleteWithdrawRequest(), or any other possible instances,\n     * The function takes the existing average and it's weight (existing balance) then calculates\n     * it with the new value and weight with a weighted avg calculation between the 2 datapoints.\n     * @param _amount - amount added\n     * @param _oldAmount - cumulative amount before\n     * @param _oldWeightedAvgLockTime -\n     */\n    function _calculateWeightedAvgLockTime(\n        uint256 _amount,\n        uint256 _oldAmount,\n        uint256 _oldWeightedAvgLockTime\n    ) internal view returns (uint256) {\n        if (_oldAmount == 0 || _oldWeightedAvgLockTime == 0) {\n            // if we are at initial state with just 1 datapoint\n            return block.timestamp;\n        } else {\n            uint256 totalWeight = _oldAmount + _amount;\n            // weighted avg calculation between the old value and the new lock timestamp\n            return (_oldAmount * _oldWeightedAvgLockTime + _amount * block.timestamp) / totalWeight;\n        }\n    }\n\n    function _addTokenLocks(address[] memory _tokenLockAddresses) internal {\n        for (uint256 index = 0; index < _tokenLockAddresses.length; ++index) {\n            tokenLocks[_tokenLockAddresses[index]] = true;\n        }\n    }\n\n    function addTokenLocks(address[] memory _tokenLockAddresses) external onlyOwner {\n        _addTokenLocks(_tokenLockAddresses);\n    }\n\n    function removeTokenLocks(address[] memory _tokenLockAddresses) external onlyOwner {\n        for (uint256 index = 0; index < _tokenLockAddresses.length; ++index) {\n            tokenLocks[_tokenLockAddresses[index]] = false;\n        }\n    }\n}\n"
      },
      "contracts/utils/Ownable.sol": {
        "content": "// SPDX-License-Identifier: BUSL-1.1\n\npragma solidity 0.8.19;\n\nimport \"@openzeppelin/contracts/utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n *\n * Modified version from openzeppelin/contracts/access/Ownable.sol that allows to\n * initialize the owner using a parameter in the constructor\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    address private _ownerCandidate;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n        _;\n    }\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor(address _initialOwner) {\n        _setOwner(_initialOwner);\n    }\n\n    /**\n     * @dev Requests transferring ownership of the contract to a new account (`_newOwnerCandidate`).\n     * Can only be called by the current owner.\n     */\n    function requestTransferOwnership(address _newOwnerCandidate) public virtual onlyOwner {\n        require(_newOwnerCandidate != address(0), \"Ownable: new owner is the zero address\");\n        _ownerCandidate = _newOwnerCandidate;\n    }\n\n    function acceptTransferOwnership() public virtual {\n        require(_ownerCandidate == _msgSender(), \"Ownable: not owner candidate\");\n        _setOwner(_ownerCandidate);\n        delete _ownerCandidate;\n    }\n\n    function cancelTransferOwnership() public virtual onlyOwner {\n        delete _ownerCandidate;\n    }\n\n    function rejectTransferOwnership() public virtual {\n        require(_ownerCandidate == _msgSender(), \"Ownable: not owner candidate\");\n        delete _ownerCandidate;\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Sets the owner.\n     */\n    function _setOwner(address _newOwner) internal {\n        address oldOwner = _owner;\n        _owner = _newOwner;\n        emit OwnershipTransferred(oldOwner, _newOwner);\n    }\n}\n"
      },
      "@openzeppelin/contracts/utils/Context.sol": {
        "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        return msg.data;\n    }\n\n    function _contextSuffixLength() internal view virtual returns (uint256) {\n        return 0;\n    }\n}\n"
      }
    },
    "settings": {
      "metadata": {
        "bytecodeHash": "none",
        "useLiteralContent": true
      },
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      },
      "libraries": {}
    }
  }}