ETH Price: $2,064.24 (+3.24%)

Transaction Decoder

Block:
16017547 at Nov-21-2022 09:39:23 AM +UTC
Transaction Fee:
0.000544971380050526 ETH $1.12
Gas Used:
47,398 Gas / 11.497771637 Gwei

Emitted Events:

120 CartesiToken.Transfer( from=[Receiver] StakingPoolImpl, to=[Sender] 0xaecb945c0187aa0b78380ed274b2a8795956ce1e, value=135117932994462802209010 )
121 StakingPoolImpl.Withdraw( user=[Sender] 0xaecb945c0187aa0b78380ed274b2a8795956ce1e, amount=135117932994462802209010 )

Account State Difference:

  Address   Before After State Difference Code
0x2942Aa43...d29629a9d
0x491604c0...A5DD06B5D
0xAeCb945C...95956CE1e
3.563697877777393203 Eth
Nonce: 7
3.563152906397342677 Eth
Nonce: 8
0.000544971380050526
(bloXroute: Max Profit Builder)
2.356145535678198782 Eth2.356216632678198782 Eth0.000071097

Execution Trace

StakingPoolImpl.withdraw( _amount=135117932994462802209010 )
  • StakingPoolImpl.withdraw( _amount=135117932994462802209010 )
    • CartesiToken.transfer( recipient=0xAeCb945C0187AA0B78380ED274B2A8795956CE1e, amount=135117932994462802209010 ) => ( True )
      File 1 of 3: StakingPoolImpl
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Interface PoS
      pragma solidity >=0.7.0 <0.9.0;
      interface IPoS {
          /// @notice Produce a block
          /// @param _index the index of the instance of pos you want to interact with
          /// @dev this function can only be called by a worker, user never calls it directly
          function produceBlock(uint256 _index) external returns (bool);
          /// @notice Get reward manager address
          /// @param _index index of instance
          /// @return address of instance's RewardManager
          function getRewardManagerAddress(uint256 _index)
              external
              view
              returns (address);
          /// @notice Get block selector address
          /// @param _index index of instance
          /// @return address of instance's block selector
          function getBlockSelectorAddress(uint256 _index)
              external
              view
              returns (address);
          /// @notice Get block selector index
          /// @param _index index of instance
          /// @return index of instance's block selector
          function getBlockSelectorIndex(uint256 _index)
              external
              view
              returns (uint256);
          /// @notice Get staking address
          /// @param _index index of instance
          /// @return address of instance's staking contract
          function getStakingAddress(uint256 _index) external view returns (address);
          /// @notice Get state of a particular instance
          /// @param _index index of instance
          /// @param _user address of user
          /// @return bool if user is eligible to produce next block
          /// @return address of user that was chosen to build the block
          /// @return current reward paid by the network for that block
          function getState(uint256 _index, address _user)
              external
              view
              returns (
                  bool,
                  address,
                  uint256
              );
          function terminate(uint256 _index) external;
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Interface RewardManager
      pragma solidity >=0.7.0 <0.9.0;
      interface IRewardManager {
          /// @notice Rewards address
          /// @param _address address be rewarded
          /// @param _amount reward
          /// @dev only the pos contract can call this
          function reward(address _address, uint256 _amount) external;
          /// @notice Get RewardManager's balance
          function getBalance() external view returns (uint256);
          /// @notice Get current reward amount
          function getCurrentReward() external view returns (uint256);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Interface Staking
      pragma solidity >=0.7.0 <0.9.0;
      interface IStaking {
          /// @notice Returns total amount of tokens counted as stake
          /// @param _userAddress user to retrieve staked balance from
          /// @return finalized staked of _userAddress
          function getStakedBalance(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Returns the timestamp when next deposit can be finalized
          /// @return timestamp of when finalizeStakes() is callable
          function getMaturingTimestamp(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Returns the timestamp when next withdraw can be finalized
          /// @return timestamp of when finalizeWithdraw() is callable
          function getReleasingTimestamp(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Returns the balance waiting/ready to be matured
          /// @return amount that will get staked after finalization
          function getMaturingBalance(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Returns the balance waiting/ready to be released
          /// @return amount that will get withdrew after finalization
          function getReleasingBalance(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Deposit CTSI to be staked. The money will turn into staked
          ///         balance after timeToStake days
          /// @param _amount The amount of tokens that are gonna be deposited.
          function stake(uint256 _amount) external;
          /// @notice Remove tokens from staked balance. The money can
          ///         be released after timeToRelease seconds, if the
          ///         function withdraw is called.
          /// @param _amount The amount of tokens that are gonna be unstaked.
          function unstake(uint256 _amount) external;
          /// @notice Transfer tokens to user's wallet.
          /// @param _amount The amount of tokens that are gonna be transferred.
          function withdraw(uint256 _amount) external;
          // events
          /// @notice CTSI tokens were deposited, they count as stake after _maturationDate
          /// @param user address of msg.sender
          /// @param amount amount deposited for staking
          /// @param maturationDate date when the stake can be finalized
          event Stake(address indexed user, uint256 amount, uint256 maturationDate);
          /// @notice Unstake tokens, moving them to releasing structure
          /// @param user address of msg.sender
          /// @param amount amount of tokens to be released
          /// @param maturationDate date when the tokens can be withdrew
          event Unstake(address indexed user, uint256 amount, uint256 maturationDate);
          /// @notice Withdraw process was finalized
          /// @param user address of msg.sender
          /// @param amount amount of tokens withdrawn
          event Withdraw(address indexed user, uint256 amount);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Interface WorkerManager
      /// @author Danilo Tuler
      pragma solidity >=0.7.0 <0.9.0;
      interface IWorkerManagerAuthManager {
          /// @notice Asks the worker to work for the sender. Sender needs to pay something.
          /// @param workerAddress address of the worker
          function hire(address payable workerAddress) external payable;
          /// @notice Called by the user to cancel a job offer
          /// @param workerAddress address of the worker node
          function cancelHire(address workerAddress) external;
          /// @notice Called by the user to retire his worker.
          /// @param workerAddress address of the worker to be retired
          /// @dev this also removes all authorizations in place
          function retire(address payable workerAddress) external;
          /// @notice Gives worker permission to act on a DApp
          /// @param _workerAddress address of the worker node to given permission
          /// @param _dappAddress address of the dapp that permission will be given to
          function authorize(address _workerAddress, address _dappAddress) external;
          /// @notice Called by the worker to accept the job
          function acceptJob() external;
          /// @notice Called by the worker to reject a job offer
          function rejectJob() external payable;
      }
      pragma solidity >=0.8.4;
      interface ENS {
          // Logged when the owner of a node assigns a new owner to a subnode.
          event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
          // Logged when the owner of a node transfers ownership to a new account.
          event Transfer(bytes32 indexed node, address owner);
          // Logged when the resolver for a node changes.
          event NewResolver(bytes32 indexed node, address resolver);
          // Logged when the TTL of a node changes
          event NewTTL(bytes32 indexed node, uint64 ttl);
          // Logged when an operator is added or removed.
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual;
          function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual;
          function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external virtual returns(bytes32);
          function setResolver(bytes32 node, address resolver) external virtual;
          function setOwner(bytes32 node, address owner) external virtual;
          function setTTL(bytes32 node, uint64 ttl) external virtual;
          function setApprovalForAll(address operator, bool approved) external virtual;
          function owner(bytes32 node) external virtual view returns (address);
          function resolver(bytes32 node) external virtual view returns (address);
          function ttl(bytes32 node) external virtual view returns (uint64);
          function recordExists(bytes32 node) external virtual view returns (bool);
          function isApprovedForAll(address owner, address operator) external virtual view returns (bool);
      }
      pragma solidity >=0.8.4;
      import "./ENS.sol";
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "../root/Controllable.sol";
      abstract contract NameResolver {
          function setName(bytes32 node, string memory name) public virtual;
      }
      bytes32 constant lookup = 0x3031323334353637383961626364656600000000000000000000000000000000;
      bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
      // namehash('addr.reverse')
      contract ReverseRegistrar is Ownable, Controllable {
          ENS public ens;
          NameResolver public defaultResolver;
          event ReverseClaimed(address indexed addr, bytes32 indexed node);
          /**
           * @dev Constructor
           * @param ensAddr The address of the ENS registry.
           * @param resolverAddr The address of the default reverse resolver.
           */
          constructor(ENS ensAddr, NameResolver resolverAddr) {
              ens = ensAddr;
              defaultResolver = resolverAddr;
              // Assign ownership of the reverse record to our deployer
              ReverseRegistrar oldRegistrar = ReverseRegistrar(
                  ens.owner(ADDR_REVERSE_NODE)
              );
              if (address(oldRegistrar) != address(0x0)) {
                  oldRegistrar.claim(msg.sender);
              }
          }
          modifier authorised(address addr) {
              require(
                  addr == msg.sender ||
                      controllers[msg.sender] ||
                      ens.isApprovedForAll(addr, msg.sender) ||
                      ownsContract(addr),
                  "Caller is not a controller or authorised by address or the address itself"
              );
              _;
          }
          /**
           * @dev Transfers ownership of the reverse ENS record associated with the
           *      calling account.
           * @param owner The address to set as the owner of the reverse record in ENS.
           * @return The ENS node hash of the reverse record.
           */
          function claim(address owner) public returns (bytes32) {
              return _claimWithResolver(msg.sender, owner, address(0x0));
          }
          /**
           * @dev Transfers ownership of the reverse ENS record associated with the
           *      calling account.
           * @param addr The reverse record to set
           * @param owner The address to set as the owner of the reverse record in ENS.
           * @return The ENS node hash of the reverse record.
           */
          function claimForAddr(address addr, address owner)
              public
              authorised(addr)
              returns (bytes32)
          {
              return _claimWithResolver(addr, owner, address(0x0));
          }
          /**
           * @dev Transfers ownership of the reverse ENS record associated with the
           *      calling account.
           * @param owner The address to set as the owner of the reverse record in ENS.
           * @param resolver The address of the resolver to set; 0 to leave unchanged.
           * @return The ENS node hash of the reverse record.
           */
          function claimWithResolver(address owner, address resolver)
              public
              returns (bytes32)
          {
              return _claimWithResolver(msg.sender, owner, resolver);
          }
          /**
           * @dev Transfers ownership of the reverse ENS record specified with the
           *      address provided
           * @param addr The reverse record to set
           * @param owner The address to set as the owner of the reverse record in ENS.
           * @param resolver The address of the resolver to set; 0 to leave unchanged.
           * @return The ENS node hash of the reverse record.
           */
          function claimWithResolverForAddr(
              address addr,
              address owner,
              address resolver
          ) public authorised(addr) returns (bytes32) {
              return _claimWithResolver(addr, owner, resolver);
          }
          /**
           * @dev Sets the `name()` record for the reverse ENS record associated with
           * the calling account. First updates the resolver to the default reverse
           * resolver if necessary.
           * @param name The name to set for this address.
           * @return The ENS node hash of the reverse record.
           */
          function setName(string memory name) public returns (bytes32) {
              bytes32 node = _claimWithResolver(
                  msg.sender,
                  address(this),
                  address(defaultResolver)
              );
              defaultResolver.setName(node, name);
              return node;
          }
          /**
           * @dev Sets the `name()` record for the reverse ENS record associated with
           * the account provided. First updates the resolver to the default reverse
           * resolver if necessary.
           * Only callable by controllers and authorised users
           * @param addr The reverse record to set
           * @param owner The owner of the reverse node
           * @param name The name to set for this address.
           * @return The ENS node hash of the reverse record.
           */
          function setNameForAddr(
              address addr,
              address owner,
              string memory name
          ) public authorised(addr) returns (bytes32) {
              bytes32 node = _claimWithResolver(
                  addr,
                  address(this),
                  address(defaultResolver)
              );
              defaultResolver.setName(node, name);
              ens.setSubnodeOwner(ADDR_REVERSE_NODE, sha3HexAddress(addr), owner);
              return node;
          }
          /**
           * @dev Returns the node hash for a given account's reverse records.
           * @param addr The address to hash
           * @return The ENS node hash.
           */
          function node(address addr) public pure returns (bytes32) {
              return
                  keccak256(
                      abi.encodePacked(ADDR_REVERSE_NODE, sha3HexAddress(addr))
                  );
          }
          /**
           * @dev An optimised function to compute the sha3 of the lower-case
           *      hexadecimal representation of an Ethereum address.
           * @param addr The address to hash
           * @return ret The SHA3 hash of the lower-case hexadecimal encoding of the
           *         input address.
           */
          function sha3HexAddress(address addr) private pure returns (bytes32 ret) {
              assembly {
                  for {
                      let i := 40
                  } gt(i, 0) {
                  } {
                      i := sub(i, 1)
                      mstore8(i, byte(and(addr, 0xf), lookup))
                      addr := div(addr, 0x10)
                      i := sub(i, 1)
                      mstore8(i, byte(and(addr, 0xf), lookup))
                      addr := div(addr, 0x10)
                  }
                  ret := keccak256(0, 40)
              }
          }
          /* Internal functions */
          function _claimWithResolver(
              address addr,
              address owner,
              address resolver
          ) internal returns (bytes32) {
              bytes32 label = sha3HexAddress(addr);
              bytes32 node = keccak256(abi.encodePacked(ADDR_REVERSE_NODE, label));
              address currentResolver = ens.resolver(node);
              bool shouldUpdateResolver = (resolver != address(0x0) &&
                  resolver != currentResolver);
              address newResolver = shouldUpdateResolver ? resolver : currentResolver;
              ens.setSubnodeRecord(ADDR_REVERSE_NODE, label, owner, newResolver, 0);
              emit ReverseClaimed(addr, node);
              return node;
          }
          function ownsContract(address addr) internal view returns (bool) {
              try Ownable(addr).owner() returns (address owner) {
                  return owner == msg.sender;
              } catch {
                  return false;
              }
          }
      }
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/access/Ownable.sol";
      contract Controllable is Ownable {
          mapping(address => bool) public controllers;
          event ControllerChanged(address indexed controller, bool enabled);
          modifier onlyController {
              require(
                  controllers[msg.sender],
                  "Controllable: Caller is not a controller"
              );
              _;
          }
          function setController(address controller, bool enabled) public onlyOwner {
              controllers[controller] = enabled;
              emit ControllerChanged(controller, enabled);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../utils/ContextUpgradeable.sol";
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          function __Ownable_init() internal initializer {
              __Context_init_unchained();
              __Ownable_init_unchained();
          }
          function __Ownable_init_unchained() internal initializer {
              _setOwner(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _setOwner(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _setOwner(newOwner);
          }
          function _setOwner(address newOwner) private {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       */
      abstract contract Initializable {
          /**
           * @dev Indicates that the contract has been initialized.
           */
          bool private _initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private _initializing;
          /**
           * @dev Modifier to protect an initializer function from being invoked twice.
           */
          modifier initializer() {
              require(_initializing || !_initialized, "Initializable: contract is already initialized");
              bool isTopLevelCall = !_initializing;
              if (isTopLevelCall) {
                  _initializing = true;
                  _initialized = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../utils/ContextUpgradeable.sol";
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module which allows children to implement an emergency stop
       * mechanism that can be triggered by an authorized account.
       *
       * This module is used through inheritance. It will make available the
       * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
       * the functions of your contract. Note that they will not be pausable by
       * simply including this module, only once the modifiers are put in place.
       */
      abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
          /**
           * @dev Emitted when the pause is triggered by `account`.
           */
          event Paused(address account);
          /**
           * @dev Emitted when the pause is lifted by `account`.
           */
          event Unpaused(address account);
          bool private _paused;
          /**
           * @dev Initializes the contract in unpaused state.
           */
          function __Pausable_init() internal initializer {
              __Context_init_unchained();
              __Pausable_init_unchained();
          }
          function __Pausable_init_unchained() internal initializer {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              require(!paused(), "Pausable: paused");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              require(paused(), "Pausable: not paused");
              _;
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract ContextUpgradeable is Initializable {
          function __Context_init() internal initializer {
              __Context_init_unchained();
          }
          function __Context_init_unchained() internal initializer {
          }
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _setOwner(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _setOwner(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _setOwner(newOwner);
          }
          function _setOwner(address newOwner) private {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address sender,
              address recipient,
              uint256 amount
          ) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
      import "@cartesi/pos/contracts/IPoS.sol";
      import "./utils/WadRayMath.sol";
      contract StakingPoolData is
          Initializable,
          PausableUpgradeable,
          OwnableUpgradeable
      {
          using WadRayMath for uint256;
          uint256 public shares; // total number of shares
          uint256 public amount; // amount of staked tokens (no matter where it is)
          uint256 public requiredLiquidity; // amount of required tokens for withdraw requests
          IPoS public pos;
          struct UserBalance {
              uint256 balance; // amount of free tokens belonging to this user
              uint256 shares; // amount of shares belonging to this user
              uint256 depositTimestamp; // timestamp of when user deposited for the last time
          }
          mapping(address => UserBalance) public userBalance;
          function amountToShares(uint256 _amount) public view returns (uint256) {
              if (amount == 0) {
                  // no shares yet, return 1 to 1 ratio
                  return _amount.wad2ray();
              }
              return _amount.wmul(shares).wdiv(amount);
          }
          function sharesToAmount(uint256 _shares) public view returns (uint256) {
              if (shares == 0) {
                  // no shares yet, return 1 to 1 ratio
                  return _shares.ray2wad();
              }
              return _shares.rmul(amount).rdiv(shares);
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "./interfaces/StakingPool.sol";
      import "./StakingPoolData.sol";
      import "./StakingPoolManagementImpl.sol";
      import "./StakingPoolProducerImpl.sol";
      import "./StakingPoolStakingImpl.sol";
      import "./StakingPoolUserImpl.sol";
      import "./StakingPoolWorkerImpl.sol";
      contract StakingPoolImpl is
          StakingPool,
          StakingPoolData,
          StakingPoolManagementImpl,
          StakingPoolProducerImpl,
          StakingPoolStakingImpl,
          StakingPoolUserImpl,
          StakingPoolWorkerImpl
      {
          constructor(
              address _ctsi,
              address _staking,
              address _workerManager,
              address _ens,
              uint256 _stakeLock
          )
              StakingPoolManagementImpl(_ens)
              StakingPoolProducerImpl(_ctsi)
              StakingPoolStakingImpl(_ctsi, _staking)
              StakingPoolUserImpl(_ctsi, _stakeLock)
              StakingPoolWorkerImpl(_workerManager)
          {}
          function initialize(address _fee, address _pos)
              external
              override
              initializer
          {
              __Pausable_init();
              __Ownable_init();
              __StakingPoolProducer_init(_fee, _pos);
              __StakingPoolStaking_init();
              __StakingPoolManagementImpl_init();
          }
          /// @notice updates the internal settings for important pieces of the Cartesi PoS system
          function update() external override onlyOwner {
              address _pos = factory.getPoS();
              __StakingPoolWorkerImpl_update(_pos);
          }
          function transferOwnership(address newOwner)
              public
              override(StakingPool, OwnableUpgradeable)
          {
              OwnableUpgradeable.transferOwnership(newOwner);
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@ensdomains/ens-contracts/contracts/registry/ReverseRegistrar.sol";
      import "@ensdomains/ens-contracts/contracts/registry/ENS.sol";
      import "./interfaces/StakingPoolManagement.sol";
      import "./interfaces/StakingPoolFactory.sol";
      import "./StakingPoolData.sol";
      contract StakingPoolManagementImpl is StakingPoolManagement, StakingPoolData {
          bytes32 private constant ADDR_REVERSE_NODE =
              0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
          ENS public immutable ens;
          StakingPoolFactory public factory;
          // all immutable variables can stay at the constructor
          constructor(address _ens) initializer {
              require(_ens != address(0), "parameter can not be zero address");
              ens = ENS(_ens);
              // make sure reference code is pause so no one stake to it
              _pause();
          }
          function __StakingPoolManagementImpl_init() internal {
              factory = StakingPoolFactory(msg.sender);
          }
          /// @notice sets a name for the pool using ENS service
          function setName(string memory name) external override onlyOwner {
              ReverseRegistrar ensReverseRegistrar = ReverseRegistrar(
                  ens.owner(ADDR_REVERSE_NODE)
              );
              // call the ENS reverse registrar resolving pool address to name
              ensReverseRegistrar.setName(name);
              // emit event, for subgraph processing
              emit StakingPoolRenamed(name);
          }
          /// @notice pauses new staking on the pool
          function pause() external override onlyOwner {
              _pause();
          }
          /// @notice unpauses new staking on the pool
          function unpause() external override onlyOwner {
              _unpause();
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@cartesi/pos/contracts/IPoS.sol";
      import "@cartesi/pos/contracts/IRewardManager.sol";
      import "./interfaces/Fee.sol";
      import "./interfaces/StakingPoolProducer.sol";
      import "./StakingPoolData.sol";
      contract StakingPoolProducerImpl is StakingPoolProducer, StakingPoolData {
          IERC20 public immutable ctsi;
          Fee public fee;
          constructor(address _ctsi) {
              ctsi = IERC20(_ctsi);
          }
          function __StakingPoolProducer_init(address _fee, address _pos) internal {
              fee = Fee(_fee);
              pos = IPoS(_pos);
          }
          /// @notice routes produceBlock to POS contract and
          /// updates internal states of the pool
          /// @return true when everything went fine
          function produceBlock(uint256 _index) external override returns (bool) {
              IRewardManager rewardManager = IRewardManager(
                  pos.getRewardManagerAddress(_index)
              );
              // get block reward
              uint256 reward = rewardManager.getCurrentReward();
              // produce block in the PoS
              require(
                  pos.produceBlock(_index),
                  "StakingPoolProducerImpl: failed to produce block"
              );
              // calculate pool commission
              uint256 commission = fee.getCommission(_index, reward);
              require(
                  commission <= reward,
                  "StakingPoolProducerImpl: commission is greater than block reward"
              );
              uint256 remainingReward = reward - commission; // this is a safety check
              // if commission is over the reward amount, it will underflow
              // increase pool amount, this will change the pool exchange rate
              amount += remainingReward;
              // send commission directly to pool owner
              if (commission > 0) {
                  require(
                      ctsi.transfer(owner(), commission),
                      "StakingPoolProducerImpl: failed to transfer commission"
                  );
              }
              // remainingReward is part of the balance, so it will automatically be staked by StakingPoolStakingImpl
              emit BlockProduced(reward, commission);
              return true;
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@cartesi/pos/contracts/IStaking.sol";
      import "./interfaces/StakingPoolStaking.sol";
      import "./StakingPoolData.sol";
      /// @notice This contract takes care of the interaction between the pool and the staking contract
      /// It makes sure that there is enough liquidity in the pool to fullfil all unstake request from
      /// users, by requesting to withdraw or unstake from Staking contract.
      /// The remaining balance is staked.
      contract StakingPoolStakingImpl is StakingPoolStaking, StakingPoolData {
          IERC20 private immutable ctsi;
          IStaking private immutable staking;
          constructor(address _ctsi, address _staking) {
              ctsi = IERC20(_ctsi);
              staking = IStaking(_staking);
          }
          function __StakingPoolStaking_init() internal {
              require(
                  ctsi.approve(address(staking), type(uint256).max),
                  "Failed to approve CTSI for staking contract"
              );
          }
          function rebalance() external override {
              // get amounts
              (uint256 _stake, uint256 _unstake, uint256 _withdraw) = amounts();
              if (_stake > 0) {
                  // we can stake
                  staking.stake(_stake);
              }
              if (_unstake > 0) {
                  // we need to provide liquidity
                  staking.unstake(_unstake);
              }
              if (_withdraw > 0) {
                  // we need to provide liquidity
                  staking.withdraw(_withdraw);
              }
          }
          function amounts()
              public
              view
              override
              returns (
                  uint256 stake,
                  uint256 unstake,
                  uint256 withdraw
              )
          {
              // get this contract balance first
              uint256 balance = ctsi.balanceOf(address(this));
              if (balance > requiredLiquidity) {
                  // we have spare tokens we can stake
                  // check if there is anything already maturing, to avoid reset the maturation clock
                  uint256 maturing = staking.getMaturingBalance(address(this));
                  if (maturing == 0) {
                      // nothing is maturing, we can stake the balance, preserving the liquidity
                      stake = balance - requiredLiquidity;
                  }
              } else if (requiredLiquidity > balance) {
                  // we don't have enough tokens to provide liquidity
                  uint256 missingLiquidity = requiredLiquidity - balance;
                  // let's first check releasing balance
                  uint256 releasing = staking.getReleasingBalance(address(this));
                  if (releasing > 0) {
                      // some is already releasing
                      // let's check timestamp to see if we can withdrawn it
                      uint256 timestamp = staking.getReleasingTimestamp(
                          address(this)
                      );
                      if (timestamp < block.timestamp) {
                          // there it is, let's grab it
                          withdraw = releasing;
                      }
                      // requiredLiquidity may be more than what is already releasing
                      // but we won't unstake more to not reset the clock
                  } else {
                      // no unstake maturing, let's queue some
                      unstake = missingLiquidity;
                  }
              } else {
                  // balance is exactly required liquidity, we can't move any tokens around
              }
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "./interfaces/StakingPoolUser.sol";
      import "./StakingPoolData.sol";
      contract StakingPoolUserImpl is StakingPoolUser, StakingPoolData {
          IERC20 private immutable ctsi;
          uint256 public immutable lockTime;
          /// @dev Constructor
          /// @param _ctsi The contract that provides the staking pool's token
          /// @param _lockTime The user deposit lock period
          constructor(address _ctsi, uint256 _lockTime) {
              ctsi = IERC20(_ctsi);
              lockTime = _lockTime;
          }
          function deposit(uint256 _amount) external override whenNotPaused {
              // transfer tokens from caller to this contract
              // user must have approved the transfer a priori
              // tokens will be lying around, until actually staked by pool owner at a later time
              require(
                  _amount > 0,
                  "StakingPoolUserImpl: amount must be greater than 0"
              );
              // add tokens to user's balance
              UserBalance storage user = userBalance[msg.sender];
              user.balance += _amount;
              // reset deposit timestamp
              user.depositTimestamp = block.timestamp;
              // reserve the balance as required liquidity (don't stake to Staking)
              requiredLiquidity += _amount;
              require(
                  ctsi.transferFrom(msg.sender, address(this), _amount),
                  "StakingPoolUserImpl: failed to transfer tokens"
              );
              // emit event containing user and amount
              emit Deposit(msg.sender, _amount, block.timestamp + lockTime);
          }
          /// @notice Stake an amount of tokens, immediately earning pool shares in returns
          /// @param _amount amount of tokens to convert from user's balance
          function stake(uint256 _amount) external override whenNotPaused {
              // get user balance
              UserBalance storage user = userBalance[msg.sender];
              // transfer tokens from caller to this contract
              // user must have approved the transfer a priori
              // tokens will be lying around, until actually staked by pool owner at a later time
              require(
                  _amount > 0,
                  "StakingPoolUserImpl: amount must be greater than 0"
              );
              require(
                  _amount <= user.balance,
                  "StakingPoolUserImpl: not enough tokens available for staking"
              );
              // check if user can already stake or if it's too early
              require(
                  block.timestamp >= user.depositTimestamp + lockTime,
                  "StakingPoolUserImpl: not enough time has passed since last deposit"
              );
              // calculate amount of shares as of now
              uint256 _shares = amountToShares(_amount);
              // make sure he get at least one share (rounding errors)
              require(
                  _shares > 0,
                  "StakingPoolUserImpl: stake not enough to emit 1 share"
              );
              // allocate new shares to user, immediately
              user.shares += _shares;
              user.balance -= _amount;
              // increase total shares and amount (not changing share value)
              amount += _amount;
              shares += _shares;
              // remove from required liquidity, as it's moving to Staking
              requiredLiquidity -= _amount;
              // emit event containing user, amount, shares and unlock time
              emit Stake(msg.sender, _amount, _shares);
          }
          /// @notice allow for users to defined exactly how many shares they
          /// want to unstake. Estimated value is then emitted on Unstake event
          function unstake(uint256 _shares) external override {
              UserBalance storage user = userBalance[msg.sender];
              // check if shares is valid value
              require(_shares > 0, "StakingPoolUserImpl: invalid amount of shares");
              // check if user has enough shares to unstake
              require(
                  user.shares >= _shares,
                  "StakingPoolUserImpl: insufficient shares"
              );
              // reduce user number of shares
              user.shares -= _shares;
              // calculate amount of tokens from shares
              uint256 _amount = sharesToAmount(_shares);
              // reduce total shares and amount
              shares -= _shares;
              amount -= _amount;
              // add amount user can withdraw (if available)
              user.balance += _amount;
              // increase required liquidity
              requiredLiquidity += _amount;
              // emit event containing user, amount and shares
              emit Unstake(msg.sender, _amount, _shares);
          }
          /// @notice Transfer tokens back to calling user wallet
          /// @dev this will transfer all free tokens for the calling user
          function withdraw(uint256 _amount) external override {
              UserBalance storage user = userBalance[msg.sender];
              // check user released value
              require(
                  user.balance > 0,
                  "StakingPoolUserImpl: no balance to withdraw"
              );
              // clear user released value
              user.balance -= _amount; // if _amount >  user.balance this will revert
              // decrease required liquidity
              requiredLiquidity -= _amount; // if _amount >  requiredLiquidity this will revert
              // transfer token back to user
              require(
                  ctsi.transfer(msg.sender, _amount),
                  "StakingPoolUserImpl: failed to transfer tokens"
              );
              // emit event containing user and token amount
              emit Withdraw(msg.sender, _amount);
          }
          function getWithdrawBalance() external view override returns (uint256) {
              UserBalance storage user = userBalance[msg.sender];
              // get maximum amount user can withdraw (his balance)
              uint256 _amount = user.balance;
              // check contract balance
              uint256 balance = ctsi.balanceOf(address(this));
              // he can withdraw whatever is available at the contract, up to his balance
              return balance >= _amount ? _amount : balance;
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@cartesi/pos/contracts/IWorkerManagerAuthManager.sol";
      import "./interfaces/StakingPoolWorker.sol";
      import "./StakingPoolData.sol";
      contract StakingPoolWorkerImpl is StakingPoolWorker, StakingPoolData {
          IWorkerManagerAuthManager immutable workerManager;
          // all immutable variables can stay at the constructor
          constructor(address _workerManager) {
              require(
                  _workerManager != address(0),
                  "parameter can not be zero address"
              );
              workerManager = IWorkerManagerAuthManager(_workerManager);
          }
          receive() external payable {}
          function __StakingPoolWorkerImpl_update(address _pos) internal {
              workerManager.authorize(address(this), _pos);
              pos = IPoS(_pos);
          }
          /// @notice allows for the pool to act on its own behalf when producing blocks.
          function selfhire() external payable override {
              // pool needs to be both user and worker
              workerManager.hire{value: msg.value}(payable(address(this)));
              workerManager.authorize(address(this), address(pos));
              workerManager.acceptJob();
              payable(msg.sender).transfer(msg.value);
          }
          /// @notice Asks the worker to work for the sender. Sender needs to pay something.
          /// @param workerAddress address of the worker
          function hire(address payable workerAddress)
              external
              payable
              override
              onlyOwner
          {
              workerManager.hire{value: msg.value}(workerAddress);
              workerManager.authorize(workerAddress, address(pos));
          }
          /// @notice Called by the user to cancel a job offer
          /// @param workerAddress address of the worker node
          function cancelHire(address workerAddress) external override onlyOwner {
              workerManager.cancelHire(workerAddress);
          }
          /// @notice Called by the user to retire his worker.
          /// @param workerAddress address of the worker to be retired
          /// @dev this also removes all authorizations in place
          function retire(address payable workerAddress) external override onlyOwner {
              workerManager.retire(workerAddress);
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0 <0.9.0;
      /// @title Calculator of pool owner commission for each block reward
      /// @author Danilo Tuler
      /// @notice This provides flexibility for different commission models
      interface Fee {
          /// @notice calculates the total amount of the reward that will be directed to the pool owner
          /// @return amount of tokens taken by the pool owner as commission
          function getCommission(uint256 posIndex, uint256 rewardAmount)
              external
              view
              returns (uint256);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      import "./StakingPoolManagement.sol";
      import "./StakingPoolProducer.sol";
      import "./StakingPoolStaking.sol";
      import "./StakingPoolUser.sol";
      import "./StakingPoolWorker.sol";
      /// @title Staking Pool interface
      /// @author Danilo Tuler
      /// @notice This interface aggregates all facets of a staking pool.
      /// It is broken down into the following sub-interfaces:
      /// - StakingPoolManagement: management operations on the pool, called by the owner
      /// - StakingPoolProducer: operations related to block production
      /// - StakingPoolStaking: interaction between the pool and the staking contract
      /// - StakingPoolUser: interaction between the pool users and the pool
      /// - StakingPoolWorker: interaction between the pool and the worker node
      interface StakingPool is
          StakingPoolManagement,
          StakingPoolProducer,
          StakingPoolStaking,
          StakingPoolUser,
          StakingPoolWorker
      {
          /// @notice initialize pool (from reference)
          function initialize(address fee, address _pos) external;
          /// @notice Transfer ownership of pool to its deployer
          function transferOwnership(address newOwner) external;
          /// @notice updates the internal settings for important pieces of the Cartesi PoS system
          function update() external;
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      interface StakingPoolFactory {
          /// @notice Creates a new staking pool using a flat commission model
          /// emits NewFlatRateCommissionStakingPool with the parameters of the new pool
          /// @return new pool address
          function createFlatRateCommission(uint256 commission)
              external
              payable
              returns (address);
          /// @notice Creates a new staking pool using a gas tax commission model
          /// emits NewGasTaxCommissionStakingPool with the parameters of the new pool
          /// @return new pool address
          function createGasTaxCommission(uint256 gas)
              external
              payable
              returns (address);
          /// @notice Returns configuration for the working pools of the current version
          /// @return _pos address for the PoS contract
          function getPoS() external view returns (address _pos);
          /// @notice Event emmited when a pool is created
          /// @param pool address of the new pool
          /// @param fee address of the commission contract
          event NewFlatRateCommissionStakingPool(address indexed pool, address fee);
          /// @notice Event emmited when a pool is created
          /// @param pool address of the new pool
          /// @param fee address of thhe commission contract
          event NewGasTaxCommissionStakingPool(address indexed pool, address fee);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      interface StakingPoolManagement {
          /// @notice sets a name for the pool using ENS service
          function setName(string memory name) external;
          /// @notice pauses new staking on the pool
          function pause() external;
          /// @notice unpauses new staking on the pool
          function unpause() external;
          /// @notice Event emmited when a pool is rename
          event StakingPoolRenamed(string name);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      /// @title Interaction between a pool and the PoS block production.
      /// @author Danilo Tuler
      /// @notice This interface provides an opportunity to handle the necessary logic
      /// after a block is produced.
      /// A commission is taken from the block reward, and the remaining stays in the pool,
      /// raising the pool share value, and being further staked.
      interface StakingPoolProducer {
          /// @notice routes produceBlock to POS contract and
          /// updates internal states of the pool
          /// @return true when everything went fine
          function produceBlock(uint256 _index) external returns (bool);
          /// @notice this event is emitted at every produceBlock call
          /// reward is the block reward
          /// commission is how much CTSI is directed to the pool owner
          event BlockProduced(uint256 reward, uint256 commission);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      /// @title Interaction between a pool and the staking contract
      /// @author Danilo Tuler
      /// @notice This interface models all interactions between a pool and the staking contract,
      /// including staking, unstaking and withdrawing.
      /// Tokens staked by pool users will stay at the pool until the pool owner decides to
      /// stake them in the staking contract. On the other hand, tokens unstaked by pool users
      /// are added to a required liquidity accumulator, and must be unstaked and withdrawn from
      /// the staking contract.
      interface StakingPoolStaking {
          /// @notice Move tokens from pool to staking or vice-versa, according to required liquidity.
          /// If the pool has more liquidity then necessary, it stakes tokens.
          /// If the pool has less liquidity then necessary, and has not started an unstake, it unstakes.
          /// If the pool has less liquity than necessary, and has started an unstake, it withdraws if possible.
          function rebalance() external;
          /// @notice provide information for offchain about the amount for each
          /// staking operation on the main Staking contract
          /// @return stake amount of tokens that can be staked
          /// @return unstake amount of tokens that must be unstaked to add liquidity
          /// @return withdraw amount of tokens that can be withdrawn to add liquidity
          function amounts()
              external
              view
              returns (
                  uint256 stake,
                  uint256 unstake,
                  uint256 withdraw
              );
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      /// @title Interaction between a pool user and a pool
      /// @author Danilo Tuler
      /// @notice This interface models all interactions between a pool user and a pool,
      /// including staking, unstaking and withdrawing. A pool user always holds pool shares.
      /// When a user stakes tokens, he immediately receive shares. When he unstakes shares
      /// he is asking to release tokens. Those tokens need to be withdrawn by an additional
      /// call to withdraw()
      interface StakingPoolUser {
          /// @notice Deposit tokens to user pool balance
          /// @param amount amount of token deposited in the pool
          function deposit(uint256 amount) external;
          /// @notice Stake an amount of tokens, immediately earning pool shares in returns
          /// @param amount amount of tokens to convert to shares
          function stake(uint256 amount) external;
          /// @notice Unstake an specified amount of shares of the calling user
          /// @dev Shares are immediately converted to tokens, and added to the pool liquidity requirement
          function unstake(uint256 shares) external;
          /// @notice Transfer tokens back to calling user wallet
          /// @dev this will transfer tokens from user pool account to user's wallet
          function withdraw(uint256 amount) external;
          /// @notice Returns the amount of tokens that can be immediately withdrawn by the calling user
          /// @dev there is no way to know the exact time in the future the requested tokens will be available
          /// @return the amount of tokens that can be immediately withdrawn by the calling user
          function getWithdrawBalance() external returns (uint256);
          /// @notice Tokens were deposited, available for staking or withdrawal
          /// @param user address of msg.sender
          /// @param amount amount of tokens deposited by the user
          /// @param stakeTimestamp instant when the amount can be staked
          event Deposit(address indexed user, uint256 amount, uint256 stakeTimestamp);
          /// @notice Tokens were deposited, they count as shares immediatly
          /// @param user address of msg.sender
          /// @param amount amount deposited by the user
          /// @param shares number of shares emitted for user
          event Stake(address indexed user, uint256 amount, uint256 shares);
          /// @notice Request to unstake tokens. Additional liquidity requested for the pool
          /// @param user address of msg.sender
          /// @param amount amount of tokens to be released
          /// @param shares number of shares being liquidated
          event Unstake(address indexed user, uint256 amount, uint256 shares);
          /// @notice Withdraw performed by a user
          /// @param user address of msg.sender
          /// @param amount amount of tokens withdrawn
          event Withdraw(address indexed user, uint256 amount);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      interface StakingPoolWorker {
          /// @notice allows for the pool to act on its own behalf when producing blocks.
          function selfhire() external payable;
          /// @notice Asks the worker to work for the sender. Sender needs to pay something.
          /// @param workerAddress address of the worker
          function hire(address payable workerAddress) external payable;
          /// @notice Called by the user to cancel a job offer
          /// @param workerAddress address of the worker node
          function cancelHire(address workerAddress) external;
          /// @notice Called by the user to retire his worker.
          /// @param workerAddress address of the worker to be retired
          /// @dev this also removes all authorizations in place
          function retire(address payable workerAddress) external;
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Wad and Ray Math library
      /// @dev Math operations for wads (fixed point with 18 digits) and rays (fixed points with 27 digits)
      pragma solidity ^0.8.0;
      library WadRayMath {
          uint256 internal constant WAD = 1e18;
          uint256 internal constant RAY = 1e27;
          uint256 internal constant RATIO = 1e9;
          function wmul(uint256 a, uint256 b) internal pure returns (uint256) {
              return ((WAD / 2) + (a * b)) / WAD;
          }
          function wdiv(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 halfB = b / 2;
              return (halfB + (a * WAD)) / b;
          }
          function rmul(uint256 a, uint256 b) internal pure returns (uint256) {
              return ((RAY / 2) + (a * b)) / RAY;
          }
          function rdiv(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 halfB = b / 2;
              return (halfB + (a * RAY)) / b;
          }
          function ray2wad(uint256 a) internal pure returns (uint256) {
              uint256 halfRatio = RATIO / 2;
              return (halfRatio + a) / RATIO;
          }
          function wad2ray(uint256 a) internal pure returns (uint256) {
              return a * RATIO;
          }
      }
      

      File 2 of 3: CartesiToken
      pragma solidity ^0.5.0;
      
      
      contract Context {
          // Empty internal constructor, to prevent people from mistakenly deploying
          // an instance of this contract, which should be used via inheritance.
          constructor () internal { }
          // solhint-disable-previous-line no-empty-blocks
      
          function _msgSender() internal view returns (address payable) {
              return msg.sender;
          }
      
          function _msgData() internal view returns (bytes memory) {
              this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
              return msg.data;
          }
      }
      
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
      
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
      
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
      
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
      
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
      
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
      
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
      
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      
      library SafeMath {
          /**
           * @dev Returns the addition of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `+` operator.
           *
           * Requirements:
           * - Addition cannot overflow.
           */
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           */
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          /**
           * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
           * overflow (when the result is negative).
           *
           * Counterpart to Solidity's `-` operator.
           *
           * Requirements:
           * - Subtraction cannot overflow.
           *
           * _Available since v2.4.0._
           */
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
      
              return c;
          }
      
          /**
           * @dev Returns the multiplication of two unsigned integers, reverting on
           * overflow.
           *
           * Counterpart to Solidity's `*` operator.
           *
           * Requirements:
           * - Multiplication cannot overflow.
           */
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
              // benefit is lost if 'b' is also tested.
              // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
      
              return c;
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          /**
           * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
           * division by zero. The result is rounded towards zero.
           *
           * Counterpart to Solidity's `/` operator. Note: this function uses a
           * `revert` opcode (which leaves remaining gas untouched) while Solidity
           * uses an invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              // Solidity only automatically asserts when dividing by 0
              require(b > 0, errorMessage);
              uint256 c = a / b;
              // assert(a == b * c + a % b); // There is no case in which this doesn't hold
      
              return c;
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           */
          function mod(uint256 a, uint256 b) internal pure returns (uint256) {
              return mod(a, b, "SafeMath: modulo by zero");
          }
      
          /**
           * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
           * Reverts with custom message when dividing by zero.
           *
           * Counterpart to Solidity's `%` operator. This function uses a `revert`
           * opcode (which leaves remaining gas untouched) while Solidity uses an
           * invalid opcode to revert (consuming all remaining gas).
           *
           * Requirements:
           * - The divisor cannot be zero.
           *
           * _Available since v2.4.0._
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      contract ERC20 is Context, IERC20 {
          using SafeMath for uint256;
      
          mapping (address => uint256) private _balances;
      
          mapping (address => mapping (address => uint256)) private _allowances;
      
          uint256 private _totalSupply;
      
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view returns (uint256) {
              return _totalSupply;
          }
      
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view returns (uint256) {
              return _balances[account];
          }
      
          /**
           * @dev See {IERC20-transfer}.
           *
           * Requirements:
           *
           * - `recipient` cannot be the zero address.
           * - the caller must have a balance of at least `amount`.
           */
          function transfer(address recipient, uint256 amount) public returns (bool) {
              _transfer(_msgSender(), recipient, amount);
              return true;
          }
      
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view returns (uint256) {
              return _allowances[owner][spender];
          }
      
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public returns (bool) {
              _approve(_msgSender(), spender, amount);
              return true;
          }
      
          /**
           * @dev See {IERC20-transferFrom}.
           *
           * Emits an {Approval} event indicating the updated allowance. This is not
           * required by the EIP. See the note at the beginning of {ERC20};
           *
           * Requirements:
           * - `sender` and `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           * - the caller must have allowance for `sender`'s tokens of at least
           * `amount`.
           */
          function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
              return true;
          }
      
          /**
           * @dev Atomically increases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
              return true;
          }
      
          /**
           * @dev Atomically decreases the allowance granted to `spender` by the caller.
           *
           * This is an alternative to {approve} that can be used as a mitigation for
           * problems described in {IERC20-approve}.
           *
           * Emits an {Approval} event indicating the updated allowance.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           * - `spender` must have allowance for the caller of at least
           * `subtractedValue`.
           */
          function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
              _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
              return true;
          }
      
          /**
           * @dev Moves tokens `amount` from `sender` to `recipient`.
           *
           * This is internal function is equivalent to {transfer}, and can be used to
           * e.g. implement automatic token fees, slashing mechanisms, etc.
           *
           * Emits a {Transfer} event.
           *
           * Requirements:
           *
           * - `sender` cannot be the zero address.
           * - `recipient` cannot be the zero address.
           * - `sender` must have a balance of at least `amount`.
           */
          function _transfer(address sender, address recipient, uint256 amount) internal {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
      
              _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
              _balances[recipient] = _balances[recipient].add(amount);
              emit Transfer(sender, recipient, amount);
          }
      
          /** @dev Creates `amount` tokens and assigns them to `account`, increasing
           * the total supply.
           *
           * Emits a {Transfer} event with `from` set to the zero address.
           *
           * Requirements
           *
           * - `to` cannot be the zero address.
           */
          function _mint(address account, uint256 amount) internal {
              require(account != address(0), "ERC20: mint to the zero address");
      
              _totalSupply = _totalSupply.add(amount);
              _balances[account] = _balances[account].add(amount);
              emit Transfer(address(0), account, amount);
          }
      
          /**
           * @dev Destroys `amount` tokens from `account`, reducing the
           * total supply.
           *
           * Emits a {Transfer} event with `to` set to the zero address.
           *
           * Requirements
           *
           * - `account` cannot be the zero address.
           * - `account` must have at least `amount` tokens.
           */
          function _burn(address account, uint256 amount) internal {
              require(account != address(0), "ERC20: burn from the zero address");
      
              _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(account, address(0), amount);
          }
      
          /**
           * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
           *
           * This is internal function is equivalent to `approve`, and can be used to
           * e.g. set automatic allowances for certain subsystems, etc.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           */
          function _approve(address owner, address spender, uint256 amount) internal {
              require(owner != address(0), "ERC20: approve from the zero address");
              require(spender != address(0), "ERC20: approve to the zero address");
      
              _allowances[owner][spender] = amount;
              emit Approval(owner, spender, amount);
          }
      
          /**
           * @dev Destroys `amount` tokens from `account`.`amount` is then deducted
           * from the caller's allowance.
           *
           * See {_burn} and {_approve}.
           */
          function _burnFrom(address account, uint256 amount) internal {
              _burn(account, amount);
              _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
          }
      }
      
      library Roles {
          struct Role {
              mapping (address => bool) bearer;
          }
      
          /**
           * @dev Give an account access to this role.
           */
          function add(Role storage role, address account) internal {
              require(!has(role, account), "Roles: account already has role");
              role.bearer[account] = true;
          }
      
          /**
           * @dev Remove an account's access to this role.
           */
          function remove(Role storage role, address account) internal {
              require(has(role, account), "Roles: account does not have role");
              role.bearer[account] = false;
          }
      
          /**
           * @dev Check if an account has this role.
           * @return bool
           */
          function has(Role storage role, address account) internal view returns (bool) {
              require(account != address(0), "Roles: account is the zero address");
              return role.bearer[account];
          }
      }
      
      contract MinterRole is Context {
          using Roles for Roles.Role;
      
          event MinterAdded(address indexed account);
          event MinterRemoved(address indexed account);
      
          Roles.Role private _minters;
      
          constructor () internal {
              _addMinter(_msgSender());
          }
      
          modifier onlyMinter() {
              require(isMinter(_msgSender()), "MinterRole: caller does not have the Minter role");
              _;
          }
      
          function isMinter(address account) public view returns (bool) {
              return _minters.has(account);
          }
      
          function addMinter(address account) public onlyMinter {
              _addMinter(account);
          }
      
          function renounceMinter() public {
              _removeMinter(_msgSender());
          }
      
          function _addMinter(address account) internal {
              _minters.add(account);
              emit MinterAdded(account);
          }
      
          function _removeMinter(address account) internal {
              _minters.remove(account);
              emit MinterRemoved(account);
          }
      }
      
      contract ERC20Mintable is ERC20, MinterRole {
          /**
           * @dev See {ERC20-_mint}.
           *
           * Requirements:
           *
           * - the caller must have the {MinterRole}.
           */
          function mint(address account, uint256 amount) public onlyMinter returns (bool) {
              _mint(account, amount);
              return true;
          }
      }
      
      contract ERC20Detailed is IERC20 {
          string private _name;
          string private _symbol;
          uint8 private _decimals;
      
          /**
           * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
           * these values are immutable: they can only be set once during
           * construction.
           */
          constructor (string memory name, string memory symbol, uint8 decimals) public {
              _name = name;
              _symbol = symbol;
              _decimals = decimals;
          }
      
          /**
           * @dev Returns the name of the token.
           */
          function name() public view returns (string memory) {
              return _name;
          }
      
          /**
           * @dev Returns the symbol of the token, usually a shorter version of the
           * name.
           */
          function symbol() public view returns (string memory) {
              return _symbol;
          }
      
          /**
           * @dev Returns the number of decimals used to get its user representation.
           * For example, if `decimals` equals `2`, a balance of `505` tokens should
           * be displayed to a user as `5,05` (`505 / 10 ** 2`).
           *
           * Tokens usually opt for a value of 18, imitating the relationship between
           * Ether and Wei.
           *
           * NOTE: This information is only used for _display_ purposes: it in
           * no way affects any of the arithmetic of the contract, including
           * {IERC20-balanceOf} and {IERC20-transfer}.
           */
          function decimals() public view returns (uint8) {
              return _decimals;
          }
      }
      
      contract CartesiToken is ERC20Mintable, ERC20Detailed{
          string public constant NAME = "Cartesi Token";
          string public constant SYMBOL = "CTSI";
          uint8 public constant DECIMALS = 18;
          uint256 public constant INITIAL_SUPPLY = 1000000000 * (10 ** uint256(DECIMALS));
      
          uint256 mintLockTime;
          uint256 deployedDate;
      
          event LocktimeExteded(uint256 extendedBy, uint256 deadline);
      
          constructor()
              ERC20Detailed(NAME, SYMBOL, DECIMALS) public
          {
              deployedDate = now;
              _mint(msg.sender, INITIAL_SUPPLY);
          }
      
          function mint(address account, uint256 amount) public onlyMinter returns (bool) {
              if (now < SafeMath.add(deployedDate, mintLockTime)) {
                  return false;
              }
              _mint(account, amount);
              return true;
          }
      
          function extendMintLockTime(uint256 _extraLockTime) public onlyMinter {
              mintLockTime = SafeMath.add(mintLockTime, _extraLockTime);
      
              emit LocktimeExteded(_extraLockTime, deployedDate + mintLockTime);
          }
      }

      File 3 of 3: StakingPoolImpl
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Interface PoS
      pragma solidity >=0.7.0 <0.9.0;
      interface IPoS {
          /// @notice Produce a block
          /// @param _index the index of the instance of pos you want to interact with
          /// @dev this function can only be called by a worker, user never calls it directly
          function produceBlock(uint256 _index) external returns (bool);
          /// @notice Get reward manager address
          /// @param _index index of instance
          /// @return address of instance's RewardManager
          function getRewardManagerAddress(uint256 _index)
              external
              view
              returns (address);
          /// @notice Get block selector address
          /// @param _index index of instance
          /// @return address of instance's block selector
          function getBlockSelectorAddress(uint256 _index)
              external
              view
              returns (address);
          /// @notice Get block selector index
          /// @param _index index of instance
          /// @return index of instance's block selector
          function getBlockSelectorIndex(uint256 _index)
              external
              view
              returns (uint256);
          /// @notice Get staking address
          /// @param _index index of instance
          /// @return address of instance's staking contract
          function getStakingAddress(uint256 _index) external view returns (address);
          /// @notice Get state of a particular instance
          /// @param _index index of instance
          /// @param _user address of user
          /// @return bool if user is eligible to produce next block
          /// @return address of user that was chosen to build the block
          /// @return current reward paid by the network for that block
          function getState(uint256 _index, address _user)
              external
              view
              returns (
                  bool,
                  address,
                  uint256
              );
          function terminate(uint256 _index) external;
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Interface RewardManager
      pragma solidity >=0.7.0 <0.9.0;
      interface IRewardManager {
          /// @notice Rewards address
          /// @param _address address be rewarded
          /// @param _amount reward
          /// @dev only the pos contract can call this
          function reward(address _address, uint256 _amount) external;
          /// @notice Get RewardManager's balance
          function getBalance() external view returns (uint256);
          /// @notice Get current reward amount
          function getCurrentReward() external view returns (uint256);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Interface Staking
      pragma solidity >=0.7.0 <0.9.0;
      interface IStaking {
          /// @notice Returns total amount of tokens counted as stake
          /// @param _userAddress user to retrieve staked balance from
          /// @return finalized staked of _userAddress
          function getStakedBalance(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Returns the timestamp when next deposit can be finalized
          /// @return timestamp of when finalizeStakes() is callable
          function getMaturingTimestamp(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Returns the timestamp when next withdraw can be finalized
          /// @return timestamp of when finalizeWithdraw() is callable
          function getReleasingTimestamp(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Returns the balance waiting/ready to be matured
          /// @return amount that will get staked after finalization
          function getMaturingBalance(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Returns the balance waiting/ready to be released
          /// @return amount that will get withdrew after finalization
          function getReleasingBalance(address _userAddress)
              external
              view
              returns (uint256);
          /// @notice Deposit CTSI to be staked. The money will turn into staked
          ///         balance after timeToStake days
          /// @param _amount The amount of tokens that are gonna be deposited.
          function stake(uint256 _amount) external;
          /// @notice Remove tokens from staked balance. The money can
          ///         be released after timeToRelease seconds, if the
          ///         function withdraw is called.
          /// @param _amount The amount of tokens that are gonna be unstaked.
          function unstake(uint256 _amount) external;
          /// @notice Transfer tokens to user's wallet.
          /// @param _amount The amount of tokens that are gonna be transferred.
          function withdraw(uint256 _amount) external;
          // events
          /// @notice CTSI tokens were deposited, they count as stake after _maturationDate
          /// @param user address of msg.sender
          /// @param amount amount deposited for staking
          /// @param maturationDate date when the stake can be finalized
          event Stake(address indexed user, uint256 amount, uint256 maturationDate);
          /// @notice Unstake tokens, moving them to releasing structure
          /// @param user address of msg.sender
          /// @param amount amount of tokens to be released
          /// @param maturationDate date when the tokens can be withdrew
          event Unstake(address indexed user, uint256 amount, uint256 maturationDate);
          /// @notice Withdraw process was finalized
          /// @param user address of msg.sender
          /// @param amount amount of tokens withdrawn
          event Withdraw(address indexed user, uint256 amount);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Interface WorkerManager
      /// @author Danilo Tuler
      pragma solidity >=0.7.0 <0.9.0;
      interface IWorkerManagerAuthManager {
          /// @notice Asks the worker to work for the sender. Sender needs to pay something.
          /// @param workerAddress address of the worker
          function hire(address payable workerAddress) external payable;
          /// @notice Called by the user to cancel a job offer
          /// @param workerAddress address of the worker node
          function cancelHire(address workerAddress) external;
          /// @notice Called by the user to retire his worker.
          /// @param workerAddress address of the worker to be retired
          /// @dev this also removes all authorizations in place
          function retire(address payable workerAddress) external;
          /// @notice Gives worker permission to act on a DApp
          /// @param _workerAddress address of the worker node to given permission
          /// @param _dappAddress address of the dapp that permission will be given to
          function authorize(address _workerAddress, address _dappAddress) external;
          /// @notice Called by the worker to accept the job
          function acceptJob() external;
          /// @notice Called by the worker to reject a job offer
          function rejectJob() external payable;
      }
      pragma solidity >=0.8.4;
      interface ENS {
          // Logged when the owner of a node assigns a new owner to a subnode.
          event NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
          // Logged when the owner of a node transfers ownership to a new account.
          event Transfer(bytes32 indexed node, address owner);
          // Logged when the resolver for a node changes.
          event NewResolver(bytes32 indexed node, address resolver);
          // Logged when the TTL of a node changes
          event NewTTL(bytes32 indexed node, uint64 ttl);
          // Logged when an operator is added or removed.
          event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
          function setRecord(bytes32 node, address owner, address resolver, uint64 ttl) external virtual;
          function setSubnodeRecord(bytes32 node, bytes32 label, address owner, address resolver, uint64 ttl) external virtual;
          function setSubnodeOwner(bytes32 node, bytes32 label, address owner) external virtual returns(bytes32);
          function setResolver(bytes32 node, address resolver) external virtual;
          function setOwner(bytes32 node, address owner) external virtual;
          function setTTL(bytes32 node, uint64 ttl) external virtual;
          function setApprovalForAll(address operator, bool approved) external virtual;
          function owner(bytes32 node) external virtual view returns (address);
          function resolver(bytes32 node) external virtual view returns (address);
          function ttl(bytes32 node) external virtual view returns (uint64);
          function recordExists(bytes32 node) external virtual view returns (bool);
          function isApprovedForAll(address owner, address operator) external virtual view returns (bool);
      }
      pragma solidity >=0.8.4;
      import "./ENS.sol";
      import "@openzeppelin/contracts/access/Ownable.sol";
      import "../root/Controllable.sol";
      abstract contract NameResolver {
          function setName(bytes32 node, string memory name) public virtual;
      }
      bytes32 constant lookup = 0x3031323334353637383961626364656600000000000000000000000000000000;
      bytes32 constant ADDR_REVERSE_NODE = 0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
      // namehash('addr.reverse')
      contract ReverseRegistrar is Ownable, Controllable {
          ENS public ens;
          NameResolver public defaultResolver;
          event ReverseClaimed(address indexed addr, bytes32 indexed node);
          /**
           * @dev Constructor
           * @param ensAddr The address of the ENS registry.
           * @param resolverAddr The address of the default reverse resolver.
           */
          constructor(ENS ensAddr, NameResolver resolverAddr) {
              ens = ensAddr;
              defaultResolver = resolverAddr;
              // Assign ownership of the reverse record to our deployer
              ReverseRegistrar oldRegistrar = ReverseRegistrar(
                  ens.owner(ADDR_REVERSE_NODE)
              );
              if (address(oldRegistrar) != address(0x0)) {
                  oldRegistrar.claim(msg.sender);
              }
          }
          modifier authorised(address addr) {
              require(
                  addr == msg.sender ||
                      controllers[msg.sender] ||
                      ens.isApprovedForAll(addr, msg.sender) ||
                      ownsContract(addr),
                  "Caller is not a controller or authorised by address or the address itself"
              );
              _;
          }
          /**
           * @dev Transfers ownership of the reverse ENS record associated with the
           *      calling account.
           * @param owner The address to set as the owner of the reverse record in ENS.
           * @return The ENS node hash of the reverse record.
           */
          function claim(address owner) public returns (bytes32) {
              return _claimWithResolver(msg.sender, owner, address(0x0));
          }
          /**
           * @dev Transfers ownership of the reverse ENS record associated with the
           *      calling account.
           * @param addr The reverse record to set
           * @param owner The address to set as the owner of the reverse record in ENS.
           * @return The ENS node hash of the reverse record.
           */
          function claimForAddr(address addr, address owner)
              public
              authorised(addr)
              returns (bytes32)
          {
              return _claimWithResolver(addr, owner, address(0x0));
          }
          /**
           * @dev Transfers ownership of the reverse ENS record associated with the
           *      calling account.
           * @param owner The address to set as the owner of the reverse record in ENS.
           * @param resolver The address of the resolver to set; 0 to leave unchanged.
           * @return The ENS node hash of the reverse record.
           */
          function claimWithResolver(address owner, address resolver)
              public
              returns (bytes32)
          {
              return _claimWithResolver(msg.sender, owner, resolver);
          }
          /**
           * @dev Transfers ownership of the reverse ENS record specified with the
           *      address provided
           * @param addr The reverse record to set
           * @param owner The address to set as the owner of the reverse record in ENS.
           * @param resolver The address of the resolver to set; 0 to leave unchanged.
           * @return The ENS node hash of the reverse record.
           */
          function claimWithResolverForAddr(
              address addr,
              address owner,
              address resolver
          ) public authorised(addr) returns (bytes32) {
              return _claimWithResolver(addr, owner, resolver);
          }
          /**
           * @dev Sets the `name()` record for the reverse ENS record associated with
           * the calling account. First updates the resolver to the default reverse
           * resolver if necessary.
           * @param name The name to set for this address.
           * @return The ENS node hash of the reverse record.
           */
          function setName(string memory name) public returns (bytes32) {
              bytes32 node = _claimWithResolver(
                  msg.sender,
                  address(this),
                  address(defaultResolver)
              );
              defaultResolver.setName(node, name);
              return node;
          }
          /**
           * @dev Sets the `name()` record for the reverse ENS record associated with
           * the account provided. First updates the resolver to the default reverse
           * resolver if necessary.
           * Only callable by controllers and authorised users
           * @param addr The reverse record to set
           * @param owner The owner of the reverse node
           * @param name The name to set for this address.
           * @return The ENS node hash of the reverse record.
           */
          function setNameForAddr(
              address addr,
              address owner,
              string memory name
          ) public authorised(addr) returns (bytes32) {
              bytes32 node = _claimWithResolver(
                  addr,
                  address(this),
                  address(defaultResolver)
              );
              defaultResolver.setName(node, name);
              ens.setSubnodeOwner(ADDR_REVERSE_NODE, sha3HexAddress(addr), owner);
              return node;
          }
          /**
           * @dev Returns the node hash for a given account's reverse records.
           * @param addr The address to hash
           * @return The ENS node hash.
           */
          function node(address addr) public pure returns (bytes32) {
              return
                  keccak256(
                      abi.encodePacked(ADDR_REVERSE_NODE, sha3HexAddress(addr))
                  );
          }
          /**
           * @dev An optimised function to compute the sha3 of the lower-case
           *      hexadecimal representation of an Ethereum address.
           * @param addr The address to hash
           * @return ret The SHA3 hash of the lower-case hexadecimal encoding of the
           *         input address.
           */
          function sha3HexAddress(address addr) private pure returns (bytes32 ret) {
              assembly {
                  for {
                      let i := 40
                  } gt(i, 0) {
                  } {
                      i := sub(i, 1)
                      mstore8(i, byte(and(addr, 0xf), lookup))
                      addr := div(addr, 0x10)
                      i := sub(i, 1)
                      mstore8(i, byte(and(addr, 0xf), lookup))
                      addr := div(addr, 0x10)
                  }
                  ret := keccak256(0, 40)
              }
          }
          /* Internal functions */
          function _claimWithResolver(
              address addr,
              address owner,
              address resolver
          ) internal returns (bytes32) {
              bytes32 label = sha3HexAddress(addr);
              bytes32 node = keccak256(abi.encodePacked(ADDR_REVERSE_NODE, label));
              address currentResolver = ens.resolver(node);
              bool shouldUpdateResolver = (resolver != address(0x0) &&
                  resolver != currentResolver);
              address newResolver = shouldUpdateResolver ? resolver : currentResolver;
              ens.setSubnodeRecord(ADDR_REVERSE_NODE, label, owner, newResolver, 0);
              emit ReverseClaimed(addr, node);
              return node;
          }
          function ownsContract(address addr) internal view returns (bool) {
              try Ownable(addr).owner() returns (address owner) {
                  return owner == msg.sender;
              } catch {
                  return false;
              }
          }
      }
      pragma solidity ^0.8.4;
      import "@openzeppelin/contracts/access/Ownable.sol";
      contract Controllable is Ownable {
          mapping(address => bool) public controllers;
          event ControllerChanged(address indexed controller, bool enabled);
          modifier onlyController {
              require(
                  controllers[msg.sender],
                  "Controllable: Caller is not a controller"
              );
              _;
          }
          function setController(address controller, bool enabled) public onlyOwner {
              controllers[controller] = enabled;
              emit ControllerChanged(controller, enabled);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../utils/ContextUpgradeable.sol";
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          function __Ownable_init() internal initializer {
              __Context_init_unchained();
              __Ownable_init_unchained();
          }
          function __Ownable_init_unchained() internal initializer {
              _setOwner(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _setOwner(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _setOwner(newOwner);
          }
          function _setOwner(address newOwner) private {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
       * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
       * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
       * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
       *
       * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
       * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
       *
       * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
       * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
       */
      abstract contract Initializable {
          /**
           * @dev Indicates that the contract has been initialized.
           */
          bool private _initialized;
          /**
           * @dev Indicates that the contract is in the process of being initialized.
           */
          bool private _initializing;
          /**
           * @dev Modifier to protect an initializer function from being invoked twice.
           */
          modifier initializer() {
              require(_initializing || !_initialized, "Initializable: contract is already initialized");
              bool isTopLevelCall = !_initializing;
              if (isTopLevelCall) {
                  _initializing = true;
                  _initialized = true;
              }
              _;
              if (isTopLevelCall) {
                  _initializing = false;
              }
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../utils/ContextUpgradeable.sol";
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Contract module which allows children to implement an emergency stop
       * mechanism that can be triggered by an authorized account.
       *
       * This module is used through inheritance. It will make available the
       * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
       * the functions of your contract. Note that they will not be pausable by
       * simply including this module, only once the modifiers are put in place.
       */
      abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
          /**
           * @dev Emitted when the pause is triggered by `account`.
           */
          event Paused(address account);
          /**
           * @dev Emitted when the pause is lifted by `account`.
           */
          event Unpaused(address account);
          bool private _paused;
          /**
           * @dev Initializes the contract in unpaused state.
           */
          function __Pausable_init() internal initializer {
              __Context_init_unchained();
              __Pausable_init_unchained();
          }
          function __Pausable_init_unchained() internal initializer {
              _paused = false;
          }
          /**
           * @dev Returns true if the contract is paused, and false otherwise.
           */
          function paused() public view virtual returns (bool) {
              return _paused;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is not paused.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          modifier whenNotPaused() {
              require(!paused(), "Pausable: paused");
              _;
          }
          /**
           * @dev Modifier to make a function callable only when the contract is paused.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          modifier whenPaused() {
              require(paused(), "Pausable: not paused");
              _;
          }
          /**
           * @dev Triggers stopped state.
           *
           * Requirements:
           *
           * - The contract must not be paused.
           */
          function _pause() internal virtual whenNotPaused {
              _paused = true;
              emit Paused(_msgSender());
          }
          /**
           * @dev Returns to normal state.
           *
           * Requirements:
           *
           * - The contract must be paused.
           */
          function _unpause() internal virtual whenPaused {
              _paused = false;
              emit Unpaused(_msgSender());
          }
          uint256[49] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../proxy/utils/Initializable.sol";
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract ContextUpgradeable is Initializable {
          function __Context_init() internal initializer {
              __Context_init_unchained();
          }
          function __Context_init_unchained() internal initializer {
          }
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
          uint256[50] private __gap;
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      import "../utils/Context.sol";
      /**
       * @dev Contract module which provides a basic access control mechanism, where
       * there is an account (an owner) that can be granted exclusive access to
       * specific functions.
       *
       * By default, the owner account will be the one that deploys the contract. This
       * can later be changed with {transferOwnership}.
       *
       * This module is used through inheritance. It will make available the modifier
       * `onlyOwner`, which can be applied to your functions to restrict their use to
       * the owner.
       */
      abstract contract Ownable is Context {
          address private _owner;
          event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
          /**
           * @dev Initializes the contract setting the deployer as the initial owner.
           */
          constructor() {
              _setOwner(_msgSender());
          }
          /**
           * @dev Returns the address of the current owner.
           */
          function owner() public view virtual returns (address) {
              return _owner;
          }
          /**
           * @dev Throws if called by any account other than the owner.
           */
          modifier onlyOwner() {
              require(owner() == _msgSender(), "Ownable: caller is not the owner");
              _;
          }
          /**
           * @dev Leaves the contract without owner. It will not be possible to call
           * `onlyOwner` functions anymore. Can only be called by the current owner.
           *
           * NOTE: Renouncing ownership will leave the contract without an owner,
           * thereby removing any functionality that is only available to the owner.
           */
          function renounceOwnership() public virtual onlyOwner {
              _setOwner(address(0));
          }
          /**
           * @dev Transfers ownership of the contract to a new account (`newOwner`).
           * Can only be called by the current owner.
           */
          function transferOwnership(address newOwner) public virtual onlyOwner {
              require(newOwner != address(0), "Ownable: new owner is the zero address");
              _setOwner(newOwner);
          }
          function _setOwner(address newOwner) private {
              address oldOwner = _owner;
              _owner = newOwner;
              emit OwnershipTransferred(oldOwner, newOwner);
          }
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Interface of the ERC20 standard as defined in the EIP.
       */
      interface IERC20 {
          /**
           * @dev Returns the amount of tokens in existence.
           */
          function totalSupply() external view returns (uint256);
          /**
           * @dev Returns the amount of tokens owned by `account`.
           */
          function balanceOf(address account) external view returns (uint256);
          /**
           * @dev Moves `amount` tokens from the caller's account to `recipient`.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transfer(address recipient, uint256 amount) external returns (bool);
          /**
           * @dev Returns the remaining number of tokens that `spender` will be
           * allowed to spend on behalf of `owner` through {transferFrom}. This is
           * zero by default.
           *
           * This value changes when {approve} or {transferFrom} are called.
           */
          function allowance(address owner, address spender) external view returns (uint256);
          /**
           * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * IMPORTANT: Beware that changing an allowance with this method brings the risk
           * that someone may use both the old and the new allowance by unfortunate
           * transaction ordering. One possible solution to mitigate this race
           * condition is to first reduce the spender's allowance to 0 and set the
           * desired value afterwards:
           * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
           *
           * Emits an {Approval} event.
           */
          function approve(address spender, uint256 amount) external returns (bool);
          /**
           * @dev Moves `amount` tokens from `sender` to `recipient` using the
           * allowance mechanism. `amount` is then deducted from the caller's
           * allowance.
           *
           * Returns a boolean value indicating whether the operation succeeded.
           *
           * Emits a {Transfer} event.
           */
          function transferFrom(
              address sender,
              address recipient,
              uint256 amount
          ) external returns (bool);
          /**
           * @dev Emitted when `value` tokens are moved from one account (`from`) to
           * another (`to`).
           *
           * Note that `value` may be zero.
           */
          event Transfer(address indexed from, address indexed to, uint256 value);
          /**
           * @dev Emitted when the allowance of a `spender` for an `owner` is set by
           * a call to {approve}. `value` is the new allowance.
           */
          event Approval(address indexed owner, address indexed spender, uint256 value);
      }
      // SPDX-License-Identifier: MIT
      pragma solidity ^0.8.0;
      /**
       * @dev Provides information about the current execution context, including the
       * sender of the transaction and its data. While these are generally available
       * via msg.sender and msg.data, they should not be accessed in such a direct
       * manner, since when dealing with meta-transactions the account sending and
       * paying for execution may not be the actual sender (as far as an application
       * is concerned).
       *
       * This contract is only required for intermediate, library-like contracts.
       */
      abstract contract Context {
          function _msgSender() internal view virtual returns (address) {
              return msg.sender;
          }
          function _msgData() internal view virtual returns (bytes calldata) {
              return msg.data;
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
      import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
      import "@cartesi/pos/contracts/IPoS.sol";
      import "./utils/WadRayMath.sol";
      contract StakingPoolData is
          Initializable,
          PausableUpgradeable,
          OwnableUpgradeable
      {
          using WadRayMath for uint256;
          uint256 public shares; // total number of shares
          uint256 public amount; // amount of staked tokens (no matter where it is)
          uint256 public requiredLiquidity; // amount of required tokens for withdraw requests
          IPoS public pos;
          struct UserBalance {
              uint256 balance; // amount of free tokens belonging to this user
              uint256 shares; // amount of shares belonging to this user
              uint256 depositTimestamp; // timestamp of when user deposited for the last time
          }
          mapping(address => UserBalance) public userBalance;
          function amountToShares(uint256 _amount) public view returns (uint256) {
              if (amount == 0) {
                  // no shares yet, return 1 to 1 ratio
                  return _amount.wad2ray();
              }
              return _amount.wmul(shares).wdiv(amount);
          }
          function sharesToAmount(uint256 _shares) public view returns (uint256) {
              if (shares == 0) {
                  // no shares yet, return 1 to 1 ratio
                  return _shares.ray2wad();
              }
              return _shares.rmul(amount).rdiv(shares);
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "./interfaces/StakingPool.sol";
      import "./StakingPoolData.sol";
      import "./StakingPoolManagementImpl.sol";
      import "./StakingPoolProducerImpl.sol";
      import "./StakingPoolStakingImpl.sol";
      import "./StakingPoolUserImpl.sol";
      import "./StakingPoolWorkerImpl.sol";
      contract StakingPoolImpl is
          StakingPool,
          StakingPoolData,
          StakingPoolManagementImpl,
          StakingPoolProducerImpl,
          StakingPoolStakingImpl,
          StakingPoolUserImpl,
          StakingPoolWorkerImpl
      {
          constructor(
              address _ctsi,
              address _staking,
              address _workerManager,
              address _ens,
              uint256 _stakeLock
          )
              StakingPoolManagementImpl(_ens)
              StakingPoolProducerImpl(_ctsi)
              StakingPoolStakingImpl(_ctsi, _staking)
              StakingPoolUserImpl(_ctsi, _stakeLock)
              StakingPoolWorkerImpl(_workerManager)
          {}
          function initialize(address _fee, address _pos)
              external
              override
              initializer
          {
              __Pausable_init();
              __Ownable_init();
              __StakingPoolProducer_init(_fee, _pos);
              __StakingPoolStaking_init();
              __StakingPoolManagementImpl_init();
          }
          /// @notice updates the internal settings for important pieces of the Cartesi PoS system
          function update() external override onlyOwner {
              address _pos = factory.getPoS();
              __StakingPoolWorkerImpl_update(_pos);
          }
          function transferOwnership(address newOwner)
              public
              override(StakingPool, OwnableUpgradeable)
          {
              OwnableUpgradeable.transferOwnership(newOwner);
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@ensdomains/ens-contracts/contracts/registry/ReverseRegistrar.sol";
      import "@ensdomains/ens-contracts/contracts/registry/ENS.sol";
      import "./interfaces/StakingPoolManagement.sol";
      import "./interfaces/StakingPoolFactory.sol";
      import "./StakingPoolData.sol";
      contract StakingPoolManagementImpl is StakingPoolManagement, StakingPoolData {
          bytes32 private constant ADDR_REVERSE_NODE =
              0x91d1777781884d03a6757a803996e38de2a42967fb37eeaca72729271025a9e2;
          ENS public immutable ens;
          StakingPoolFactory public factory;
          // all immutable variables can stay at the constructor
          constructor(address _ens) initializer {
              require(_ens != address(0), "parameter can not be zero address");
              ens = ENS(_ens);
              // make sure reference code is pause so no one stake to it
              _pause();
          }
          function __StakingPoolManagementImpl_init() internal {
              factory = StakingPoolFactory(msg.sender);
          }
          /// @notice sets a name for the pool using ENS service
          function setName(string memory name) external override onlyOwner {
              ReverseRegistrar ensReverseRegistrar = ReverseRegistrar(
                  ens.owner(ADDR_REVERSE_NODE)
              );
              // call the ENS reverse registrar resolving pool address to name
              ensReverseRegistrar.setName(name);
              // emit event, for subgraph processing
              emit StakingPoolRenamed(name);
          }
          /// @notice pauses new staking on the pool
          function pause() external override onlyOwner {
              _pause();
          }
          /// @notice unpauses new staking on the pool
          function unpause() external override onlyOwner {
              _unpause();
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@cartesi/pos/contracts/IPoS.sol";
      import "@cartesi/pos/contracts/IRewardManager.sol";
      import "./interfaces/Fee.sol";
      import "./interfaces/StakingPoolProducer.sol";
      import "./StakingPoolData.sol";
      contract StakingPoolProducerImpl is StakingPoolProducer, StakingPoolData {
          IERC20 public immutable ctsi;
          Fee public fee;
          constructor(address _ctsi) {
              ctsi = IERC20(_ctsi);
          }
          function __StakingPoolProducer_init(address _fee, address _pos) internal {
              fee = Fee(_fee);
              pos = IPoS(_pos);
          }
          /// @notice routes produceBlock to POS contract and
          /// updates internal states of the pool
          /// @return true when everything went fine
          function produceBlock(uint256 _index) external override returns (bool) {
              IRewardManager rewardManager = IRewardManager(
                  pos.getRewardManagerAddress(_index)
              );
              // get block reward
              uint256 reward = rewardManager.getCurrentReward();
              // produce block in the PoS
              require(
                  pos.produceBlock(_index),
                  "StakingPoolProducerImpl: failed to produce block"
              );
              // calculate pool commission
              uint256 commission = fee.getCommission(_index, reward);
              require(
                  commission <= reward,
                  "StakingPoolProducerImpl: commission is greater than block reward"
              );
              uint256 remainingReward = reward - commission; // this is a safety check
              // if commission is over the reward amount, it will underflow
              // increase pool amount, this will change the pool exchange rate
              amount += remainingReward;
              // send commission directly to pool owner
              if (commission > 0) {
                  require(
                      ctsi.transfer(owner(), commission),
                      "StakingPoolProducerImpl: failed to transfer commission"
                  );
              }
              // remainingReward is part of the balance, so it will automatically be staked by StakingPoolStakingImpl
              emit BlockProduced(reward, commission);
              return true;
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "@cartesi/pos/contracts/IStaking.sol";
      import "./interfaces/StakingPoolStaking.sol";
      import "./StakingPoolData.sol";
      /// @notice This contract takes care of the interaction between the pool and the staking contract
      /// It makes sure that there is enough liquidity in the pool to fullfil all unstake request from
      /// users, by requesting to withdraw or unstake from Staking contract.
      /// The remaining balance is staked.
      contract StakingPoolStakingImpl is StakingPoolStaking, StakingPoolData {
          IERC20 private immutable ctsi;
          IStaking private immutable staking;
          constructor(address _ctsi, address _staking) {
              ctsi = IERC20(_ctsi);
              staking = IStaking(_staking);
          }
          function __StakingPoolStaking_init() internal {
              require(
                  ctsi.approve(address(staking), type(uint256).max),
                  "Failed to approve CTSI for staking contract"
              );
          }
          function rebalance() external override {
              // get amounts
              (uint256 _stake, uint256 _unstake, uint256 _withdraw) = amounts();
              if (_stake > 0) {
                  // we can stake
                  staking.stake(_stake);
              }
              if (_unstake > 0) {
                  // we need to provide liquidity
                  staking.unstake(_unstake);
              }
              if (_withdraw > 0) {
                  // we need to provide liquidity
                  staking.withdraw(_withdraw);
              }
          }
          function amounts()
              public
              view
              override
              returns (
                  uint256 stake,
                  uint256 unstake,
                  uint256 withdraw
              )
          {
              // get this contract balance first
              uint256 balance = ctsi.balanceOf(address(this));
              if (balance > requiredLiquidity) {
                  // we have spare tokens we can stake
                  // check if there is anything already maturing, to avoid reset the maturation clock
                  uint256 maturing = staking.getMaturingBalance(address(this));
                  if (maturing == 0) {
                      // nothing is maturing, we can stake the balance, preserving the liquidity
                      stake = balance - requiredLiquidity;
                  }
              } else if (requiredLiquidity > balance) {
                  // we don't have enough tokens to provide liquidity
                  uint256 missingLiquidity = requiredLiquidity - balance;
                  // let's first check releasing balance
                  uint256 releasing = staking.getReleasingBalance(address(this));
                  if (releasing > 0) {
                      // some is already releasing
                      // let's check timestamp to see if we can withdrawn it
                      uint256 timestamp = staking.getReleasingTimestamp(
                          address(this)
                      );
                      if (timestamp < block.timestamp) {
                          // there it is, let's grab it
                          withdraw = releasing;
                      }
                      // requiredLiquidity may be more than what is already releasing
                      // but we won't unstake more to not reset the clock
                  } else {
                      // no unstake maturing, let's queue some
                      unstake = missingLiquidity;
                  }
              } else {
                  // balance is exactly required liquidity, we can't move any tokens around
              }
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
      import "./interfaces/StakingPoolUser.sol";
      import "./StakingPoolData.sol";
      contract StakingPoolUserImpl is StakingPoolUser, StakingPoolData {
          IERC20 private immutable ctsi;
          uint256 public immutable lockTime;
          /// @dev Constructor
          /// @param _ctsi The contract that provides the staking pool's token
          /// @param _lockTime The user deposit lock period
          constructor(address _ctsi, uint256 _lockTime) {
              ctsi = IERC20(_ctsi);
              lockTime = _lockTime;
          }
          function deposit(uint256 _amount) external override whenNotPaused {
              // transfer tokens from caller to this contract
              // user must have approved the transfer a priori
              // tokens will be lying around, until actually staked by pool owner at a later time
              require(
                  _amount > 0,
                  "StakingPoolUserImpl: amount must be greater than 0"
              );
              // add tokens to user's balance
              UserBalance storage user = userBalance[msg.sender];
              user.balance += _amount;
              // reset deposit timestamp
              user.depositTimestamp = block.timestamp;
              // reserve the balance as required liquidity (don't stake to Staking)
              requiredLiquidity += _amount;
              require(
                  ctsi.transferFrom(msg.sender, address(this), _amount),
                  "StakingPoolUserImpl: failed to transfer tokens"
              );
              // emit event containing user and amount
              emit Deposit(msg.sender, _amount, block.timestamp + lockTime);
          }
          /// @notice Stake an amount of tokens, immediately earning pool shares in returns
          /// @param _amount amount of tokens to convert from user's balance
          function stake(uint256 _amount) external override whenNotPaused {
              // get user balance
              UserBalance storage user = userBalance[msg.sender];
              // transfer tokens from caller to this contract
              // user must have approved the transfer a priori
              // tokens will be lying around, until actually staked by pool owner at a later time
              require(
                  _amount > 0,
                  "StakingPoolUserImpl: amount must be greater than 0"
              );
              require(
                  _amount <= user.balance,
                  "StakingPoolUserImpl: not enough tokens available for staking"
              );
              // check if user can already stake or if it's too early
              require(
                  block.timestamp >= user.depositTimestamp + lockTime,
                  "StakingPoolUserImpl: not enough time has passed since last deposit"
              );
              // calculate amount of shares as of now
              uint256 _shares = amountToShares(_amount);
              // make sure he get at least one share (rounding errors)
              require(
                  _shares > 0,
                  "StakingPoolUserImpl: stake not enough to emit 1 share"
              );
              // allocate new shares to user, immediately
              user.shares += _shares;
              user.balance -= _amount;
              // increase total shares and amount (not changing share value)
              amount += _amount;
              shares += _shares;
              // remove from required liquidity, as it's moving to Staking
              requiredLiquidity -= _amount;
              // emit event containing user, amount, shares and unlock time
              emit Stake(msg.sender, _amount, _shares);
          }
          /// @notice allow for users to defined exactly how many shares they
          /// want to unstake. Estimated value is then emitted on Unstake event
          function unstake(uint256 _shares) external override {
              UserBalance storage user = userBalance[msg.sender];
              // check if shares is valid value
              require(_shares > 0, "StakingPoolUserImpl: invalid amount of shares");
              // check if user has enough shares to unstake
              require(
                  user.shares >= _shares,
                  "StakingPoolUserImpl: insufficient shares"
              );
              // reduce user number of shares
              user.shares -= _shares;
              // calculate amount of tokens from shares
              uint256 _amount = sharesToAmount(_shares);
              // reduce total shares and amount
              shares -= _shares;
              amount -= _amount;
              // add amount user can withdraw (if available)
              user.balance += _amount;
              // increase required liquidity
              requiredLiquidity += _amount;
              // emit event containing user, amount and shares
              emit Unstake(msg.sender, _amount, _shares);
          }
          /// @notice Transfer tokens back to calling user wallet
          /// @dev this will transfer all free tokens for the calling user
          function withdraw(uint256 _amount) external override {
              UserBalance storage user = userBalance[msg.sender];
              // check user released value
              require(
                  user.balance > 0,
                  "StakingPoolUserImpl: no balance to withdraw"
              );
              // clear user released value
              user.balance -= _amount; // if _amount >  user.balance this will revert
              // decrease required liquidity
              requiredLiquidity -= _amount; // if _amount >  requiredLiquidity this will revert
              // transfer token back to user
              require(
                  ctsi.transfer(msg.sender, _amount),
                  "StakingPoolUserImpl: failed to transfer tokens"
              );
              // emit event containing user and token amount
              emit Withdraw(msg.sender, _amount);
          }
          function getWithdrawBalance() external view override returns (uint256) {
              UserBalance storage user = userBalance[msg.sender];
              // get maximum amount user can withdraw (his balance)
              uint256 _amount = user.balance;
              // check contract balance
              uint256 balance = ctsi.balanceOf(address(this));
              // he can withdraw whatever is available at the contract, up to his balance
              return balance >= _amount ? _amount : balance;
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity ^0.8.0;
      import "@cartesi/pos/contracts/IWorkerManagerAuthManager.sol";
      import "./interfaces/StakingPoolWorker.sol";
      import "./StakingPoolData.sol";
      contract StakingPoolWorkerImpl is StakingPoolWorker, StakingPoolData {
          IWorkerManagerAuthManager immutable workerManager;
          // all immutable variables can stay at the constructor
          constructor(address _workerManager) {
              require(
                  _workerManager != address(0),
                  "parameter can not be zero address"
              );
              workerManager = IWorkerManagerAuthManager(_workerManager);
          }
          receive() external payable {}
          function __StakingPoolWorkerImpl_update(address _pos) internal {
              workerManager.authorize(address(this), _pos);
              pos = IPoS(_pos);
          }
          /// @notice allows for the pool to act on its own behalf when producing blocks.
          function selfhire() external payable override {
              // pool needs to be both user and worker
              workerManager.hire{value: msg.value}(payable(address(this)));
              workerManager.authorize(address(this), address(pos));
              workerManager.acceptJob();
              payable(msg.sender).transfer(msg.value);
          }
          /// @notice Asks the worker to work for the sender. Sender needs to pay something.
          /// @param workerAddress address of the worker
          function hire(address payable workerAddress)
              external
              payable
              override
              onlyOwner
          {
              workerManager.hire{value: msg.value}(workerAddress);
              workerManager.authorize(workerAddress, address(pos));
          }
          /// @notice Called by the user to cancel a job offer
          /// @param workerAddress address of the worker node
          function cancelHire(address workerAddress) external override onlyOwner {
              workerManager.cancelHire(workerAddress);
          }
          /// @notice Called by the user to retire his worker.
          /// @param workerAddress address of the worker to be retired
          /// @dev this also removes all authorizations in place
          function retire(address payable workerAddress) external override onlyOwner {
              workerManager.retire(workerAddress);
          }
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0 <0.9.0;
      /// @title Calculator of pool owner commission for each block reward
      /// @author Danilo Tuler
      /// @notice This provides flexibility for different commission models
      interface Fee {
          /// @notice calculates the total amount of the reward that will be directed to the pool owner
          /// @return amount of tokens taken by the pool owner as commission
          function getCommission(uint256 posIndex, uint256 rewardAmount)
              external
              view
              returns (uint256);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      import "./StakingPoolManagement.sol";
      import "./StakingPoolProducer.sol";
      import "./StakingPoolStaking.sol";
      import "./StakingPoolUser.sol";
      import "./StakingPoolWorker.sol";
      /// @title Staking Pool interface
      /// @author Danilo Tuler
      /// @notice This interface aggregates all facets of a staking pool.
      /// It is broken down into the following sub-interfaces:
      /// - StakingPoolManagement: management operations on the pool, called by the owner
      /// - StakingPoolProducer: operations related to block production
      /// - StakingPoolStaking: interaction between the pool and the staking contract
      /// - StakingPoolUser: interaction between the pool users and the pool
      /// - StakingPoolWorker: interaction between the pool and the worker node
      interface StakingPool is
          StakingPoolManagement,
          StakingPoolProducer,
          StakingPoolStaking,
          StakingPoolUser,
          StakingPoolWorker
      {
          /// @notice initialize pool (from reference)
          function initialize(address fee, address _pos) external;
          /// @notice Transfer ownership of pool to its deployer
          function transferOwnership(address newOwner) external;
          /// @notice updates the internal settings for important pieces of the Cartesi PoS system
          function update() external;
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      interface StakingPoolFactory {
          /// @notice Creates a new staking pool using a flat commission model
          /// emits NewFlatRateCommissionStakingPool with the parameters of the new pool
          /// @return new pool address
          function createFlatRateCommission(uint256 commission)
              external
              payable
              returns (address);
          /// @notice Creates a new staking pool using a gas tax commission model
          /// emits NewGasTaxCommissionStakingPool with the parameters of the new pool
          /// @return new pool address
          function createGasTaxCommission(uint256 gas)
              external
              payable
              returns (address);
          /// @notice Returns configuration for the working pools of the current version
          /// @return _pos address for the PoS contract
          function getPoS() external view returns (address _pos);
          /// @notice Event emmited when a pool is created
          /// @param pool address of the new pool
          /// @param fee address of the commission contract
          event NewFlatRateCommissionStakingPool(address indexed pool, address fee);
          /// @notice Event emmited when a pool is created
          /// @param pool address of the new pool
          /// @param fee address of thhe commission contract
          event NewGasTaxCommissionStakingPool(address indexed pool, address fee);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      interface StakingPoolManagement {
          /// @notice sets a name for the pool using ENS service
          function setName(string memory name) external;
          /// @notice pauses new staking on the pool
          function pause() external;
          /// @notice unpauses new staking on the pool
          function unpause() external;
          /// @notice Event emmited when a pool is rename
          event StakingPoolRenamed(string name);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      /// @title Interaction between a pool and the PoS block production.
      /// @author Danilo Tuler
      /// @notice This interface provides an opportunity to handle the necessary logic
      /// after a block is produced.
      /// A commission is taken from the block reward, and the remaining stays in the pool,
      /// raising the pool share value, and being further staked.
      interface StakingPoolProducer {
          /// @notice routes produceBlock to POS contract and
          /// updates internal states of the pool
          /// @return true when everything went fine
          function produceBlock(uint256 _index) external returns (bool);
          /// @notice this event is emitted at every produceBlock call
          /// reward is the block reward
          /// commission is how much CTSI is directed to the pool owner
          event BlockProduced(uint256 reward, uint256 commission);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      /// @title Interaction between a pool and the staking contract
      /// @author Danilo Tuler
      /// @notice This interface models all interactions between a pool and the staking contract,
      /// including staking, unstaking and withdrawing.
      /// Tokens staked by pool users will stay at the pool until the pool owner decides to
      /// stake them in the staking contract. On the other hand, tokens unstaked by pool users
      /// are added to a required liquidity accumulator, and must be unstaked and withdrawn from
      /// the staking contract.
      interface StakingPoolStaking {
          /// @notice Move tokens from pool to staking or vice-versa, according to required liquidity.
          /// If the pool has more liquidity then necessary, it stakes tokens.
          /// If the pool has less liquidity then necessary, and has not started an unstake, it unstakes.
          /// If the pool has less liquity than necessary, and has started an unstake, it withdraws if possible.
          function rebalance() external;
          /// @notice provide information for offchain about the amount for each
          /// staking operation on the main Staking contract
          /// @return stake amount of tokens that can be staked
          /// @return unstake amount of tokens that must be unstaked to add liquidity
          /// @return withdraw amount of tokens that can be withdrawn to add liquidity
          function amounts()
              external
              view
              returns (
                  uint256 stake,
                  uint256 unstake,
                  uint256 withdraw
              );
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      /// @title Interaction between a pool user and a pool
      /// @author Danilo Tuler
      /// @notice This interface models all interactions between a pool user and a pool,
      /// including staking, unstaking and withdrawing. A pool user always holds pool shares.
      /// When a user stakes tokens, he immediately receive shares. When he unstakes shares
      /// he is asking to release tokens. Those tokens need to be withdrawn by an additional
      /// call to withdraw()
      interface StakingPoolUser {
          /// @notice Deposit tokens to user pool balance
          /// @param amount amount of token deposited in the pool
          function deposit(uint256 amount) external;
          /// @notice Stake an amount of tokens, immediately earning pool shares in returns
          /// @param amount amount of tokens to convert to shares
          function stake(uint256 amount) external;
          /// @notice Unstake an specified amount of shares of the calling user
          /// @dev Shares are immediately converted to tokens, and added to the pool liquidity requirement
          function unstake(uint256 shares) external;
          /// @notice Transfer tokens back to calling user wallet
          /// @dev this will transfer tokens from user pool account to user's wallet
          function withdraw(uint256 amount) external;
          /// @notice Returns the amount of tokens that can be immediately withdrawn by the calling user
          /// @dev there is no way to know the exact time in the future the requested tokens will be available
          /// @return the amount of tokens that can be immediately withdrawn by the calling user
          function getWithdrawBalance() external returns (uint256);
          /// @notice Tokens were deposited, available for staking or withdrawal
          /// @param user address of msg.sender
          /// @param amount amount of tokens deposited by the user
          /// @param stakeTimestamp instant when the amount can be staked
          event Deposit(address indexed user, uint256 amount, uint256 stakeTimestamp);
          /// @notice Tokens were deposited, they count as shares immediatly
          /// @param user address of msg.sender
          /// @param amount amount deposited by the user
          /// @param shares number of shares emitted for user
          event Stake(address indexed user, uint256 amount, uint256 shares);
          /// @notice Request to unstake tokens. Additional liquidity requested for the pool
          /// @param user address of msg.sender
          /// @param amount amount of tokens to be released
          /// @param shares number of shares being liquidated
          event Unstake(address indexed user, uint256 amount, uint256 shares);
          /// @notice Withdraw performed by a user
          /// @param user address of msg.sender
          /// @param amount amount of tokens withdrawn
          event Withdraw(address indexed user, uint256 amount);
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      pragma solidity >=0.7.0;
      interface StakingPoolWorker {
          /// @notice allows for the pool to act on its own behalf when producing blocks.
          function selfhire() external payable;
          /// @notice Asks the worker to work for the sender. Sender needs to pay something.
          /// @param workerAddress address of the worker
          function hire(address payable workerAddress) external payable;
          /// @notice Called by the user to cancel a job offer
          /// @param workerAddress address of the worker node
          function cancelHire(address workerAddress) external;
          /// @notice Called by the user to retire his worker.
          /// @param workerAddress address of the worker to be retired
          /// @dev this also removes all authorizations in place
          function retire(address payable workerAddress) external;
      }
      // Copyright 2021 Cartesi Pte. Ltd.
      // SPDX-License-Identifier: Apache-2.0
      // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
      // this file except in compliance with the License. You may obtain a copy of the
      // License at http://www.apache.org/licenses/LICENSE-2.0
      // Unless required by applicable law or agreed to in writing, software distributed
      // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
      // CONDITIONS OF ANY KIND, either express or implied. See the License for the
      // specific language governing permissions and limitations under the License.
      /// @title Wad and Ray Math library
      /// @dev Math operations for wads (fixed point with 18 digits) and rays (fixed points with 27 digits)
      pragma solidity ^0.8.0;
      library WadRayMath {
          uint256 internal constant WAD = 1e18;
          uint256 internal constant RAY = 1e27;
          uint256 internal constant RATIO = 1e9;
          function wmul(uint256 a, uint256 b) internal pure returns (uint256) {
              return ((WAD / 2) + (a * b)) / WAD;
          }
          function wdiv(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 halfB = b / 2;
              return (halfB + (a * WAD)) / b;
          }
          function rmul(uint256 a, uint256 b) internal pure returns (uint256) {
              return ((RAY / 2) + (a * b)) / RAY;
          }
          function rdiv(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 halfB = b / 2;
              return (halfB + (a * RAY)) / b;
          }
          function ray2wad(uint256 a) internal pure returns (uint256) {
              uint256 halfRatio = RATIO / 2;
              return (halfRatio + a) / RATIO;
          }
          function wad2ray(uint256 a) internal pure returns (uint256) {
              return a * RATIO;
          }
      }