ETH Price: $2,654.09 (-1.30%)

Transaction Decoder

Block:
15602462 at Sep-24-2022 09:56:35 AM +UTC
Transaction Fee:
0.0006091854776673 ETH $1.62
Gas Used:
115,590 Gas / 5.27022647 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x0ab87046...25a20a52f
0x38351818...37814a899
(Lido: Execution Layer Rewards Vault)
167.632116098187120696 Eth167.632289483187120696 Eth0.000173385
0x6F5416D5...CAd07Eb55
5.333013815992765366 Eth
Nonce: 125
5.332404630515098066 Eth
Nonce: 126
0.0006091854776673

Execution Trace

OlympusTokenMigrator.migrateAll( _to=2 )
  • sOlympus.balanceOf( who=0x6F5416D56b7f67A6eCf48FC1899F8A1CAd07Eb55 ) => ( 0 )
  • wOHM.balanceOf( account=0x6F5416D56b7f67A6eCf48FC1899F8A1CAd07Eb55 ) => ( 0 )
  • OlympusERC20Token.balanceOf( account=0x6F5416D56b7f67A6eCf48FC1899F8A1CAd07Eb55 ) => ( 7848430845 )
  • OlympusERC20Token.balanceOf( account=0x6F5416D56b7f67A6eCf48FC1899F8A1CAd07Eb55 ) => ( 7848430845 )
  • OlympusERC20Token.transferFrom( sender=0x6F5416D56b7f67A6eCf48FC1899F8A1CAd07Eb55, recipient=0x184f3FAd8618a6F458C16bae63F70C426fE784B3, amount=7848430845 ) => ( True )
  • wOHM.sOHMTowOHM( _amount=7848430845 ) => ( 167983948202081146 )
    • sOlympus.STATICCALL( )
    • OlympusERC20Token.STATICCALL( )
    • gOHM.transfer( recipient=0x6F5416D56b7f67A6eCf48FC1899F8A1CAd07Eb55, amount=167983948202081146 ) => ( True )
      File 1 of 5: OlympusTokenMigrator
      // SPDX-License-Identifier: AGPL-3.0-or-later
      
      // File: libraries/SafeMath.sol
      
      
      pragma solidity ^0.7.5;
      
      
      // TODO(zx): Replace all instances of SafeMath with OZ implementation
      library SafeMath {
      
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
      
              return c;
          }
      
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
      
              return c;
          }
      
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              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;
          }
      
          // Only used in the  BondingCalculator.sol
          function sqrrt(uint256 a) internal pure returns (uint c) {
              if (a > 3) {
                  c = a;
                  uint b = add( div( a, 2), 1 );
                  while (b < c) {
                      c = b;
                      b = div( add( div( a, b ), b), 2 );
                  }
              } else if (a != 0) {
                  c = 1;
              }
          }
      
      }
      // File: interfaces/IOlympusAuthority.sol
      
      
      pragma solidity =0.7.5;
      
      interface IOlympusAuthority {
          /* ========== EVENTS ========== */
          
          event GovernorPushed(address indexed from, address indexed to, bool _effectiveImmediately);
          event GuardianPushed(address indexed from, address indexed to, bool _effectiveImmediately);    
          event PolicyPushed(address indexed from, address indexed to, bool _effectiveImmediately);    
          event VaultPushed(address indexed from, address indexed to, bool _effectiveImmediately);    
      
          event GovernorPulled(address indexed from, address indexed to);
          event GuardianPulled(address indexed from, address indexed to);
          event PolicyPulled(address indexed from, address indexed to);
          event VaultPulled(address indexed from, address indexed to);
      
          /* ========== VIEW ========== */
          
          function governor() external view returns (address);
          function guardian() external view returns (address);
          function policy() external view returns (address);
          function vault() external view returns (address);
      }
      // File: types/OlympusAccessControlled.sol
      
      
      pragma solidity >=0.7.5;
      
      
      abstract contract OlympusAccessControlled {
      
          /* ========== EVENTS ========== */
      
          event AuthorityUpdated(IOlympusAuthority indexed authority);
      
          string UNAUTHORIZED = "UNAUTHORIZED"; // save gas
      
          /* ========== STATE VARIABLES ========== */
      
          IOlympusAuthority public authority;
      
      
          /* ========== Constructor ========== */
      
          constructor(IOlympusAuthority _authority) {
              authority = _authority;
              emit AuthorityUpdated(_authority);
          }
          
      
          /* ========== MODIFIERS ========== */
          
          modifier onlyGovernor() {
              require(msg.sender == authority.governor(), UNAUTHORIZED);
              _;
          }
          
          modifier onlyGuardian() {
              require(msg.sender == authority.guardian(), UNAUTHORIZED);
              _;
          }
          
          modifier onlyPolicy() {
              require(msg.sender == authority.policy(), UNAUTHORIZED);
              _;
          }
      
          modifier onlyVault() {
              require(msg.sender == authority.vault(), UNAUTHORIZED);
              _;
          }
          
          /* ========== GOV ONLY ========== */
          
          function setAuthority(IOlympusAuthority _newAuthority) external onlyGovernor {
              authority = _newAuthority;
              emit AuthorityUpdated(_newAuthority);
          }
      }
      
      // File: interfaces/ITreasuryV1.sol
      
      
      pragma solidity >=0.7.5;
      
      interface ITreasuryV1 {
          function withdraw(uint256 amount, address token) external;
          function manage(address token, uint256 amount) external;
          function valueOf(address token, uint256 amount) external view returns (uint256);
          function excessReserves() external view returns (uint256);
      }
      // File: interfaces/IStakingV1.sol
      
      
      pragma solidity >=0.7.5;
      
      interface IStakingV1 {
          function unstake(uint256 _amount, bool _trigger) external;
      
          function index() external view returns (uint256);
      }
      // File: interfaces/IUniswapV2Router.sol
      
      
      pragma solidity >=0.7.5;
      
      interface IUniswapV2Router {
          function swapExactTokensForTokens(
              uint amountIn,
              uint amountOutMin,
              address[] calldata path,
              address to,
              uint deadline
          ) external returns (uint[] memory amounts);
      
          function addLiquidity(
              address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline
              ) external returns (uint amountA, uint amountB, uint liquidity);
              
          function removeLiquidity(
              address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline
              ) external returns (uint amountA, uint amountB);
      }
      // File: interfaces/IOwnable.sol
      
      
      pragma solidity >=0.7.5;
      
      
      interface IOwnable {
        function owner() external view returns (address);
      
        function renounceManagement() external;
        
        function pushManagement( address newOwner_ ) external;
        
        function pullManagement() external;
      }
      // File: interfaces/IStaking.sol
      
      
      pragma solidity >=0.7.5;
      
      interface IStaking {
          function stake(
              address _to,
              uint256 _amount,
              bool _rebasing,
              bool _claim
          ) external returns (uint256);
      
          function claim(address _recipient, bool _rebasing) external returns (uint256);
      
          function forfeit() external returns (uint256);
      
          function toggleLock() external;
      
          function unstake(
              address _to,
              uint256 _amount,
              bool _trigger,
              bool _rebasing
          ) external returns (uint256);
      
          function wrap(address _to, uint256 _amount) external returns (uint256 gBalance_);
      
          function unwrap(address _to, uint256 _amount) external returns (uint256 sBalance_);
      
          function rebase() external;
      
          function index() external view returns (uint256);
      
          function contractBalance() external view returns (uint256);
      
          function totalStaked() external view returns (uint256);
      
          function supplyInWarmup() external view returns (uint256);
      }
      
      // File: interfaces/ITreasury.sol
      
      
      pragma solidity >=0.7.5;
      
      interface ITreasury {
          function deposit(
              uint256 _amount,
              address _token,
              uint256 _profit
          ) external returns (uint256);
      
          function withdraw(uint256 _amount, address _token) external;
      
          function tokenValue(address _token, uint256 _amount) external view returns (uint256 value_);
      
          function mint(address _recipient, uint256 _amount) external;
      
          function manage(address _token, uint256 _amount) external;
      
          function incurDebt(uint256 amount_, address token_) external;
      
          function repayDebtWithReserve(uint256 amount_, address token_) external;
      
          function excessReserves() external view returns (uint256);
      }
      
      // File: interfaces/IERC20.sol
      
      
      pragma solidity >=0.7.5;
      
      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);
      }
      
      // File: libraries/SafeERC20.sol
      
      
      pragma solidity >=0.7.5;
      
      
      /// @notice Safe IERC20 and ETH transfer library that safely handles missing return values.
      /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/libraries/TransferHelper.sol)
      /// Taken from Solmate
      library SafeERC20 {
          function safeTransferFrom(
              IERC20 token,
              address from,
              address to,
              uint256 amount
          ) internal {
              (bool success, bytes memory data) = address(token).call(
                  abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount)
              );
      
              require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FROM_FAILED");
          }
      
          function safeTransfer(
              IERC20 token,
              address to,
              uint256 amount
          ) internal {
              (bool success, bytes memory data) = address(token).call(
                  abi.encodeWithSelector(IERC20.transfer.selector, to, amount)
              );
      
              require(success && (data.length == 0 || abi.decode(data, (bool))), "TRANSFER_FAILED");
          }
      
          function safeApprove(
              IERC20 token,
              address to,
              uint256 amount
          ) internal {
              (bool success, bytes memory data) = address(token).call(
                  abi.encodeWithSelector(IERC20.approve.selector, to, amount)
              );
      
              require(success && (data.length == 0 || abi.decode(data, (bool))), "APPROVE_FAILED");
          }
      
          function safeTransferETH(address to, uint256 amount) internal {
              (bool success, ) = to.call{value: amount}(new bytes(0));
      
              require(success, "ETH_TRANSFER_FAILED");
          }
      }
      // File: interfaces/IgOHM.sol
      
      
      pragma solidity >=0.7.5;
      
      
      interface IgOHM is IERC20 {
        function mint(address _to, uint256 _amount) external;
      
        function burn(address _from, uint256 _amount) external;
      
        function index() external view returns (uint256);
      
        function balanceFrom(uint256 _amount) external view returns (uint256);
      
        function balanceTo(uint256 _amount) external view returns (uint256);
      
        function migrate( address _staking, address _sOHM ) external;
      }
      
      // File: interfaces/IwsOHM.sol
      
      
      pragma solidity >=0.7.5;
      
      
      // Old wsOHM interface
      interface IwsOHM is IERC20 {
        function wrap(uint256 _amount) external returns (uint256);
      
        function unwrap(uint256 _amount) external returns (uint256);
      
        function wOHMTosOHM(uint256 _amount) external view returns (uint256);
      
        function sOHMTowOHM(uint256 _amount) external view returns (uint256);
      }
      
      // File: interfaces/IsOHM.sol
      
      
      pragma solidity >=0.7.5;
      
      
      interface IsOHM is IERC20 {
          function rebase( uint256 ohmProfit_, uint epoch_) external returns (uint256);
      
          function circulatingSupply() external view returns (uint256);
      
          function gonsForBalance( uint amount ) external view returns ( uint );
      
          function balanceForGons( uint gons ) external view returns ( uint );
      
          function index() external view returns ( uint );
      
          function toG(uint amount) external view returns (uint);
      
          function fromG(uint amount) external view returns (uint);
      
           function changeDebt(
              uint256 amount,
              address debtor,
              bool add
          ) external;
      
          function debtBalances(address _address) external view returns (uint256);
      }
      
      // File: gOHM.sol
      
      
      pragma solidity 0.7.5;
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      contract OlympusTokenMigrator is OlympusAccessControlled {
          using SafeMath for uint256;
          using SafeERC20 for IERC20;
          using SafeERC20 for IgOHM;
          using SafeERC20 for IsOHM;
          using SafeERC20 for IwsOHM;
      
          /* ========== MIGRATION ========== */
      
          event TimelockStarted(uint256 block, uint256 end);
          event Migrated(address staking, address treasury);
          event Funded(uint256 amount);
          event Defunded(uint256 amount);
      
          /* ========== STATE VARIABLES ========== */
      
          IERC20 public immutable oldOHM;
          IsOHM public immutable oldsOHM;
          IwsOHM public immutable oldwsOHM;
          ITreasuryV1 public immutable oldTreasury;
          IStakingV1 public immutable oldStaking;
      
          IUniswapV2Router public immutable sushiRouter;
          IUniswapV2Router public immutable uniRouter;
      
          IgOHM public gOHM;
          ITreasury public newTreasury;
          IStaking public newStaking;
          IERC20 public newOHM;
      
          bool public ohmMigrated;
          bool public shutdown;
      
          uint256 public immutable timelockLength;
          uint256 public timelockEnd;
      
          uint256 public oldSupply;
      
          constructor(
              address _oldOHM,
              address _oldsOHM,
              address _oldTreasury,
              address _oldStaking,
              address _oldwsOHM,
              address _sushi,
              address _uni,
              uint256 _timelock,
              address _authority
          ) OlympusAccessControlled(IOlympusAuthority(_authority)) {
              require(_oldOHM != address(0), "Zero address: OHM");
              oldOHM = IERC20(_oldOHM);
              require(_oldsOHM != address(0), "Zero address: sOHM");
              oldsOHM = IsOHM(_oldsOHM);
              require(_oldTreasury != address(0), "Zero address: Treasury");
              oldTreasury = ITreasuryV1(_oldTreasury);
              require(_oldStaking != address(0), "Zero address: Staking");
              oldStaking = IStakingV1(_oldStaking);
              require(_oldwsOHM != address(0), "Zero address: wsOHM");
              oldwsOHM = IwsOHM(_oldwsOHM);
              require(_sushi != address(0), "Zero address: Sushi");
              sushiRouter = IUniswapV2Router(_sushi);
              require(_uni != address(0), "Zero address: Uni");
              uniRouter = IUniswapV2Router(_uni);
              timelockLength = _timelock;
          }
      
          /* ========== MIGRATION ========== */
      
          enum TYPE {
              UNSTAKED,
              STAKED,
              WRAPPED
          }
      
          // migrate OHMv1, sOHMv1, or wsOHM for OHMv2, sOHMv2, or gOHM
          function migrate(
              uint256 _amount,
              TYPE _from,
              TYPE _to
          ) external {
              require(!shutdown, "Shut down");
      
              uint256 wAmount = oldwsOHM.sOHMTowOHM(_amount);
      
              if (_from == TYPE.UNSTAKED) {
                  require(ohmMigrated, "Only staked until migration");
                  oldOHM.safeTransferFrom(msg.sender, address(this), _amount);
              } else if (_from == TYPE.STAKED) {
                  oldsOHM.safeTransferFrom(msg.sender, address(this), _amount);
              } else {
                  oldwsOHM.safeTransferFrom(msg.sender, address(this), _amount);
                  wAmount = _amount;
              }
      
              if (ohmMigrated) {
                  require(oldSupply >= oldOHM.totalSupply(), "OHMv1 minted");
                  _send(wAmount, _to);
              } else {
                  gOHM.mint(msg.sender, wAmount);
              }
          }
      
          // migrate all olympus tokens held
          function migrateAll(TYPE _to) external {
              require(!shutdown, "Shut down");
      
              uint256 ohmBal = 0;
              uint256 sOHMBal = oldsOHM.balanceOf(msg.sender);
              uint256 wsOHMBal = oldwsOHM.balanceOf(msg.sender);
      
              if (oldOHM.balanceOf(msg.sender) > 0 && ohmMigrated) {
                  ohmBal = oldOHM.balanceOf(msg.sender);
                  oldOHM.safeTransferFrom(msg.sender, address(this), ohmBal);
              }
              if (sOHMBal > 0) {
                  oldsOHM.safeTransferFrom(msg.sender, address(this), sOHMBal);
              }
              if (wsOHMBal > 0) {
                  oldwsOHM.safeTransferFrom(msg.sender, address(this), wsOHMBal);
              }
      
              uint256 wAmount = wsOHMBal.add(oldwsOHM.sOHMTowOHM(ohmBal.add(sOHMBal)));
              if (ohmMigrated) {
                  require(oldSupply >= oldOHM.totalSupply(), "OHMv1 minted");
                  _send(wAmount, _to);
              } else {
                  gOHM.mint(msg.sender, wAmount);
              }
          }
      
          // send preferred token
          function _send(uint256 wAmount, TYPE _to) internal {
              if (_to == TYPE.WRAPPED) {
                  gOHM.safeTransfer(msg.sender, wAmount);
              } else if (_to == TYPE.STAKED) {
                  newStaking.unwrap(msg.sender, wAmount);
              } else if (_to == TYPE.UNSTAKED) {
                  newStaking.unstake(msg.sender, wAmount, false, false);
              }
          }
      
          // bridge back to OHM, sOHM, or wsOHM
          function bridgeBack(uint256 _amount, TYPE _to) external {
              if (!ohmMigrated) {
                  gOHM.burn(msg.sender, _amount);
              } else {
                  gOHM.safeTransferFrom(msg.sender, address(this), _amount);
              }
      
              uint256 amount = oldwsOHM.wOHMTosOHM(_amount);
              // error throws if contract does not have enough of type to send
              if (_to == TYPE.UNSTAKED) {
                  oldOHM.safeTransfer(msg.sender, amount);
              } else if (_to == TYPE.STAKED) {
                  oldsOHM.safeTransfer(msg.sender, amount);
              } else if (_to == TYPE.WRAPPED) {
                  oldwsOHM.safeTransfer(msg.sender, _amount);
              }
          }
      
          /* ========== OWNABLE ========== */
      
          // halt migrations (but not bridging back)
          function halt() external onlyPolicy {
              require(!ohmMigrated, "Migration has occurred");
              shutdown = !shutdown;
          }
      
          // withdraw backing of migrated OHM
          function defund(address reserve) external onlyGovernor {
              require(ohmMigrated, "Migration has not begun");
              require(timelockEnd < block.number && timelockEnd != 0, "Timelock not complete");
      
              oldwsOHM.unwrap(oldwsOHM.balanceOf(address(this)));
      
              uint256 amountToUnstake = oldsOHM.balanceOf(address(this));
              oldsOHM.approve(address(oldStaking), amountToUnstake);
              oldStaking.unstake(amountToUnstake, false);
      
              uint256 balance = oldOHM.balanceOf(address(this));
      
              if(balance > oldSupply) {
                  oldSupply = 0;
              } else {
                  oldSupply -= balance;
              }
      
              uint256 amountToWithdraw = balance.mul(1e9);
              oldOHM.approve(address(oldTreasury), amountToWithdraw);
              oldTreasury.withdraw(amountToWithdraw, reserve);
              IERC20(reserve).safeTransfer(address(newTreasury), IERC20(reserve).balanceOf(address(this)));
      
              emit Defunded(balance);
          }
      
          // start timelock to send backing to new treasury
          function startTimelock() external onlyGovernor {
              require(timelockEnd == 0, "Timelock set");
              timelockEnd = block.number.add(timelockLength);
      
              emit TimelockStarted(block.number, timelockEnd);
          }
      
          // set gOHM address
          function setgOHM(address _gOHM) external onlyGovernor {
              require(address(gOHM) == address(0), "Already set");
              require(_gOHM != address(0), "Zero address: gOHM");
      
              gOHM = IgOHM(_gOHM);
          }
      
          // call internal migrate token function
          function migrateToken(address token) external onlyGovernor {
              _migrateToken(token, false);
          }
      
          /**
           *   @notice Migrate LP and pair with new OHM
           */
          function migrateLP(
              address pair,
              bool sushi,
              address token,
              uint256 _minA,
              uint256 _minB
          ) external onlyGovernor {
              uint256 oldLPAmount = IERC20(pair).balanceOf(address(oldTreasury));
              oldTreasury.manage(pair, oldLPAmount);
      
              IUniswapV2Router router = sushiRouter;
              if (!sushi) {
                  router = uniRouter;
              }
      
              IERC20(pair).approve(address(router), oldLPAmount);
              (uint256 amountA, uint256 amountB) = router.removeLiquidity(
                  token, 
                  address(oldOHM), 
                  oldLPAmount,
                  _minA, 
                  _minB, 
                  address(this), 
                  block.timestamp
              );
      
              newTreasury.mint(address(this), amountB);
      
              IERC20(token).approve(address(router), amountA);
              newOHM.approve(address(router), amountB);
      
              router.addLiquidity(
                  token, 
                  address(newOHM), 
                  amountA, 
                  amountB, 
                  amountA, 
                  amountB, 
                  address(newTreasury), 
                  block.timestamp
              );
          }
      
          // Failsafe function to allow owner to withdraw funds sent directly to contract in case someone sends non-ohm tokens to the contract
          function withdrawToken(
              address tokenAddress,
              uint256 amount,
              address recipient
          ) external onlyGovernor {
              require(tokenAddress != address(0), "Token address cannot be 0x0");
              require(tokenAddress != address(gOHM), "Cannot withdraw: gOHM");
              require(tokenAddress != address(oldOHM), "Cannot withdraw: old-OHM");
              require(tokenAddress != address(oldsOHM), "Cannot withdraw: old-sOHM");
              require(tokenAddress != address(oldwsOHM), "Cannot withdraw: old-wsOHM");
              require(amount > 0, "Withdraw value must be greater than 0");
              if (recipient == address(0)) {
                  recipient = msg.sender; // if no address is specified the value will will be withdrawn to Owner
              }
      
              IERC20 tokenContract = IERC20(tokenAddress);
              uint256 contractBalance = tokenContract.balanceOf(address(this));
              if (amount > contractBalance) {
                  amount = contractBalance; // set the withdrawal amount equal to balance within the account.
              }
              // transfer the token from address of this contract
              tokenContract.safeTransfer(recipient, amount);
          }
      
          // migrate contracts
          function migrateContracts(
              address _newTreasury,
              address _newStaking,
              address _newOHM,
              address _newsOHM,
              address _reserve
          ) external onlyGovernor {
              require(!ohmMigrated, "Already migrated");
              ohmMigrated = true;
              shutdown = false;
      
              require(_newTreasury != address(0), "Zero address: Treasury");
              newTreasury = ITreasury(_newTreasury);
              require(_newStaking != address(0), "Zero address: Staking");
              newStaking = IStaking(_newStaking);
              require(_newOHM != address(0), "Zero address: OHM");
              newOHM = IERC20(_newOHM);
      
              oldSupply = oldOHM.totalSupply(); // log total supply at time of migration
      
              gOHM.migrate(_newStaking, _newsOHM); // change gOHM minter
      
              _migrateToken(_reserve, true); // will deposit tokens into new treasury so reserves can be accounted for
      
              _fund(oldsOHM.circulatingSupply()); // fund with current staked supply for token migration
      
              emit Migrated(_newStaking, _newTreasury);
          }
      
          /* ========== INTERNAL FUNCTIONS ========== */
      
          // fund contract with gOHM
          function _fund(uint256 _amount) internal {
              newTreasury.mint(address(this), _amount);
              newOHM.approve(address(newStaking), _amount);
              newStaking.stake(address(this), _amount, false, true); // stake and claim gOHM
      
              emit Funded(_amount);
          }
      
          /**
           *   @notice Migrate token from old treasury to new treasury
           */
          function _migrateToken(address token, bool deposit) internal {
              uint256 balance = IERC20(token).balanceOf(address(oldTreasury));
      
              uint256 excessReserves = oldTreasury.excessReserves();
              uint256 tokenValue = oldTreasury.valueOf(token, balance);
      
              if (tokenValue > excessReserves) {
                  tokenValue = excessReserves;
                  balance = excessReserves * 10**9;
              }
      
              oldTreasury.manage(token, balance);
      
              if (deposit) {
                  IERC20(token).safeApprove(address(newTreasury), balance);
                  newTreasury.deposit(balance, token, tokenValue);
              } else {
                  IERC20(token).safeTransfer(address(newTreasury), balance);
              }
          }
      }

      File 2 of 5: OlympusERC20Token
      // SPDX-License-Identifier: AGPL-3.0-or-later
      pragma solidity 0.7.5;
      
      /**
       * @dev Intended to update the TWAP for a token based on accepting an update call from that token.
       *  expectation is to have this happen in the _beforeTokenTransfer function of ERC20.
       *  Provides a method for a token to register its price sourve adaptor.
       *  Provides a function for a token to register its TWAP updater. Defaults to token itself.
       *  Provides a function a tokent to set its TWAP epoch.
       *  Implements automatic closeing and opening up a TWAP epoch when epoch ends.
       *  Provides a function to report the TWAP from the last epoch when passed a token address.
       */
      interface ITWAPOracle {
      
        function uniV2CompPairAddressForLastEpochUpdateBlockTimstamp( address ) external returns ( uint32 );
      
        function priceTokenAddressForPricingTokenAddressForLastEpochUpdateBlockTimstamp( address tokenToPrice_, address tokenForPriceComparison_, uint epochPeriod_ ) external returns ( uint32 );
      
        function pricedTokenForPricingTokenForEpochPeriodForPrice( address, address, uint ) external returns ( uint );
      
        function pricedTokenForPricingTokenForEpochPeriodForLastEpochPrice( address, address, uint ) external returns ( uint );
      
        function updateTWAP( address uniV2CompatPairAddressToUpdate_, uint eopchPeriodToUpdate_ ) external returns ( bool );
      }
      
      library EnumerableSet {
      
        // To implement this library for multiple types with as little code
        // repetition as possible, we write it in terms of a generic Set type with
        // bytes32 values.
        // The Set implementation uses private functions, and user-facing
        // implementations (such as AddressSet) are just wrappers around the
        // underlying Set.
        // This means that we can only create new EnumerableSets for types that fit
        // in bytes32.
        struct Set {
          // Storage of set values
          bytes32[] _values;
      
          // Position of the value in the `values` array, plus 1 because index 0
          // means a value is not in the set.
          mapping (bytes32 => uint256) _indexes;
        }
      
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function _add(Set storage set, bytes32 value) private returns (bool) {
          if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._indexes[value] = set._values.length;
            return true;
          } else {
            return false;
          }
        }
      
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function _remove(Set storage set, bytes32 value) private returns (bool) {
          // We read and store the value's index to prevent multiple reads from the same storage slot
          uint256 valueIndex = set._indexes[value];
      
          if (valueIndex != 0) { // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.
      
            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;
      
            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
            // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
      
            bytes32 lastvalue = set._values[lastIndex];
      
            // Move the last value to the index where the value to delete is
            set._values[toDeleteIndex] = lastvalue;
            // Update the index for the moved value
            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
      
            // Delete the slot where the moved value was stored
            set._values.pop();
      
            // Delete the index for the deleted slot
            delete set._indexes[value];
      
            return true;
          } else {
            return false;
          }
        }
      
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function _contains(Set storage set, bytes32 value) private view returns (bool) {
          return set._indexes[value] != 0;
        }
      
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function _length(Set storage set) private view returns (uint256) {
          return set._values.length;
        }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
        function _at(Set storage set, uint256 index) private view returns (bytes32) {
          require(set._values.length > index, "EnumerableSet: index out of bounds");
          return set._values[index];
        }
      
        function _getValues( Set storage set_ ) private view returns ( bytes32[] storage ) {
          return set_._values;
        }
      
        // TODO needs insert function that maintains order.
        // TODO needs NatSpec documentation comment.
        /**
         * Inserts new value by moving existing value at provided index to end of array and setting provided value at provided index
         */
        function _insert(Set storage set_, uint256 index_, bytes32 valueToInsert_ ) private returns ( bool ) {
          require(  set_._values.length > index_ );
          require( !_contains( set_, valueToInsert_ ), "Remove value you wish to insert if you wish to reorder array." );
          bytes32 existingValue_ = _at( set_, index_ );
          set_._values[index_] = valueToInsert_;
          return _add( set_, existingValue_);
        } 
      
        struct Bytes4Set {
          Set _inner;
        }
      
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(Bytes4Set storage set, bytes4 value) internal returns (bool) {
          return _add(set._inner, value);
        }
      
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(Bytes4Set storage set, bytes4 value) internal returns (bool) {
          return _remove(set._inner, value);
        }
      
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(Bytes4Set storage set, bytes4 value) internal view returns (bool) {
          return _contains(set._inner, value);
        }
      
        /**
         * @dev Returns the number of values on the set. O(1).
         */
        function length(Bytes4Set storage set) internal view returns (uint256) {
          return _length(set._inner);
        }
      
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(Bytes4Set storage set, uint256 index) internal view returns ( bytes4 ) {
          return bytes4( _at( set._inner, index ) );
        }
      
        function getValues( Bytes4Set storage set_ ) internal view returns ( bytes4[] memory ) {
          bytes4[] memory bytes4Array_;
          for( uint256 iteration_ = 0; _length( set_._inner ) > iteration_; iteration_++ ) {
            bytes4Array_[iteration_] = bytes4( _at( set_._inner, iteration_ ) );
          }
          return bytes4Array_;
        }
      
        function insert( Bytes4Set storage set_, uint256 index_, bytes4 valueToInsert_ ) internal returns ( bool ) {
          return _insert( set_._inner, index_, valueToInsert_ );
        }
      
          struct Bytes32Set {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _add(set._inner, value);
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
              return _remove(set._inner, value);
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
              return _contains(set._inner, value);
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(Bytes32Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(Bytes32Set storage set, uint256 index) internal view returns ( bytes32 ) {
              return _at(set._inner, index);
          }
      
        function getValues( Bytes32Set storage set_ ) internal view returns ( bytes4[] memory ) {
          bytes4[] memory bytes4Array_;
      
            for( uint256 iteration_ = 0; _length( set_._inner ) >= iteration_; iteration_++ ){
              bytes4Array_[iteration_] = bytes4( at( set_, iteration_ ) );
            }
      
            return bytes4Array_;
        }
      
        function insert( Bytes32Set storage set_, uint256 index_, bytes32 valueToInsert_ ) internal returns ( bool ) {
          return _insert( set_._inner, index_, valueToInsert_ );
        }
      
        // AddressSet
        struct AddressSet {
          Set _inner;
        }
      
        /**
         * @dev Add a value to a set. O(1).
         *
         * Returns true if the value was added to the set, that is if it was not
         * already present.
         */
        function add(AddressSet storage set, address value) internal returns (bool) {
          return _add(set._inner, bytes32(uint256(value)));
        }
      
        /**
         * @dev Removes a value from a set. O(1).
         *
         * Returns true if the value was removed from the set, that is if it was
         * present.
         */
        function remove(AddressSet storage set, address value) internal returns (bool) {
          return _remove(set._inner, bytes32(uint256(value)));
        }
      
        /**
         * @dev Returns true if the value is in the set. O(1).
         */
        function contains(AddressSet storage set, address value) internal view returns (bool) {
          return _contains(set._inner, bytes32(uint256(value)));
        }
      
        /**
         * @dev Returns the number of values in the set. O(1).
         */
        function length(AddressSet storage set) internal view returns (uint256) {
          return _length(set._inner);
        }
      
        /**
         * @dev Returns the value stored at position `index` in the set. O(1).
         *
         * Note that there are no guarantees on the ordering of values inside the
         * array, and it may change when more values are added or removed.
         *
         * Requirements:
         *
         * - `index` must be strictly less than {length}.
         */
        function at(AddressSet storage set, uint256 index) internal view returns (address) {
          return address(uint256(_at(set._inner, index)));
        }
      
        /**
         * TODO Might require explicit conversion of bytes32[] to address[].
         *  Might require iteration.
         */
        function getValues( AddressSet storage set_ ) internal view returns ( address[] memory ) {
      
          address[] memory addressArray;
      
          for( uint256 iteration_ = 0; _length(set_._inner) >= iteration_; iteration_++ ){
            addressArray[iteration_] = at( set_, iteration_ );
          }
      
          return addressArray;
        }
      
        function insert(AddressSet storage set_, uint256 index_, address valueToInsert_ ) internal returns ( bool ) {
          return _insert( set_._inner, index_, bytes32(uint256(valueToInsert_)) );
        }
      
      
          // UintSet
      
          struct UintSet {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UintSet storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UintSet storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UintSet storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UintSet storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
         /**
          * @dev Returns the value stored at position `index` in the set. O(1).
          *
          * Note that there are no guarantees on the ordering of values inside the
          * array, and it may change when more values are added or removed.
          *
          * Requirements:
          *
          * - `index` must be strictly less than {length}.
          */
          function at(UintSet storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
      
          struct UInt256Set {
              Set _inner;
          }
      
          /**
           * @dev Add a value to a set. O(1).
           *
           * Returns true if the value was added to the set, that is if it was not
           * already present.
           */
          function add(UInt256Set storage set, uint256 value) internal returns (bool) {
              return _add(set._inner, bytes32(value));
          }
      
          /**
           * @dev Removes a value from a set. O(1).
           *
           * Returns true if the value was removed from the set, that is if it was
           * present.
           */
          function remove(UInt256Set storage set, uint256 value) internal returns (bool) {
              return _remove(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns true if the value is in the set. O(1).
           */
          function contains(UInt256Set storage set, uint256 value) internal view returns (bool) {
              return _contains(set._inner, bytes32(value));
          }
      
          /**
           * @dev Returns the number of values on the set. O(1).
           */
          function length(UInt256Set storage set) internal view returns (uint256) {
              return _length(set._inner);
          }
      
          /**
           * @dev Returns the value stored at position `index` in the set. O(1).
           *
           * Note that there are no guarantees on the ordering of values inside the
           * array, and it may change when more values are added or removed.
           *
           * Requirements:
           *
           * - `index` must be strictly less than {length}.
           */
          function at(UInt256Set storage set, uint256 index) internal view returns (uint256) {
              return uint256(_at(set._inner, index));
          }
      }
      
      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.
           */
          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.
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              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.
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      
          // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
          function sqrrt(uint256 a) internal pure returns (uint c) {
              if (a > 3) {
                  c = a;
                  uint b = add( div( a, 2), 1 );
                  while (b < c) {
                      c = b;
                      b = div( add( div( a, b ), b), 2 );
                  }
              } else if (a != 0) {
                  c = 1;
              }
          }
      
          /*
           * Expects percentage to be trailed by 00,
          */
          function percentageAmount( uint256 total_, uint8 percentage_ ) internal pure returns ( uint256 percentAmount_ ) {
              return div( mul( total_, percentage_ ), 1000 );
          }
      
          /*
           * Expects percentage to be trailed by 00,
          */
          function substractPercentage( uint256 total_, uint8 percentageToSub_ ) internal pure returns ( uint256 result_ ) {
              return sub( total_, div( mul( total_, percentageToSub_ ), 1000 ) );
          }
      
          function percentageOfTotal( uint256 part_, uint256 total_ ) internal pure returns ( uint256 percent_ ) {
              return div( mul(part_, 100) , total_ );
          }
      
          /**
           * Taken from Hypersonic https://github.com/M2629/HyperSonic/blob/main/Math.sol
           * @dev Returns the average of two numbers. The result is rounded towards
           * zero.
           */
          function average(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b) / 2 can overflow, so we distribute
              return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
          }
      
          function quadraticPricing( uint256 payment_, uint256 multiplier_ ) internal pure returns (uint256) {
              return sqrrt( mul( multiplier_, payment_ ) );
          }
      
        function bondingCurve( uint256 supply_, uint256 multiplier_ ) internal pure returns (uint256) {
            return mul( multiplier_, supply_ );
        }
      }
      
      abstract contract ERC20
        is 
          IERC20
        {
      
        using SafeMath for uint256;
      
        // TODO comment actual hash value.
        bytes32 constant private ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256( "ERC20Token" );
          
        // Present in ERC777
        mapping (address => uint256) internal _balances;
      
        // Present in ERC777
        mapping (address => mapping (address => uint256)) internal _allowances;
      
        // Present in ERC777
        uint256 internal _totalSupply;
      
        // Present in ERC777
        string internal _name;
          
        // Present in ERC777
        string internal _symbol;
          
        // Present in ERC777
        uint8 internal _decimals;
      
        /**
         * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
         * a default value of 18.
         *
         * To select a different value for {decimals}, use {_setupDecimals}.
         *
         * All three of these values are immutable: they can only be set once during
         * construction.
         */
        constructor (string memory name_, string memory symbol_, uint8 decimals_) {
          _name = name_;
          _symbol = symbol_;
          _decimals = decimals_;
        }
      
        /**
         * @dev Returns the name of the token.
         */
        // Present in ERC777
        function name() public view returns (string memory) {
          return _name;
        }
      
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        // Present in ERC777
        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. This is the value {ERC20} uses, unless {_setupDecimals} is
         * called.
         *
         * 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}.
         */
        // Present in ERC777
        function decimals() public view returns (uint8) {
          return _decimals;
        }
      
        /**
         * @dev See {IERC20-totalSupply}.
         */
        // Present in ERC777
        function totalSupply() public view override returns (uint256) {
          return _totalSupply;
        }
      
        /**
         * @dev See {IERC20-balanceOf}.
         */
        // Present in ERC777
        function balanceOf(address account) public view virtual override 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`.
         */
        // Overrideen in ERC777
        // Confirm that this behavior changes 
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
          _transfer(msg.sender, recipient, amount);
          return true;
        }
      
          /**
           * @dev See {IERC20-allowance}.
           */
          // Present in ERC777
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
      
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          // Present in ERC777
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(msg.sender, 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`.
           */
          // Present in ERC777
          function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, msg.sender, _allowances[sender][msg.sender].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 virtual returns (bool) {
              _approve(msg.sender, spender, _allowances[msg.sender][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 virtual returns (bool) {
              _approve(msg.sender, spender, _allowances[msg.sender][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 virtual {
          require(sender != address(0), "ERC20: transfer from the zero address");
          require(recipient != address(0), "ERC20: transfer to the zero address");
      
          _beforeTokenTransfer(sender, recipient, amount);
      
          _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.
           */
          // Present in ERC777
          function _mint(address account_, uint256 amount_) internal virtual {
              require(account_ != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address( this ), account_, amount_);
              _totalSupply = _totalSupply.add(amount_);
              _balances[account_] = _balances[account_].add(amount_);
              emit Transfer(address( this ), 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.
           */
          // Present in ERC777
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
      
              _beforeTokenTransfer(account, address(0), amount);
      
              _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 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.
           */
          // Present in ERC777
          function _approve(address owner, address spender, uint256 amount) internal virtual {
              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 Sets {decimals} to a value other than the default one of 18.
           *
           * WARNING: This function should only be called from the constructor. Most
           * applications that interact with token contracts will not expect
           * {decimals} to ever change, and may work incorrectly if it does.
           */
          // Considering deprication to reduce size of bytecode as changing _decimals to internal acheived the same functionality.
          // function _setupDecimals(uint8 decimals_) internal {
          //     _decimals = decimals_;
          // }
      
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be to transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        // Present in ERC777
        function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { }
      }
      
      library Counters {
          using SafeMath for uint256;
      
          struct Counter {
              // This variable should never be directly accessed by users of the library: interactions must be restricted to
              // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
              // this feature: see https://github.com/ethereum/solidity/issues/4637
              uint256 _value; // default: 0
          }
      
          function current(Counter storage counter) internal view returns (uint256) {
              return counter._value;
          }
      
          function increment(Counter storage counter) internal {
              // The {SafeMath} overflow check can be skipped here, see the comment at the top
              counter._value += 1;
          }
      
          function decrement(Counter storage counter) internal {
              counter._value = counter._value.sub(1);
          }
      }
      
      interface IERC2612Permit {
          /**
           * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
           * given `owner`'s signed approval.
           *
           * IMPORTANT: The same issues {IERC20-approve} has related to transaction
           * ordering also apply here.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           * - `deadline` must be a timestamp in the future.
           * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
           * over the EIP712-formatted function arguments.
           * - the signature must use ``owner``'s current nonce (see {nonces}).
           *
           * For more information on the signature format, see the
           * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
           * section].
           */
          function permit(
              address owner,
              address spender,
              uint256 amount,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external;
      
          /**
           * @dev Returns the current ERC2612 nonce for `owner`. This value must be
           * included whenever a signature is generated for {permit}.
           *
           * Every successful call to {permit} increases ``owner``'s nonce by one. This
           * prevents a signature from being used multiple times.
           */
          function nonces(address owner) external view returns (uint256);
      }
      
      abstract contract ERC20Permit is ERC20, IERC2612Permit {
          using Counters for Counters.Counter;
      
          mapping(address => Counters.Counter) private _nonces;
      
          // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
          bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
      
          bytes32 public DOMAIN_SEPARATOR;
      
          constructor() {
              uint256 chainID;
              assembly {
                  chainID := chainid()
              }
      
              DOMAIN_SEPARATOR = keccak256(
                  abi.encode(
                      keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                      keccak256(bytes(name())),
                      keccak256(bytes("1")), // Version
                      chainID,
                      address(this)
                  )
              );
          }
      
          /**
           * @dev See {IERC2612Permit-permit}.
           *
           */
          function permit(
              address owner,
              address spender,
              uint256 amount,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual override {
              require(block.timestamp <= deadline, "Permit: expired deadline");
      
              bytes32 hashStruct =
                  keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline));
      
              bytes32 _hash = keccak256(abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct));
      
              address signer = ecrecover(_hash, v, r, s);
              require(signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature");
      
              _nonces[owner].increment();
              _approve(owner, spender, amount);
          }
      
          /**
           * @dev See {IERC2612Permit-nonces}.
           */
          function nonces(address owner) public view override returns (uint256) {
              return _nonces[owner].current();
          }
      }
      
      interface IOwnable {
      
        function owner() external view returns (address);
      
        function renounceOwnership() external;
        
        function transferOwnership( address newOwner_ ) external;
      }
      
      contract Ownable is IOwnable {
          
        address internal _owner;
      
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
      
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor () {
          _owner = msg.sender;
          emit OwnershipTransferred( address(0), _owner );
        }
      
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view override returns (address) {
          return _owner;
        }
      
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
          require( _owner == msg.sender, "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 override onlyOwner() {
          emit OwnershipTransferred( _owner, address(0) );
          _owner = 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 override onlyOwner() {
          require( newOwner_ != address(0), "Ownable: new owner is the zero address");
          emit OwnershipTransferred( _owner, newOwner_ );
          _owner = newOwner_;
        }
      }
      
      contract VaultOwned is Ownable {
          
        address internal _vault;
      
        function setVault( address vault_ ) external onlyOwner() returns ( bool ) {
          _vault = vault_;
      
          return true;
        }
      
        /**
         * @dev Returns the address of the current vault.
         */
        function vault() public view returns (address) {
          return _vault;
        }
      
        /**
         * @dev Throws if called by any account other than the vault.
         */
        modifier onlyVault() {
          require( _vault == msg.sender, "VaultOwned: caller is not the Vault" );
          _;
        }
      
      }
      
      contract TWAPOracleUpdater is ERC20Permit, VaultOwned {
      
        using EnumerableSet for EnumerableSet.AddressSet;
      
        event TWAPOracleChanged( address indexed previousTWAPOracle, address indexed newTWAPOracle );
        event TWAPEpochChanged( uint previousTWAPEpochPeriod, uint newTWAPEpochPeriod );
        event TWAPSourceAdded( address indexed newTWAPSource );
        event TWAPSourceRemoved( address indexed removedTWAPSource );
          
        EnumerableSet.AddressSet private _dexPoolsTWAPSources;
      
        ITWAPOracle public twapOracle;
      
        uint public twapEpochPeriod;
      
        constructor(
              string memory name_,
              string memory symbol_,
              uint8 decimals_
          ) ERC20(name_, symbol_, decimals_) {
          }
      
        function changeTWAPOracle( address newTWAPOracle_ ) external onlyOwner() {
          emit TWAPOracleChanged( address(twapOracle), newTWAPOracle_);
          twapOracle = ITWAPOracle( newTWAPOracle_ );
        }
      
        function changeTWAPEpochPeriod( uint newTWAPEpochPeriod_ ) external onlyOwner() {
          require( newTWAPEpochPeriod_ > 0, "TWAPOracleUpdater: TWAP Epoch period must be greater than 0." );
          emit TWAPEpochChanged( twapEpochPeriod, newTWAPEpochPeriod_ );
          twapEpochPeriod = newTWAPEpochPeriod_;
        }
      
        function addTWAPSource( address newTWAPSourceDexPool_ ) external onlyOwner() {
          require( _dexPoolsTWAPSources.add( newTWAPSourceDexPool_ ), "OlympusERC20TOken: TWAP Source already stored." );
          emit TWAPSourceAdded( newTWAPSourceDexPool_ );
        }
      
        function removeTWAPSource( address twapSourceToRemove_ ) external onlyOwner() {
          require( _dexPoolsTWAPSources.remove( twapSourceToRemove_ ), "OlympusERC20TOken: TWAP source not present." );
          emit TWAPSourceRemoved( twapSourceToRemove_ );
        }
      
        function _uodateTWAPOracle( address dexPoolToUpdateFrom_, uint twapEpochPeriodToUpdate_ ) internal {
          if ( _dexPoolsTWAPSources.contains( dexPoolToUpdateFrom_ )) {
            twapOracle.updateTWAP( dexPoolToUpdateFrom_, twapEpochPeriodToUpdate_ );
          }
        }
      
        function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal override virtual {
            if( _dexPoolsTWAPSources.contains( from_ ) ) {
              _uodateTWAPOracle( from_, twapEpochPeriod );
            } else {
              if ( _dexPoolsTWAPSources.contains( to_ ) ) {
                _uodateTWAPOracle( to_, twapEpochPeriod );
              }
            }
          }
      }
      
      contract Divine is TWAPOracleUpdater {
        constructor(
          string memory name_,
          string memory symbol_,
          uint8 decimals_
        ) TWAPOracleUpdater(name_, symbol_, decimals_) {
        }
      }
      
      contract OlympusERC20Token is Divine {
      
        using SafeMath for uint256;
      
          constructor() Divine("Olympus", "OHM", 9) {
          }
      
          function mint(address account_, uint256 amount_) external onlyVault() {
              _mint(account_, amount_);
          }
      
          /**
           * @dev Destroys `amount` tokens from the caller.
           *
           * See {ERC20-_burn}.
           */
          function burn(uint256 amount) public virtual {
              _burn(msg.sender, amount);
          }
      
          // function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal override virtual {
          //   if( _dexPoolsTWAPSources.contains( from_ ) ) {
          //     _uodateTWAPOracle( from_, twapEpochPeriod );
          //   } else {
          //     if ( _dexPoolsTWAPSources.contains( to_ ) ) {
          //       _uodateTWAPOracle( to_, twapEpochPeriod );
          //     }
          //   }
          // }
      
          /*
           * @dev Destroys `amount` tokens from `account`, deducting from the caller's
           * allowance.
           *
           * See {ERC20-_burn} and {ERC20-allowance}.
           *
           * Requirements:
           *
           * - the caller must have allowance for ``accounts``'s tokens of at least
           * `amount`.
           */
           
          function burnFrom(address account_, uint256 amount_) public virtual {
              _burnFrom(account_, amount_);
          }
      
          function _burnFrom(address account_, uint256 amount_) public virtual {
              uint256 decreasedAllowance_ =
                  allowance(account_, msg.sender).sub(
                      amount_,
                      "ERC20: burn amount exceeds allowance"
                  );
      
              _approve(account_, msg.sender, decreasedAllowance_);
              _burn(account_, amount_);
          }
      }

      File 3 of 5: gOHM
      // SPDX-License-Identifier: AGPL-3.0-or-later
      
      // File: interfaces/IERC20.sol
      
      
      pragma solidity >=0.7.5;
      
      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);
      }
      
      // File: interfaces/IgOHM.sol
      
      
      pragma solidity >=0.7.5;
      
      
      interface IgOHM is IERC20 {
        function mint(address _to, uint256 _amount) external;
      
        function burn(address _from, uint256 _amount) external;
      
        function index() external view returns (uint256);
      
        function balanceFrom(uint256 _amount) external view returns (uint256);
      
        function balanceTo(uint256 _amount) external view returns (uint256);
      
        function migrate( address _staking, address _sOHM ) external;
      }
      
      // File: interfaces/IsOHM.sol
      
      
      pragma solidity >=0.7.5;
      
      
      interface IsOHM is IERC20 {
          function rebase( uint256 ohmProfit_, uint epoch_) external returns (uint256);
      
          function circulatingSupply() external view returns (uint256);
      
          function gonsForBalance( uint amount ) external view returns ( uint );
      
          function balanceForGons( uint gons ) external view returns ( uint );
      
          function index() external view returns ( uint );
      
          function toG(uint amount) external view returns (uint);
      
          function fromG(uint amount) external view returns (uint);
      
           function changeDebt(
              uint256 amount,
              address debtor,
              bool add
          ) external;
      
          function debtBalances(address _address) external view returns (uint256);
      }
      
      // File: libraries/Address.sol
      
      
      pragma solidity 0.7.5;
      
      
      // TODO(zx): replace with OZ implementation.
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies in extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
      
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
      
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
      
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return _functionCallWithValue(target, data, 0, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          // function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
          //     require(address(this).balance >= value, "Address: insufficient balance for call");
          //     return _functionCallWithValue(target, data, value, errorMessage);
          // }
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
              require(isContract(target), "Address: call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
      
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      
        /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.3._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.3._
           */
          function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
      
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      
          function addressToString(address _address) internal pure returns(string memory) {
              bytes32 _bytes = bytes32(uint256(_address));
              bytes memory HEX = "0123456789abcdef";
              bytes memory _addr = new bytes(42);
      
              _addr[0] = '0';
              _addr[1] = 'x';
      
              for(uint256 i = 0; i < 20; i++) {
                  _addr[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)];
                  _addr[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)];
              }
      
              return string(_addr);
      
          }
      }
      // File: libraries/SafeMath.sol
      
      
      pragma solidity ^0.7.5;
      
      
      // TODO(zx): Replace all instances of SafeMath with OZ implementation
      library SafeMath {
      
          function add(uint256 a, uint256 b) internal pure returns (uint256) {
              uint256 c = a + b;
              require(c >= a, "SafeMath: addition overflow");
      
              return c;
          }
      
          function sub(uint256 a, uint256 b) internal pure returns (uint256) {
              return sub(a, b, "SafeMath: subtraction overflow");
          }
      
          function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b <= a, errorMessage);
              uint256 c = a - b;
      
              return c;
          }
      
          function mul(uint256 a, uint256 b) internal pure returns (uint256) {
              if (a == 0) {
                  return 0;
              }
      
              uint256 c = a * b;
              require(c / a == b, "SafeMath: multiplication overflow");
      
              return c;
          }
      
          function div(uint256 a, uint256 b) internal pure returns (uint256) {
              return div(a, b, "SafeMath: division by zero");
          }
      
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              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;
          }
      
          // Only used in the  BondingCalculator.sol
          function sqrrt(uint256 a) internal pure returns (uint c) {
              if (a > 3) {
                  c = a;
                  uint b = add( div( a, 2), 1 );
                  while (b < c) {
                      c = b;
                      b = div( add( div( a, b ), b), 2 );
                  }
              } else if (a != 0) {
                  c = 1;
              }
          }
      
      }
      // File: types/ERC20.sol
      
      
      pragma solidity 0.7.5;
      
      
      
      
      abstract contract ERC20 is IERC20 {
      
          using SafeMath for uint256;
      
          // TODO comment actual hash value.
          bytes32 constant private ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256( "ERC20Token" );
          
          mapping (address => uint256) internal _balances;
      
          mapping (address => mapping (address => uint256)) internal _allowances;
      
          uint256 internal _totalSupply;
      
          string internal _name;
          
          string internal _symbol;
          
          uint8 internal _decimals;
      
          constructor (string memory name_, string memory symbol_, uint8 decimals_) {
              _name = name_;
              _symbol = symbol_;
              _decimals = decimals_;
          }
      
          function name() public view returns (string memory) {
              return _name;
          }
      
          function symbol() public view returns (string memory) {
              return _symbol;
          }
      
          function decimals() public view virtual returns (uint8) {
              return _decimals;
          }
      
          function totalSupply() public view override returns (uint256) {
              return _totalSupply;
          }
      
          function balanceOf(address account) public view virtual override returns (uint256) {
              return _balances[account];
          }
      
          function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(msg.sender, recipient, amount);
              return true;
          }
      
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
      
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(msg.sender, spender, amount);
              return true;
          }
      
          function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance"));
              return true;
          }
      
          function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
              _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
              return true;
          }
      
          function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
              _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
              return true;
          }
      
          function _transfer(address sender, address recipient, uint256 amount) internal virtual {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
      
              _beforeTokenTransfer(sender, recipient, amount);
      
              _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
              _balances[recipient] = _balances[recipient].add(amount);
              emit Transfer(sender, recipient, amount);
          }
      
          function _mint(address account_, uint256 ammount_) internal virtual {
              require(account_ != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address( this ), account_, ammount_);
              _totalSupply = _totalSupply.add(ammount_);
              _balances[account_] = _balances[account_].add(ammount_);
              emit Transfer(address( this ), account_, ammount_);
          }
      
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
      
              _beforeTokenTransfer(account, address(0), amount);
      
              _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
              _totalSupply = _totalSupply.sub(amount);
              emit Transfer(account, address(0), amount);
          }
      
          function _approve(address owner, address spender, uint256 amount) internal virtual {
              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);
          }
      
        function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { }
      }
      
      // File: gOHM.sol
      
      
      pragma solidity ^0.7.5;
      
      
      
      
      
      
      contract gOHM is IgOHM, ERC20 {
      
          /* ========== DEPENDENCIES ========== */
      
          using Address for address;
          using SafeMath for uint256;
      
          /* ========== MODIFIERS ========== */
      
          modifier onlyApproved() {
              require(msg.sender == approved, "Only approved");
              _;
          }
      
          /* ========== EVENTS ========== */
      
          event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
          event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
      
          /* ========== DATA STRUCTURES ========== */
      
          /// @notice A checkpoint for marking number of votes from a given block
          struct Checkpoint {
              uint256 fromBlock;
              uint256 votes;
          }
      
          /* ========== STATE VARIABLES ========== */
      
          IsOHM public sOHM;
          address public approved; // minter
          bool public migrated;
      
          mapping(address => mapping(uint256 => Checkpoint)) public checkpoints;
          mapping(address => uint256) public numCheckpoints;
          mapping(address => address) public delegates;
      
          /* ========== CONSTRUCTOR ========== */
      
          constructor(address _migrator, address _sOHM)
              ERC20("Governance OHM", "gOHM", 18)
          {
              require(_migrator != address(0), "Zero address: Migrator");
              approved = _migrator;
              require(_sOHM != address(0), "Zero address: sOHM");
              sOHM = IsOHM(_sOHM);
          }
      
          /* ========== MUTATIVE FUNCTIONS ========== */
      
          /**
           * @notice transfer mint rights from migrator to staking
           * @notice can only be done once, at the time of contract migration
           * @param _staking address
           * @param _sOHM address
           */
          function migrate(address _staking, address _sOHM) external override onlyApproved {
              require(!migrated, "Migrated");
              migrated = true;
      
              require(_staking != approved, "Invalid argument");
              require(_staking != address(0), "Zero address found");
              approved = _staking;
      
              require(_sOHM != address(0), "Zero address found");
              sOHM = IsOHM(_sOHM);
          }
      
          /**
           * @notice Delegate votes from `msg.sender` to `delegatee`
           * @param delegatee The address to delegate votes to
           */
          function delegate(address delegatee) external {
              return _delegate(msg.sender, delegatee);
          }
      
          /**
              @notice mint gOHM
              @param _to address
              @param _amount uint
           */
          function mint(address _to, uint256 _amount) external override onlyApproved {
              _mint(_to, _amount);
          }
      
          /**
              @notice burn gOHM
              @param _from address
              @param _amount uint
           */
          function burn(address _from, uint256 _amount) external override onlyApproved {
              _burn(_from, _amount);
          }
      
          /* ========== VIEW FUNCTIONS ========== */
      
          /**
           * @notice pull index from sOHM token
           */
          function index() public view override returns (uint256) {
              return sOHM.index();
          }
      
          /**
              @notice converts gOHM amount to OHM
              @param _amount uint
              @return uint
           */
          function balanceFrom(uint256 _amount) public view override returns (uint256) {
              return _amount.mul(index()).div(10**decimals());
          }
      
          /**
              @notice converts OHM amount to gOHM
              @param _amount uint
              @return uint
           */
          function balanceTo(uint256 _amount) public view override returns (uint256) {
              return _amount.mul(10**decimals()).div(index());
          }
      
          /**
           * @notice Gets the current votes balance for `account`
           * @param account The address to get votes balance
           * @return The number of current votes for `account`
           */
          function getCurrentVotes(address account) external view returns (uint256) {
              uint256 nCheckpoints = numCheckpoints[account];
              return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
          }
      
          /**
           * @notice Determine the prior number of votes for an account as of a block number
           * @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
           * @param account The address of the account to check
           * @param blockNumber The block number to get the vote balance at
           * @return The number of votes the account had as of the given block
           */
          function getPriorVotes(address account, uint256 blockNumber) external view returns (uint256) {
              require(blockNumber < block.number, "gOHM::getPriorVotes: not yet determined");
      
              uint256 nCheckpoints = numCheckpoints[account];
              if (nCheckpoints == 0) {
                  return 0;
              }
      
              // First check most recent balance
              if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
                  return checkpoints[account][nCheckpoints - 1].votes;
              }
      
              // Next check implicit zero balance
              if (checkpoints[account][0].fromBlock > blockNumber) {
                  return 0;
              }
      
              uint256 lower = 0;
              uint256 upper = nCheckpoints - 1;
              while (upper > lower) {
                  uint256 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
                  Checkpoint memory cp = checkpoints[account][center];
                  if (cp.fromBlock == blockNumber) {
                      return cp.votes;
                  } else if (cp.fromBlock < blockNumber) {
                      lower = center;
                  } else {
                      upper = center - 1;
                  }
              }
              return checkpoints[account][lower].votes;
          }
      
          /* ========== INTERNAL FUNCTIONS ========== */
      
          function _delegate(address delegator, address delegatee) internal {
              address currentDelegate = delegates[delegator];
              uint256 delegatorBalance = _balances[delegator];
              delegates[delegator] = delegatee;
      
              emit DelegateChanged(delegator, currentDelegate, delegatee);
      
              _moveDelegates(currentDelegate, delegatee, delegatorBalance);
          }
      
          function _moveDelegates(
              address srcRep,
              address dstRep,
              uint256 amount
          ) internal {
              if (srcRep != dstRep && amount > 0) {
                  if (srcRep != address(0)) {
                      uint256 srcRepNum = numCheckpoints[srcRep];
                      uint256 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                      uint256 srcRepNew = srcRepOld.sub(amount);
                      _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
                  }
      
                  if (dstRep != address(0)) {
                      uint256 dstRepNum = numCheckpoints[dstRep];
                      uint256 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                      uint256 dstRepNew = dstRepOld.add(amount);
                      _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
                  }
              }
          }
      
          function _writeCheckpoint(
              address delegatee,
              uint256 nCheckpoints,
              uint256 oldVotes,
              uint256 newVotes
          ) internal {
              if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == block.number) {
                  checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
              } else {
                  checkpoints[delegatee][nCheckpoints] = Checkpoint(block.number, newVotes);
                  numCheckpoints[delegatee] = nCheckpoints + 1;
              }
      
              emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
          }
      
          /**
              @notice Ensure delegation moves when token is transferred.
           */
          function _beforeTokenTransfer(
              address from,
              address to,
              uint256 amount
          ) internal override {
              _moveDelegates(delegates[from], delegates[to], amount);
          }
      }

      File 4 of 5: sOlympus
      // SPDX-License-Identifier: AGPL-3.0-or-later
      pragma solidity 0.7.5;
      
      
      /**
       * @dev Wrappers over Solidity's arithmetic operations with added overflow
       * checks.
       *
       * Arithmetic operations in Solidity wrap on overflow. This can easily result
       * in bugs, because programmers usually assume that an overflow raises an
       * error, which is the standard behavior in high level programming languages.
       * `SafeMath` restores this intuition by reverting the transaction when an
       * operation overflows.
       *
       * Using this library instead of the unchecked operations eliminates an entire
       * class of bugs, so it's recommended to use it always.
       */
      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.
           */
          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.
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              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.
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      
          // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
          function sqrrt(uint256 a) internal pure returns (uint c) {
              if (a > 3) {
                  c = a;
                  uint b = add( div( a, 2), 1 );
                  while (b < c) {
                      c = b;
                      b = div( add( div( a, b ), b), 2 );
                  }
              } else if (a != 0) {
                  c = 1;
              }
          }
      
          /*
           * Expects percentage to be trailed by 00,
          */
          function percentageAmount( uint256 total_, uint8 percentage_ ) internal pure returns ( uint256 percentAmount_ ) {
              return div( mul( total_, percentage_ ), 1000 );
          }
      
          /*
           * Expects percentage to be trailed by 00,
          */
          function substractPercentage( uint256 total_, uint8 percentageToSub_ ) internal pure returns ( uint256 result_ ) {
              return sub( total_, div( mul( total_, percentageToSub_ ), 1000 ) );
          }
      
          function percentageOfTotal( uint256 part_, uint256 total_ ) internal pure returns ( uint256 percent_ ) {
              return div( mul(part_, 100) , total_ );
          }
      
          /**
           * Taken from Hypersonic https://github.com/M2629/HyperSonic/blob/main/Math.sol
           * @dev Returns the average of two numbers. The result is rounded towards
           * zero.
           */
          function average(uint256 a, uint256 b) internal pure returns (uint256) {
              // (a + b) / 2 can overflow, so we distribute
              return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
          }
      
          function quadraticPricing( uint256 payment_, uint256 multiplier_ ) internal pure returns (uint256) {
              return sqrrt( mul( multiplier_, payment_ ) );
          }
      
        function bondingCurve( uint256 supply_, uint256 multiplier_ ) internal pure returns (uint256) {
            return mul( multiplier_, supply_ );
        }
      }
      
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies in extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
      
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
      
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
      
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return _functionCallWithValue(target, data, 0, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          // function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
          //     require(address(this).balance >= value, "Address: insufficient balance for call");
          //     return _functionCallWithValue(target, data, value, errorMessage);
          // }
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              require(isContract(target), "Address: call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: value }(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
              require(isContract(target), "Address: call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
      
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      
        /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
              return functionStaticCall(target, data, "Address: low-level static call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a static call.
           *
           * _Available since v3.3._
           */
          function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
              require(isContract(target), "Address: static call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.staticcall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.3._
           */
          function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
              return functionDelegateCall(target, data, "Address: low-level delegate call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
           * but performing a delegate call.
           *
           * _Available since v3.3._
           */
          function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              require(isContract(target), "Address: delegate call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.delegatecall(data);
              return _verifyCallResult(success, returndata, errorMessage);
          }
      
          function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
      
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      
          function addressToString(address _address) internal pure returns(string memory) {
              bytes32 _bytes = bytes32(uint256(_address));
              bytes memory HEX = "0123456789abcdef";
              bytes memory _addr = new bytes(42);
      
              _addr[0] = '0';
              _addr[1] = 'x';
      
              for(uint256 i = 0; i < 20; i++) {
                  _addr[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)];
                  _addr[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)];
              }
      
              return string(_addr);
      
          }
      }
      
      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);
      }
      
      abstract contract ERC20
        is 
          IERC20
        {
      
        using SafeMath for uint256;
      
        // TODO comment actual hash value.
        bytes32 constant private ERC20TOKEN_ERC1820_INTERFACE_ID = keccak256( "ERC20Token" );
          
        // Present in ERC777
        mapping (address => uint256) internal _balances;
      
        // Present in ERC777
        mapping (address => mapping (address => uint256)) internal _allowances;
      
        // Present in ERC777
        uint256 internal _totalSupply;
      
        // Present in ERC777
        string internal _name;
          
        // Present in ERC777
        string internal _symbol;
          
        // Present in ERC777
        uint8 internal _decimals;
      
        /**
         * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
         * a default value of 18.
         *
         * To select a different value for {decimals}, use {_setupDecimals}.
         *
         * All three of these values are immutable: they can only be set once during
         * construction.
         */
        constructor (string memory name_, string memory symbol_, uint8 decimals_) {
          _name = name_;
          _symbol = symbol_;
          _decimals = decimals_;
        }
      
        /**
         * @dev Returns the name of the token.
         */
        // Present in ERC777
        function name() public view returns (string memory) {
          return _name;
        }
      
        /**
         * @dev Returns the symbol of the token, usually a shorter version of the
         * name.
         */
        // Present in ERC777
        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. This is the value {ERC20} uses, unless {_setupDecimals} is
         * called.
         *
         * 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}.
         */
        // Present in ERC777
        function decimals() public view returns (uint8) {
          return _decimals;
        }
      
        /**
         * @dev See {IERC20-totalSupply}.
         */
        // Present in ERC777
        function totalSupply() public view override returns (uint256) {
          return _totalSupply;
        }
      
        /**
         * @dev See {IERC20-balanceOf}.
         */
        // Present in ERC777
        function balanceOf(address account) public view virtual override 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`.
         */
        // Overrideen in ERC777
        // Confirm that this behavior changes 
        function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
          _transfer(msg.sender, recipient, amount);
          return true;
        }
      
          /**
           * @dev See {IERC20-allowance}.
           */
          // Present in ERC777
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
      
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          // Present in ERC777
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(msg.sender, 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`.
           */
          // Present in ERC777
          function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, msg.sender, _allowances[sender][msg.sender].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 virtual returns (bool) {
              _approve(msg.sender, spender, _allowances[msg.sender][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 virtual returns (bool) {
              _approve(msg.sender, spender, _allowances[msg.sender][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 virtual {
          require(sender != address(0), "ERC20: transfer from the zero address");
          require(recipient != address(0), "ERC20: transfer to the zero address");
      
          _beforeTokenTransfer(sender, recipient, amount);
      
          _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.
           */
          // Present in ERC777
          function _mint(address account_, uint256 ammount_) internal virtual {
              require(account_ != address(0), "ERC20: mint to the zero address");
              _beforeTokenTransfer(address( this ), account_, ammount_);
              _totalSupply = _totalSupply.add(ammount_);
              _balances[account_] = _balances[account_].add(ammount_);
              emit Transfer(address( this ), account_, ammount_);
          }
      
          /**
           * @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.
           */
          // Present in ERC777
          function _burn(address account, uint256 amount) internal virtual {
              require(account != address(0), "ERC20: burn from the zero address");
      
              _beforeTokenTransfer(account, address(0), amount);
      
              _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 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.
           */
          // Present in ERC777
          function _approve(address owner, address spender, uint256 amount) internal virtual {
              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 Sets {decimals} to a value other than the default one of 18.
           *
           * WARNING: This function should only be called from the constructor. Most
           * applications that interact with token contracts will not expect
           * {decimals} to ever change, and may work incorrectly if it does.
           */
          // Considering deprication to reduce size of bytecode as changing _decimals to internal acheived the same functionality.
          // function _setupDecimals(uint8 decimals_) internal {
          //     _decimals = decimals_;
          // }
      
        /**
         * @dev Hook that is called before any transfer of tokens. This includes
         * minting and burning.
         *
         * Calling conditions:
         *
         * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
         * will be to transferred to `to`.
         * - when `from` is zero, `amount` tokens will be minted for `to`.
         * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
         * - `from` and `to` are never both zero.
         *
         * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
         */
        // Present in ERC777
        function _beforeTokenTransfer( address from_, address to_, uint256 amount_ ) internal virtual { }
      }
      
      library Counters {
          using SafeMath for uint256;
      
          struct Counter {
              // This variable should never be directly accessed by users of the library: interactions must be restricted to
              // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
              // this feature: see https://github.com/ethereum/solidity/issues/4637
              uint256 _value; // default: 0
          }
      
          function current(Counter storage counter) internal view returns (uint256) {
              return counter._value;
          }
      
          function increment(Counter storage counter) internal {
              // The {SafeMath} overflow check can be skipped here, see the comment at the top
              counter._value += 1;
          }
      
          function decrement(Counter storage counter) internal {
              counter._value = counter._value.sub(1);
          }
      }
      
      interface IERC2612Permit {
          /**
           * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
           * given `owner`'s signed approval.
           *
           * IMPORTANT: The same issues {IERC20-approve} has related to transaction
           * ordering also apply here.
           *
           * Emits an {Approval} event.
           *
           * Requirements:
           *
           * - `owner` cannot be the zero address.
           * - `spender` cannot be the zero address.
           * - `deadline` must be a timestamp in the future.
           * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
           * over the EIP712-formatted function arguments.
           * - the signature must use ``owner``'s current nonce (see {nonces}).
           *
           * For more information on the signature format, see the
           * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
           * section].
           */
          function permit(
              address owner,
              address spender,
              uint256 amount,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) external;
      
          /**
           * @dev Returns the current ERC2612 nonce for `owner`. This value must be
           * included whenever a signature is generated for {permit}.
           *
           * Every successful call to {permit} increases ``owner``'s nonce by one. This
           * prevents a signature from being used multiple times.
           */
          function nonces(address owner) external view returns (uint256);
      }
      
      abstract contract ERC20Permit is ERC20, IERC2612Permit {
          using Counters for Counters.Counter;
      
          mapping(address => Counters.Counter) private _nonces;
      
          // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
          bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
      
          bytes32 public DOMAIN_SEPARATOR;
      
          constructor() {
      
              uint256 chainID;
              assembly {
                  chainID := chainid()
              }
      
              DOMAIN_SEPARATOR = keccak256(abi.encode(
                  keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                  keccak256(bytes(name())),
                  keccak256(bytes("1")), // Version
                  chainID,
                  address(this)
              ));
          }
      
          /**
           * @dev See {IERC2612Permit-permit}.
           *
           */
          function permit(
              address owner,
              address spender,
              uint256 amount,
              uint256 deadline,
              uint8 v,
              bytes32 r,
              bytes32 s
          ) public virtual override {
              require(block.timestamp <= deadline, "Permit: expired deadline");
      
              bytes32 hashStruct =
                  keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _nonces[owner].current(), deadline));
      
              bytes32 _hash = keccak256(abi.encodePacked(uint16(0x1901), DOMAIN_SEPARATOR, hashStruct));
      
              address signer = ecrecover(_hash, v, r, s);
              require(signer != address(0) && signer == owner, "ZeroSwapPermit: Invalid signature");
      
              _nonces[owner].increment();
              _approve(owner, spender, amount);
          }
      
          /**
           * @dev See {IERC2612Permit-nonces}.
           */
          function nonces(address owner) public view override returns (uint256) {
              return _nonces[owner].current();
          }
      }
      
      interface IOwnable {
        function manager() external view returns (address);
      
        function renounceManagement() external;
        
        function pushManagement( address newOwner_ ) external;
        
        function pullManagement() external;
      }
      
      contract Ownable is IOwnable {
      
          address internal _owner;
          address internal _newOwner;
      
          event OwnershipPushed(address indexed previousOwner, address indexed newOwner);
          event OwnershipPulled(address indexed previousOwner, address indexed newOwner);
      
          constructor () {
              _owner = msg.sender;
              emit OwnershipPushed( address(0), _owner );
          }
      
          function manager() public view override returns (address) {
              return _owner;
          }
      
          modifier onlyManager() {
              require( _owner == msg.sender, "Ownable: caller is not the owner" );
              _;
          }
      
          function renounceManagement() public virtual override onlyManager() {
              emit OwnershipPushed( _owner, address(0) );
              _owner = address(0);
          }
      
          function pushManagement( address newOwner_ ) public virtual override onlyManager() {
              require( newOwner_ != address(0), "Ownable: new owner is the zero address");
              emit OwnershipPushed( _owner, newOwner_ );
              _newOwner = newOwner_;
          }
          
          function pullManagement() public virtual override {
              require( msg.sender == _newOwner, "Ownable: must be new owner to pull");
              emit OwnershipPulled( _owner, _newOwner );
              _owner = _newOwner;
          }
      }
      
      contract sOlympus is ERC20Permit, Ownable {
      
          using SafeMath for uint256;
      
          modifier onlyStakingContract() {
              require( msg.sender == stakingContract );
              _;
          }
      
          address public stakingContract;
          address public initializer;
      
          event LogSupply(uint256 indexed epoch, uint256 timestamp, uint256 totalSupply );
          event LogRebase( uint256 indexed epoch, uint256 rebase, uint256 index );
          event LogStakingContractUpdated( address stakingContract );
      
          struct Rebase {
              uint epoch;
              uint rebase; // 18 decimals
              uint totalStakedBefore;
              uint totalStakedAfter;
              uint amountRebased;
              uint index;
              uint blockNumberOccured;
          }
          Rebase[] public rebases;
      
          uint public INDEX;
      
          uint256 private constant MAX_UINT256 = ~uint256(0);
          uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 5000000 * 10**9;
      
          // TOTAL_GONS is a multiple of INITIAL_FRAGMENTS_SUPPLY so that _gonsPerFragment is an integer.
          // Use the highest value that fits in a uint256 for max granularity.
          uint256 private constant TOTAL_GONS = MAX_UINT256 - (MAX_UINT256 % INITIAL_FRAGMENTS_SUPPLY);
      
          // MAX_SUPPLY = maximum integer < (sqrt(4*TOTAL_GONS + 1) - 1) / 2
          uint256 private constant MAX_SUPPLY = ~uint128(0);  // (2^128) - 1
      
          uint256 private _gonsPerFragment;
          mapping(address => uint256) private _gonBalances;
      
          mapping ( address => mapping ( address => uint256 ) ) private _allowedValue;
      
          constructor() ERC20("Staked Olympus", "sOHM", 9) ERC20Permit() {
              initializer = msg.sender;
              _totalSupply = INITIAL_FRAGMENTS_SUPPLY;
              _gonsPerFragment = TOTAL_GONS.div(_totalSupply);
          }
      
          function initialize( address stakingContract_ ) external returns ( bool ) {
              require( msg.sender == initializer );
              require( stakingContract_ != address(0) );
              stakingContract = stakingContract_;
              _gonBalances[ stakingContract ] = TOTAL_GONS;
      
              emit Transfer( address(0x0), stakingContract, _totalSupply );
              emit LogStakingContractUpdated( stakingContract_ );
              
              initializer = address(0);
              return true;
          }
      
          function setIndex( uint _INDEX ) external onlyManager() returns ( bool ) {
              require( INDEX == 0 );
              INDEX = gonsForBalance( _INDEX );
              return true;
          }
      
          /**
              @notice increases sOHM supply to increase staking balances relative to profit_
              @param profit_ uint256
              @return uint256
           */
          function rebase( uint256 profit_, uint epoch_ ) public onlyStakingContract() returns ( uint256 ) {
              uint256 rebaseAmount;
              uint256 circulatingSupply_ = circulatingSupply();
      
              if ( profit_ == 0 ) {
                  emit LogSupply( epoch_, block.timestamp, _totalSupply );
                  emit LogRebase( epoch_, 0, index() );
                  return _totalSupply;
              } else if ( circulatingSupply_ > 0 ){
                  rebaseAmount = profit_.mul( _totalSupply ).div( circulatingSupply_ );
              } else {
                  rebaseAmount = profit_;
              }
      
              _totalSupply = _totalSupply.add( rebaseAmount );
      
              if ( _totalSupply > MAX_SUPPLY ) {
                  _totalSupply = MAX_SUPPLY;
              }
      
              _gonsPerFragment = TOTAL_GONS.div( _totalSupply );
      
              _storeRebase( circulatingSupply_, profit_, epoch_ );
      
              return _totalSupply;
          }
      
          /**
              @notice emits event with data about rebase
              @param previousCirculating_ uint
              @param profit_ uint
              @param epoch_ uint
              @return bool
           */
          function _storeRebase( uint previousCirculating_, uint profit_, uint epoch_ ) internal returns ( bool ) {
              uint rebasePercent = profit_.mul( 1e18 ).div( previousCirculating_ );
      
              rebases.push( Rebase ( {
                  epoch: epoch_,
                  rebase: rebasePercent, // 18 decimals
                  totalStakedBefore: previousCirculating_,
                  totalStakedAfter: circulatingSupply(),
                  amountRebased: profit_,
                  index: index(),
                  blockNumberOccured: block.number
              }));
              
              emit LogSupply( epoch_, block.timestamp, _totalSupply );
              emit LogRebase( epoch_, rebasePercent, index() );
      
              return true;
          }
      
          function balanceOf( address who ) public view override returns ( uint256 ) {
              return _gonBalances[ who ].div( _gonsPerFragment );
          }
      
          function gonsForBalance( uint amount ) public view returns ( uint ) {
              return amount.mul( _gonsPerFragment );
          }
      
          function balanceForGons( uint gons ) public view returns ( uint ) {
              return gons.div( _gonsPerFragment );
          }
      
          // Staking contract holds excess sOHM
          function circulatingSupply() public view returns ( uint ) {
              return _totalSupply.sub( balanceOf( stakingContract ) );
          }
      
          function index() public view returns ( uint ) {
              return balanceForGons( INDEX );
          }
      
          function transfer( address to, uint256 value ) public override returns (bool) {
              uint256 gonValue = value.mul( _gonsPerFragment );
              _gonBalances[ msg.sender ] = _gonBalances[ msg.sender ].sub( gonValue );
              _gonBalances[ to ] = _gonBalances[ to ].add( gonValue );
              emit Transfer( msg.sender, to, value );
              return true;
          }
      
          function allowance( address owner_, address spender ) public view override returns ( uint256 ) {
              return _allowedValue[ owner_ ][ spender ];
          }
      
          function transferFrom( address from, address to, uint256 value ) public override returns ( bool ) {
             _allowedValue[ from ][ msg.sender ] = _allowedValue[ from ][ msg.sender ].sub( value );
             emit Approval( from, msg.sender,  _allowedValue[ from ][ msg.sender ] );
      
              uint256 gonValue = gonsForBalance( value );
              _gonBalances[ from ] = _gonBalances[from].sub( gonValue );
              _gonBalances[ to ] = _gonBalances[to].add( gonValue );
              emit Transfer( from, to, value );
      
              return true;
          }
      
          function approve( address spender, uint256 value ) public override returns (bool) {
               _allowedValue[ msg.sender ][ spender ] = value;
               emit Approval( msg.sender, spender, value );
               return true;
          }
      
          // What gets called in a permit
          function _approve( address owner, address spender, uint256 value ) internal override virtual {
              _allowedValue[owner][spender] = value;
              emit Approval( owner, spender, value );
          }
      
          function increaseAllowance( address spender, uint256 addedValue ) public override returns (bool) {
              _allowedValue[ msg.sender ][ spender ] = _allowedValue[ msg.sender ][ spender ].add( addedValue );
              emit Approval( msg.sender, spender, _allowedValue[ msg.sender ][ spender ] );
              return true;
          }
      
          function decreaseAllowance( address spender, uint256 subtractedValue ) public override returns (bool) {
              uint256 oldValue = _allowedValue[ msg.sender ][ spender ];
              if (subtractedValue >= oldValue) {
                  _allowedValue[ msg.sender ][ spender ] = 0;
              } else {
                  _allowedValue[ msg.sender ][ spender ] = oldValue.sub( subtractedValue );
              }
              emit Approval( msg.sender, spender, _allowedValue[ msg.sender ][ spender ] );
              return true;
          }
      }

      File 5 of 5: wOHM
      // SPDX-License-Identifier: MIT
      pragma solidity 0.7.5;
      
      /**
       * @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);
      }
      
      /**
       * @dev Wrappers over Solidity's arithmetic operations with added overflow
       * checks.
       *
       * Arithmetic operations in Solidity wrap on overflow. This can easily result
       * in bugs, because programmers usually assume that an overflow raises an
       * error, which is the standard behavior in high level programming languages.
       * `SafeMath` restores this intuition by reverting the transaction when an
       * operation overflows.
       *
       * Using this library instead of the unchecked operations eliminates an entire
       * class of bugs, so it's recommended to use it always.
       */
      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.
           */
          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.
           */
          function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              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.
           */
          function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
              require(b != 0, errorMessage);
              return a % b;
          }
      }
      
      
      /**
       * @dev Collection of functions related to the address type
       */
      library Address {
          /**
           * @dev Returns true if `account` is a contract.
           *
           * [IMPORTANT]
           * ====
           * It is unsafe to assume that an address for which this function returns
           * false is an externally-owned account (EOA) and not a contract.
           *
           * Among others, `isContract` will return false for the following
           * types of addresses:
           *
           *  - an externally-owned account
           *  - a contract in construction
           *  - an address where a contract will be created
           *  - an address where a contract lived, but was destroyed
           * ====
           */
          function isContract(address account) internal view returns (bool) {
              // This method relies in extcodesize, which returns 0 for contracts in
              // construction, since the code is only stored at the end of the
              // constructor execution.
      
              uint256 size;
              // solhint-disable-next-line no-inline-assembly
              assembly { size := extcodesize(account) }
              return size > 0;
          }
      
          /**
           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
           * `recipient`, forwarding all available gas and reverting on errors.
           *
           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
           * of certain opcodes, possibly making contracts go over the 2300 gas limit
           * imposed by `transfer`, making them unable to receive funds via
           * `transfer`. {sendValue} removes this limitation.
           *
           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
           *
           * IMPORTANT: because control is transferred to `recipient`, care must be
           * taken to not create reentrancy vulnerabilities. Consider using
           * {ReentrancyGuard} or the
           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
           */
          function sendValue(address payable recipient, uint256 amount) internal {
              require(address(this).balance >= amount, "Address: insufficient balance");
      
              // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
              (bool success, ) = recipient.call{ value: amount }("");
              require(success, "Address: unable to send value, recipient may have reverted");
          }
      
          /**
           * @dev Performs a Solidity function call using a low level `call`. A
           * plain`call` is an unsafe replacement for a function call: use this
           * function instead.
           *
           * If `target` reverts with a revert reason, it is bubbled up by this
           * function (like regular Solidity function calls).
           *
           * Returns the raw returned data. To convert to the expected return value,
           * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
           *
           * Requirements:
           *
           * - `target` must be a contract.
           * - calling `target` with `data` must not revert.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data) internal returns (bytes memory) {
            return functionCall(target, data, "Address: low-level call failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
           * `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
              return _functionCallWithValue(target, data, 0, errorMessage);
          }
      
          /**
           * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
           * but also transferring `value` wei to `target`.
           *
           * Requirements:
           *
           * - the calling contract must have an ETH balance of at least `value`.
           * - the called Solidity function must be `payable`.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
              return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
          }
      
          /**
           * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
           * with `errorMessage` as a fallback revert reason when `target` reverts.
           *
           * _Available since v3.1._
           */
          function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
              require(address(this).balance >= value, "Address: insufficient balance for call");
              return _functionCallWithValue(target, data, value, errorMessage);
          }
      
          function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
              require(isContract(target), "Address: call to non-contract");
      
              // solhint-disable-next-line avoid-low-level-calls
              (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
              if (success) {
                  return returndata;
              } else {
                  // Look for revert reason and bubble it up if present
                  if (returndata.length > 0) {
                      // The easiest way to bubble the revert reason is using memory via assembly
      
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let returndata_size := mload(returndata)
                          revert(add(32, returndata), returndata_size)
                      }
                  } else {
                      revert(errorMessage);
                  }
              }
          }
      }
      
      /**
       * @dev Implementation of the {IERC20} interface.
       *
       * This implementation is agnostic to the way tokens are created. This means
       * that a supply mechanism has to be added in a derived contract using {_mint}.
       * For a generic mechanism see {ERC20PresetMinterPauser}.
       *
       * TIP: For a detailed writeup see our guide
       * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
       * to implement supply mechanisms].
       *
       * We have followed general OpenZeppelin guidelines: functions revert instead
       * of returning `false` on failure. This behavior is nonetheless conventional
       * and does not conflict with the expectations of ERC20 applications.
       *
       * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
       * This allows applications to reconstruct the allowance for all accounts just
       * by listening to said events. Other implementations of the EIP may not emit
       * these events, as it isn't required by the specification.
       *
       * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
       * functions have been added to mitigate the well-known issues around setting
       * allowances. See {IERC20-approve}.
       */
      contract ERC20 is IERC20 {
          using SafeMath for uint256;
          using Address for address;
      
          mapping (address => uint256) private _balances;
      
          mapping (address => mapping (address => uint256)) private _allowances;
      
          uint256 private _totalSupply;
      
          string private _name;
          string private _symbol;
          uint8 private _decimals;
      
          /**
           * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
           * a default value of 18.
           *
           * To select a different value for {decimals}, use {_setupDecimals}.
           *
           * All three of these values are immutable: they can only be set once during
           * construction.
           */
          constructor (string memory name, string memory symbol) {
              _name = name;
              _symbol = symbol;
              _decimals = 18;
          }
      
          /**
           * @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. This is the value {ERC20} uses, unless {_setupDecimals} is
           * called.
           *
           * 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;
          }
      
          /**
           * @dev See {IERC20-totalSupply}.
           */
          function totalSupply() public view override returns (uint256) {
              return _totalSupply;
          }
      
          /**
           * @dev See {IERC20-balanceOf}.
           */
          function balanceOf(address account) public view override 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 virtual override returns (bool) {
              _transfer(msg.sender, recipient, amount);
              return true;
          }
      
          /**
           * @dev See {IERC20-allowance}.
           */
          function allowance(address owner, address spender) public view virtual override returns (uint256) {
              return _allowances[owner][spender];
          }
      
          /**
           * @dev See {IERC20-approve}.
           *
           * Requirements:
           *
           * - `spender` cannot be the zero address.
           */
          function approve(address spender, uint256 amount) public virtual override returns (bool) {
              _approve(msg.sender, 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 virtual override returns (bool) {
              _transfer(sender, recipient, amount);
              _approve(sender, msg.sender, _allowances[sender][msg.sender].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 virtual returns (bool) {
              _approve(msg.sender, spender, _allowances[msg.sender][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 virtual returns (bool) {
              _approve(msg.sender, spender, _allowances[msg.sender][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 virtual {
              require(sender != address(0), "ERC20: transfer from the zero address");
              require(recipient != address(0), "ERC20: transfer to the zero address");
      
              _beforeTokenTransfer(sender, recipient, amount);
      
              _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 virtual {
              require(account != address(0), "ERC20: mint to the zero address");
      
              _beforeTokenTransfer(address(0), account, amount);
      
              _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 virtual {
              require(account != address(0), "ERC20: burn from the zero address");
      
              _beforeTokenTransfer(account, address(0), amount);
      
              _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 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 virtual {
              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 Sets {decimals} to a value other than the default one of 18.
           *
           * WARNING: This function should only be called from the constructor. Most
           * applications that interact with token contracts will not expect
           * {decimals} to ever change, and may work incorrectly if it does.
           */
          function _setupDecimals(uint8 decimals_) internal {
              _decimals = decimals_;
          }
      
          /**
           * @dev Hook that is called before any transfer of tokens. This includes
           * minting and burning.
           *
           * Calling conditions:
           *
           * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
           * will be to transferred to `to`.
           * - when `from` is zero, `amount` tokens will be minted for `to`.
           * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
           * - `from` and `to` are never both zero.
           *
           * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
           */
          function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
      }
      
      /**
       * @title SafeERC20
       * @dev Wrappers around ERC20 operations that throw on failure (when the token
       * contract returns false). Tokens that return no value (and instead revert or
       * throw on failure) are also supported, non-reverting calls are assumed to be
       * successful.
       * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
       * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
       */
      library SafeERC20 {
          using SafeMath for uint256;
          using Address for address;
      
          function safeTransfer(IERC20 token, address to, uint256 value) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
          }
      
          function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
              _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
          }
      
          /**
           * @dev Deprecated. This function has issues similar to the ones found in
           * {IERC20-approve}, and its usage is discouraged.
           *
           * Whenever possible, use {safeIncreaseAllowance} and
           * {safeDecreaseAllowance} instead.
           */
          function safeApprove(IERC20 token, address spender, uint256 value) internal {
              // safeApprove should only be called when setting an initial allowance,
              // or when resetting it to zero. To increase and decrease it, use
              // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
              // solhint-disable-next-line max-line-length
              require((value == 0) || (token.allowance(address(this), spender) == 0),
                  "SafeERC20: approve from non-zero to non-zero allowance"
              );
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
          }
      
          function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              uint256 newAllowance = token.allowance(address(this), spender).add(value);
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
      
          function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
              uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
              _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
          }
      
          /**
           * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
           * on the return value: the return value is optional (but if data is returned, it must not be false).
           * @param token The token targeted by the call.
           * @param data The call data (encoded using abi.encode or one of its variants).
           */
          function _callOptionalReturn(IERC20 token, bytes memory data) private {
              // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
              // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
              // the target address contains contract code and also asserts for success in the low-level call.
      
              bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
              if (returndata.length > 0) { // Return data is optional
                  // solhint-disable-next-line max-line-length
                  require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
              }
          }
      }
      
      interface IsOHM {
          function index() external view returns ( uint );
      }
      
      contract wOHM is ERC20 {
          using SafeERC20 for ERC20;
          using Address for address;
          using SafeMath for uint;
      
          address public immutable sOHM;
      
          constructor( address _sOHM ) ERC20( 'Wrapped sOHM', 'wsOHM' ) {
              require( _sOHM != address(0) );
              sOHM = _sOHM;
          }
      
          /**
              @notice wrap sOHM
              @param _amount uint
              @return uint
           */
          function wrap( uint _amount ) external returns ( uint ) {
              IERC20( sOHM ).transferFrom( msg.sender, address(this), _amount );
              
              uint value = sOHMTowOHM( _amount );
              _mint( msg.sender, value );
              return value;
          }
      
          /**
              @notice unwrap sOHM
              @param _amount uint
              @return uint
           */
          function unwrap( uint _amount ) external returns ( uint ) {
              _burn( msg.sender, _amount );
      
              uint value = wOHMTosOHM( _amount );
              IERC20( sOHM ).transfer( msg.sender, value );
              return value;
          }
      
          /**
              @notice converts wOHM amount to sOHM
              @param _amount uint
              @return uint
           */
          function wOHMTosOHM( uint _amount ) public view returns ( uint ) {
              return _amount.mul( IsOHM( sOHM ).index() ).div( 10 ** decimals() );
          }
      
          /**
              @notice converts sOHM amount to wOHM
              @param _amount uint
              @return uint
           */
          function sOHMTowOHM( uint _amount ) public view returns ( uint ) {
              return _amount.mul( 10 ** decimals() ).div( IsOHM( sOHM ).index() );
          }
      
      }