ETH Price: $1,999.16 (+3.21%)

Transaction Decoder

Block:
12927113 at Jul-30-2021 01:00:37 PM +UTC
Transaction Fee:
0.004055643 ETH $8.11
Gas Used:
150,209 Gas / 27 Gwei

Emitted Events:

155 OlympusBondDepository.BondRedeemed( recipient=[Sender] 0x98eafe193b06914df90c0a6d1e3a9ccbae42eba8, payout=6776104080, remaining=14998009288 )
156 OlympusERC20Token.Approval( owner=[Receiver] OlympusBondDepository, spender=StakingHelper, value=6776104080 )
157 OlympusERC20Token.Transfer( from=[Receiver] OlympusBondDepository, to=StakingHelper, value=6776104080 )
158 OlympusERC20Token.Approval( owner=[Receiver] OlympusBondDepository, spender=StakingHelper, value=0 )
159 OlympusERC20Token.Approval( owner=StakingHelper, spender=OlympusStaking, value=6776104080 )
160 OlympusERC20Token.Transfer( from=StakingHelper, to=OlympusStaking, value=6776104080 )
161 OlympusERC20Token.Approval( owner=StakingHelper, spender=OlympusStaking, value=0 )
162 sOlympus.Transfer( from=OlympusStaking, to=StakingWarmup, value=6776104080 )
163 sOlympus.Transfer( from=StakingWarmup, to=[Sender] 0x98eafe193b06914df90c0a6d1e3a9ccbae42eba8, value=6776104080 )

Account State Difference:

  Address   Before After State Difference Code
0x04F2694C...8c352111F
0x38351818...37814a899
0x98eAFe19...bAE42EbA8
0.134048733284673913 Eth
Nonce: 278
0.129993090284673913 Eth
Nonce: 279
0.004055643
(BeePool)
979.50166546033283413 Eth979.50572110333283413 Eth0.004055643
0xc20CffF0...90E5A02f7
(OlympusDAO: OHM/FRAX LP Bond V2)

Execution Trace

OlympusBondDepository.redeem( _recipient=0x98eAFe193b06914DF90c0A6d1e3a9cCbAE42EbA8, _stake=True ) => ( 6776104080 )
  • OlympusERC20Token.approve( spender=0xa55cE3E25bD4cb6C5375AA393335b708dB790915, amount=6776104080 ) => ( True )
  • StakingHelper.stake( _amount=6776104080, _recipient=0x98eAFe193b06914DF90c0A6d1e3a9cCbAE42EbA8 )
    • OlympusERC20Token.transferFrom( sender=0xc20CffF07076858a7e642E396180EC390E5A02f7, recipient=0xa55cE3E25bD4cb6C5375AA393335b708dB790915, amount=6776104080 ) => ( True )
    • OlympusERC20Token.approve( spender=0xFd31c7d00Ca47653c6Ce64Af53c1571f9C36566a, amount=6776104080 ) => ( True )
    • OlympusStaking.stake( _amount=6776104080, _recipient=0x98eAFe193b06914DF90c0A6d1e3a9cCbAE42EbA8 ) => ( True )
      • OlympusERC20Token.transferFrom( sender=0xa55cE3E25bD4cb6C5375AA393335b708dB790915, recipient=0xFd31c7d00Ca47653c6Ce64Af53c1571f9C36566a, amount=6776104080 ) => ( True )
      • sOlympus.gonsForBalance( amount=6776104080 ) => ( 79825888323951155799634593606025865197741358631315248353139006184288240 )
      • sOlympus.transfer( to=0x2882A5CD82AC49e06620382660f5ed932607c5f1, value=6776104080 ) => ( True )
      • OlympusStaking.claim( _recipient=0x98eAFe193b06914DF90c0A6d1e3a9cCbAE42EbA8 )
        • sOlympus.balanceForGons( gons=79825888323951155799634593606025865197741358631315248353139006184288240 ) => ( 6776104080 )
        • StakingWarmup.retrieve( _staker=0x98eAFe193b06914DF90c0A6d1e3a9cCbAE42EbA8, _amount=6776104080 )
          • sOlympus.transfer( to=0x98eAFe193b06914DF90c0A6d1e3a9cCbAE42EbA8, value=6776104080 ) => ( True )
            File 1 of 6: OlympusBondDepository
            /**
             *Submitted for verification at Etherscan.io on 2021-06-16
            */
            
            // SPDX-License-Identifier: AGPL-3.0-or-later
            pragma solidity 0.7.5;
            
            interface IOwnable {
              function policy() 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 policy() public view override returns (address) {
                    return _owner;
                }
            
                modifier onlyPolicy() {
                    require( _owner == msg.sender, "Ownable: caller is not the owner" );
                    _;
                }
            
                function renounceManagement() public virtual override onlyPolicy() {
                    emit OwnershipPushed( _owner, address(0) );
                    _owner = address(0);
                }
            
                function pushManagement( address newOwner_ ) public virtual override onlyPolicy() {
                    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;
                }
            }
            
            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;
                    return c;
                }
            
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                    return mod(a, b, "SafeMath: modulo by zero");
                }
            
                function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            
                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;
                    }
                }
            }
            
            library Address {
            
                function isContract(address account) internal view returns (bool) {
            
                    uint256 size;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { size := extcodesize(account) }
                    return size > 0;
                }
            
                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");
                }
            
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCall(target, data, "Address: low-level call failed");
                }
            
                function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                    return _functionCallWithValue(target, data, 0, errorMessage);
                }
            
                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");
                }
            
                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);
                        }
                    }
                }
            
                function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
                    return functionStaticCall(target, data, "Address: low-level static call failed");
                }
            
                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);
                }
            
                function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
                    return functionDelegateCall(target, data, "Address: low-level delegate call failed");
                }
            
                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 {
                        if (returndata.length > 0) {
            
                            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 {
                function decimals() external view returns (uint8);
            
                function totalSupply() external view returns (uint256);
            
                function balanceOf(address account) external view returns (uint256);
            
                function transfer(address recipient, uint256 amount) external returns (bool);
            
                function allowance(address owner, address spender) external view returns (uint256);
            
                function approve(address spender, uint256 amount) external returns (bool);
            
                function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
            
                event Transfer(address indexed from, address indexed to, uint256 value);
            
                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" );
                
                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 override 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 { }
            }
            
            interface IERC2612Permit {
            
                function permit(
                    address owner,
                    address spender,
                    uint256 amount,
                    uint256 deadline,
                    uint8 v,
                    bytes32 r,
                    bytes32 s
                ) external;
            
                function nonces(address owner) external view returns (uint256);
            }
            
            library Counters {
                using SafeMath for uint256;
            
                struct Counter {
            
                    uint256 _value; // default: 0
                }
            
                function current(Counter storage counter) internal view returns (uint256) {
                    return counter._value;
                }
            
                function increment(Counter storage counter) internal {
                    counter._value += 1;
                }
            
                function decrement(Counter storage counter) internal {
                    counter._value = counter._value.sub(1);
                }
            }
            
            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)
                        )
                    );
                }
            
                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);
                }
            
                function nonces(address owner) public view override returns (uint256) {
                    return _nonces[owner].current();
                }
            }
            
            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));
                }
            
                function safeApprove(IERC20 token, address spender, uint256 value) internal {
            
                    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));
                }
            
                function _callOptionalReturn(IERC20 token, bytes memory data) private {
            
                    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");
                    }
                }
            }
            
            library FullMath {
                function fullMul(uint256 x, uint256 y) private pure returns (uint256 l, uint256 h) {
                    uint256 mm = mulmod(x, y, uint256(-1));
                    l = x * y;
                    h = mm - l;
                    if (mm < l) h -= 1;
                }
            
                function fullDiv(
                    uint256 l,
                    uint256 h,
                    uint256 d
                ) private pure returns (uint256) {
                    uint256 pow2 = d & -d;
                    d /= pow2;
                    l /= pow2;
                    l += h * ((-pow2) / pow2 + 1);
                    uint256 r = 1;
                    r *= 2 - d * r;
                    r *= 2 - d * r;
                    r *= 2 - d * r;
                    r *= 2 - d * r;
                    r *= 2 - d * r;
                    r *= 2 - d * r;
                    r *= 2 - d * r;
                    r *= 2 - d * r;
                    return l * r;
                }
            
                function mulDiv(
                    uint256 x,
                    uint256 y,
                    uint256 d
                ) internal pure returns (uint256) {
                    (uint256 l, uint256 h) = fullMul(x, y);
                    uint256 mm = mulmod(x, y, d);
                    if (mm > l) h -= 1;
                    l -= mm;
                    require(h < d, 'FullMath::mulDiv: overflow');
                    return fullDiv(l, h, d);
                }
            }
            
            library FixedPoint {
            
                struct uq112x112 {
                    uint224 _x;
                }
            
                struct uq144x112 {
                    uint256 _x;
                }
            
                uint8 private constant RESOLUTION = 112;
                uint256 private constant Q112 = 0x10000000000000000000000000000;
                uint256 private constant Q224 = 0x100000000000000000000000000000000000000000000000000000000;
                uint256 private constant LOWER_MASK = 0xffffffffffffffffffffffffffff; // decimal of UQ*x112 (lower 112 bits)
            
                function decode(uq112x112 memory self) internal pure returns (uint112) {
                    return uint112(self._x >> RESOLUTION);
                }
            
                function decode112with18(uq112x112 memory self) internal pure returns (uint) {
            
                    return uint(self._x) / 5192296858534827;
                }
            
                function fraction(uint256 numerator, uint256 denominator) internal pure returns (uq112x112 memory) {
                    require(denominator > 0, 'FixedPoint::fraction: division by zero');
                    if (numerator == 0) return FixedPoint.uq112x112(0);
            
                    if (numerator <= uint144(-1)) {
                        uint256 result = (numerator << RESOLUTION) / denominator;
                        require(result <= uint224(-1), 'FixedPoint::fraction: overflow');
                        return uq112x112(uint224(result));
                    } else {
                        uint256 result = FullMath.mulDiv(numerator, Q112, denominator);
                        require(result <= uint224(-1), 'FixedPoint::fraction: overflow');
                        return uq112x112(uint224(result));
                    }
                }
            }
            
            interface ITreasury {
                function deposit( uint _amount, address _token, uint _profit ) external returns ( bool );
                function valueOf( address _token, uint _amount ) external view returns ( uint value_ );
            }
            
            interface IBondCalculator {
                function valuation( address _LP, uint _amount ) external view returns ( uint );
                function markdown( address _LP ) external view returns ( uint );
            }
            
            interface IStaking {
                function stake( uint _amount, address _recipient ) external returns ( bool );
            }
            
            interface IStakingHelper {
                function stake( uint _amount, address _recipient ) external;
            }
            
            contract OlympusBondDepository is Ownable {
            
                using FixedPoint for *;
                using SafeERC20 for IERC20;
                using SafeMath for uint;
            
            
            
            
                /* ======== EVENTS ======== */
            
                event BondCreated( uint deposit, uint indexed payout, uint indexed expires, uint indexed priceInUSD );
                event BondRedeemed( address indexed recipient, uint payout, uint remaining );
                event BondPriceChanged( uint indexed priceInUSD, uint indexed internalPrice, uint indexed debtRatio );
                event ControlVariableAdjustment( uint initialBCV, uint newBCV, uint adjustment, bool addition );
            
            
            
            
                /* ======== STATE VARIABLES ======== */
            
                address public immutable OHM; // token given as payment for bond
                address public immutable principle; // token used to create bond
                address public immutable treasury; // mints OHM when receives principle
                address public immutable DAO; // receives profit share from bond
            
                bool public immutable isLiquidityBond; // LP and Reserve bonds are treated slightly different
                address public immutable bondCalculator; // calculates value of LP tokens
            
                address public staking; // to auto-stake payout
                address public stakingHelper; // to stake and claim if no staking warmup
                bool public useHelper;
            
                Terms public terms; // stores terms for new bonds
                Adjust public adjustment; // stores adjustment to BCV data
            
                mapping( address => Bond ) public bondInfo; // stores bond information for depositors
            
                uint public totalDebt; // total value of outstanding bonds; used for pricing
                uint public lastDecay; // reference block for debt decay
            
            
            
            
                /* ======== STRUCTS ======== */
            
                // Info for creating new bonds
                struct Terms {
                    uint controlVariable; // scaling variable for price
                    uint vestingTerm; // in blocks
                    uint minimumPrice; // vs principle value
                    uint maxPayout; // in thousandths of a %. i.e. 500 = 0.5%
                    uint fee; // as % of bond payout, in hundreths. ( 500 = 5% = 0.05 for every 1 paid)
                    uint maxDebt; // 9 decimal debt ratio, max % total supply created as debt
                }
            
                // Info for bond holder
                struct Bond {
                    uint payout; // OHM remaining to be paid
                    uint vesting; // Blocks left to vest
                    uint lastBlock; // Last interaction
                    uint pricePaid; // In DAI, for front end viewing
                }
            
                // Info for incremental adjustments to control variable 
                struct Adjust {
                    bool add; // addition or subtraction
                    uint rate; // increment
                    uint target; // BCV when adjustment finished
                    uint buffer; // minimum length (in blocks) between adjustments
                    uint lastBlock; // block when last adjustment made
                }
            
            
            
            
                /* ======== INITIALIZATION ======== */
            
                constructor ( 
                    address _OHM,
                    address _principle,
                    address _treasury, 
                    address _DAO, 
                    address _bondCalculator
                ) {
                    require( _OHM != address(0) );
                    OHM = _OHM;
                    require( _principle != address(0) );
                    principle = _principle;
                    require( _treasury != address(0) );
                    treasury = _treasury;
                    require( _DAO != address(0) );
                    DAO = _DAO;
                    // bondCalculator should be address(0) if not LP bond
                    bondCalculator = _bondCalculator;
                    isLiquidityBond = ( _bondCalculator != address(0) );
                }
            
                /**
                 *  @notice initializes bond parameters
                 *  @param _controlVariable uint
                 *  @param _vestingTerm uint
                 *  @param _minimumPrice uint
                 *  @param _maxPayout uint
                 *  @param _fee uint
                 *  @param _maxDebt uint
                 *  @param _initialDebt uint
                 */
                function initializeBondTerms( 
                    uint _controlVariable, 
                    uint _vestingTerm,
                    uint _minimumPrice,
                    uint _maxPayout,
                    uint _fee,
                    uint _maxDebt,
                    uint _initialDebt
                ) external onlyPolicy() {
                    require( terms.controlVariable == 0, "Bonds must be initialized from 0" );
                    terms = Terms ({
                        controlVariable: _controlVariable,
                        vestingTerm: _vestingTerm,
                        minimumPrice: _minimumPrice,
                        maxPayout: _maxPayout,
                        fee: _fee,
                        maxDebt: _maxDebt
                    });
                    totalDebt = _initialDebt;
                    lastDecay = block.number;
                }
            
            
            
                
                /* ======== POLICY FUNCTIONS ======== */
            
                enum PARAMETER { VESTING, PAYOUT, FEE, DEBT }
                /**
                 *  @notice set parameters for new bonds
                 *  @param _parameter PARAMETER
                 *  @param _input uint
                 */
                function setBondTerms ( PARAMETER _parameter, uint _input ) external onlyPolicy() {
                    if ( _parameter == PARAMETER.VESTING ) { // 0
                        require( _input >= 10000, "Vesting must be longer than 36 hours" );
                        terms.vestingTerm = _input;
                    } else if ( _parameter == PARAMETER.PAYOUT ) { // 1
                        require( _input <= 1000, "Payout cannot be above 1 percent" );
                        terms.maxPayout = _input;
                    } else if ( _parameter == PARAMETER.FEE ) { // 2
                        require( _input <= 10000, "DAO fee cannot exceed payout" );
                        terms.fee = _input;
                    } else if ( _parameter == PARAMETER.DEBT ) { // 3
                        terms.maxDebt = _input;
                    }
                }
            
                /**
                 *  @notice set control variable adjustment
                 *  @param _addition bool
                 *  @param _increment uint
                 *  @param _target uint
                 *  @param _buffer uint
                 */
                function setAdjustment ( 
                    bool _addition,
                    uint _increment, 
                    uint _target,
                    uint _buffer 
                ) external onlyPolicy() {
                    require( _increment <= terms.controlVariable.mul( 25 ).div( 1000 ), "Increment too large" );
            
                    adjustment = Adjust({
                        add: _addition,
                        rate: _increment,
                        target: _target,
                        buffer: _buffer,
                        lastBlock: block.number
                    });
                }
            
                /**
                 *  @notice set contract for auto stake
                 *  @param _staking address
                 *  @param _helper bool
                 */
                function setStaking( address _staking, bool _helper ) external onlyPolicy() {
                    require( _staking != address(0) );
                    if ( _helper ) {
                        useHelper = true;
                        stakingHelper = _staking;
                    } else {
                        useHelper = false;
                        staking = _staking;
                    }
                }
            
            
                
            
                /* ======== USER FUNCTIONS ======== */
            
                /**
                 *  @notice deposit bond
                 *  @param _amount uint
                 *  @param _maxPrice uint
                 *  @param _depositor address
                 *  @return uint
                 */
                function deposit( 
                    uint _amount, 
                    uint _maxPrice,
                    address _depositor
                ) external returns ( uint ) {
                    require( _depositor != address(0), "Invalid address" );
            
                    decayDebt();
                    require( totalDebt <= terms.maxDebt, "Max capacity reached" );
                    
                    uint priceInUSD = bondPriceInUSD(); // Stored in bond info
                    uint nativePrice = _bondPrice();
            
                    require( _maxPrice >= nativePrice, "Slippage limit: more than max price" ); // slippage protection
            
                    uint value = ITreasury( treasury ).valueOf( principle, _amount );
                    uint payout = payoutFor( value ); // payout to bonder is computed
            
                    require( payout >= 10000000, "Bond too small" ); // must be > 0.01 OHM ( underflow protection )
                    require( payout <= maxPayout(), "Bond too large"); // size protection because there is no slippage
            
                    // profits are calculated
                    uint fee = payout.mul( terms.fee ).div( 10000 );
                    uint profit = value.sub( payout ).sub( fee );
            
                    /**
                        principle is transferred in
                        approved and
                        deposited into the treasury, returning (_amount - profit) OHM
                     */
                    IERC20( principle ).safeTransferFrom( msg.sender, address(this), _amount );
                    IERC20( principle ).approve( address( treasury ), _amount );
                    ITreasury( treasury ).deposit( _amount, principle, profit );
                    
                    if ( fee != 0 ) { // fee is transferred to dao 
                        IERC20( OHM ).safeTransfer( DAO, fee ); 
                    }
                    
                    // total debt is increased
                    totalDebt = totalDebt.add( value ); 
                            
                    // depositor info is stored
                    bondInfo[ _depositor ] = Bond({ 
                        payout: bondInfo[ _depositor ].payout.add( payout ),
                        vesting: terms.vestingTerm,
                        lastBlock: block.number,
                        pricePaid: priceInUSD
                    });
            
                    // indexed events are emitted
                    emit BondCreated( _amount, payout, block.number.add( terms.vestingTerm ), priceInUSD );
                    emit BondPriceChanged( bondPriceInUSD(), _bondPrice(), debtRatio() );
            
                    adjust(); // control variable is adjusted
                    return payout; 
                }
            
                /** 
                 *  @notice redeem bond for user
                 *  @param _recipient address
                 *  @param _stake bool
                 *  @return uint
                 */ 
                function redeem( address _recipient, bool _stake ) external returns ( uint ) {        
                    Bond memory info = bondInfo[ _recipient ];
                    uint percentVested = percentVestedFor( _recipient ); // (blocks since last interaction / vesting term remaining)
            
                    if ( percentVested >= 10000 ) { // if fully vested
                        delete bondInfo[ _recipient ]; // delete user info
                        emit BondRedeemed( _recipient, info.payout, 0 ); // emit bond data
                        return stakeOrSend( _recipient, _stake, info.payout ); // pay user everything due
            
                    } else { // if unfinished
                        // calculate payout vested
                        uint payout = info.payout.mul( percentVested ).div( 10000 );
            
                        // store updated deposit info
                        bondInfo[ _recipient ] = Bond({
                            payout: info.payout.sub( payout ),
                            vesting: info.vesting.sub( block.number.sub( info.lastBlock ) ),
                            lastBlock: block.number,
                            pricePaid: info.pricePaid
                        });
            
                        emit BondRedeemed( _recipient, payout, bondInfo[ _recipient ].payout );
                        return stakeOrSend( _recipient, _stake, payout );
                    }
                }
            
            
            
                
                /* ======== INTERNAL HELPER FUNCTIONS ======== */
            
                /**
                 *  @notice allow user to stake payout automatically
                 *  @param _stake bool
                 *  @param _amount uint
                 *  @return uint
                 */
                function stakeOrSend( address _recipient, bool _stake, uint _amount ) internal returns ( uint ) {
                    if ( !_stake ) { // if user does not want to stake
                        IERC20( OHM ).transfer( _recipient, _amount ); // send payout
                    } else { // if user wants to stake
                        if ( useHelper ) { // use if staking warmup is 0
                            IERC20( OHM ).approve( stakingHelper, _amount );
                            IStakingHelper( stakingHelper ).stake( _amount, _recipient );
                        } else {
                            IERC20( OHM ).approve( staking, _amount );
                            IStaking( staking ).stake( _amount, _recipient );
                        }
                    }
                    return _amount;
                }
            
                /**
                 *  @notice makes incremental adjustment to control variable
                 */
                function adjust() internal {
                    uint blockCanAdjust = adjustment.lastBlock.add( adjustment.buffer );
                    if( adjustment.rate != 0 && block.number >= blockCanAdjust ) {
                        uint initial = terms.controlVariable;
                        if ( adjustment.add ) {
                            terms.controlVariable = terms.controlVariable.add( adjustment.rate );
                            if ( terms.controlVariable >= adjustment.target ) {
                                adjustment.rate = 0;
                            }
                        } else {
                            terms.controlVariable = terms.controlVariable.sub( adjustment.rate );
                            if ( terms.controlVariable <= adjustment.target ) {
                                adjustment.rate = 0;
                            }
                        }
                        adjustment.lastBlock = block.number;
                        emit ControlVariableAdjustment( initial, terms.controlVariable, adjustment.rate, adjustment.add );
                    }
                }
            
                /**
                 *  @notice reduce total debt
                 */
                function decayDebt() internal {
                    totalDebt = totalDebt.sub( debtDecay() );
                    lastDecay = block.number;
                }
            
            
            
            
                /* ======== VIEW FUNCTIONS ======== */
            
                /**
                 *  @notice determine maximum bond size
                 *  @return uint
                 */
                function maxPayout() public view returns ( uint ) {
                    return IERC20( OHM ).totalSupply().mul( terms.maxPayout ).div( 100000 );
                }
            
                /**
                 *  @notice calculate interest due for new bond
                 *  @param _value uint
                 *  @return uint
                 */
                function payoutFor( uint _value ) public view returns ( uint ) {
                    return FixedPoint.fraction( _value, bondPrice() ).decode112with18().div( 1e16 );
                }
            
            
                /**
                 *  @notice calculate current bond premium
                 *  @return price_ uint
                 */
                function bondPrice() public view returns ( uint price_ ) {        
                    price_ = terms.controlVariable.mul( debtRatio() ).add( 1000000000 ).div( 1e7 );
                    if ( price_ < terms.minimumPrice ) {
                        price_ = terms.minimumPrice;
                    }
                }
            
                /**
                 *  @notice calculate current bond price and remove floor if above
                 *  @return price_ uint
                 */
                function _bondPrice() internal returns ( uint price_ ) {
                    price_ = terms.controlVariable.mul( debtRatio() ).add( 1000000000 ).div( 1e7 );
                    if ( price_ < terms.minimumPrice ) {
                        price_ = terms.minimumPrice;        
                    } else if ( terms.minimumPrice != 0 ) {
                        terms.minimumPrice = 0;
                    }
                }
            
                /**
                 *  @notice converts bond price to DAI value
                 *  @return price_ uint
                 */
                function bondPriceInUSD() public view returns ( uint price_ ) {
                    if( isLiquidityBond ) {
                        price_ = bondPrice().mul( IBondCalculator( bondCalculator ).markdown( principle ) ).div( 100 );
                    } else {
                        price_ = bondPrice().mul( 10 ** IERC20( principle ).decimals() ).div( 100 );
                    }
                }
            
            
                /**
                 *  @notice calculate current ratio of debt to OHM supply
                 *  @return debtRatio_ uint
                 */
                function debtRatio() public view returns ( uint debtRatio_ ) {   
                    uint supply = IERC20( OHM ).totalSupply();
                    debtRatio_ = FixedPoint.fraction( 
                        currentDebt().mul( 1e9 ), 
                        supply
                    ).decode112with18().div( 1e18 );
                }
            
                /**
                 *  @notice debt ratio in same terms for reserve or liquidity bonds
                 *  @return uint
                 */
                function standardizedDebtRatio() external view returns ( uint ) {
                    if ( isLiquidityBond ) {
                        return debtRatio().mul( IBondCalculator( bondCalculator ).markdown( principle ) ).div( 1e9 );
                    } else {
                        return debtRatio();
                    }
                }
            
                /**
                 *  @notice calculate debt factoring in decay
                 *  @return uint
                 */
                function currentDebt() public view returns ( uint ) {
                    return totalDebt.sub( debtDecay() );
                }
            
                /**
                 *  @notice amount to decay total debt by
                 *  @return decay_ uint
                 */
                function debtDecay() public view returns ( uint decay_ ) {
                    uint blocksSinceLast = block.number.sub( lastDecay );
                    decay_ = totalDebt.mul( blocksSinceLast ).div( terms.vestingTerm );
                    if ( decay_ > totalDebt ) {
                        decay_ = totalDebt;
                    }
                }
            
            
                /**
                 *  @notice calculate how far into vesting a depositor is
                 *  @param _depositor address
                 *  @return percentVested_ uint
                 */
                function percentVestedFor( address _depositor ) public view returns ( uint percentVested_ ) {
                    Bond memory bond = bondInfo[ _depositor ];
                    uint blocksSinceLast = block.number.sub( bond.lastBlock );
                    uint vesting = bond.vesting;
            
                    if ( vesting > 0 ) {
                        percentVested_ = blocksSinceLast.mul( 10000 ).div( vesting );
                    } else {
                        percentVested_ = 0;
                    }
                }
            
                /**
                 *  @notice calculate amount of OHM available for claim by depositor
                 *  @param _depositor address
                 *  @return pendingPayout_ uint
                 */
                function pendingPayoutFor( address _depositor ) external view returns ( uint pendingPayout_ ) {
                    uint percentVested = percentVestedFor( _depositor );
                    uint payout = bondInfo[ _depositor ].payout;
            
                    if ( percentVested >= 10000 ) {
                        pendingPayout_ = payout;
                    } else {
                        pendingPayout_ = payout.mul( percentVested ).div( 10000 );
                    }
                }
            
            
            
            
                /* ======= AUXILLIARY ======= */
            
                /**
                 *  @notice allow anyone to send lost tokens (excluding principle or OHM) to the DAO
                 *  @return bool
                 */
                function recoverLostToken( address _token ) external returns ( bool ) {
                    require( _token != OHM );
                    require( _token != principle );
                    IERC20( _token ).safeTransfer( DAO, IERC20( _token ).balanceOf( address(this) ) );
                    return true;
                }
            }

            File 2 of 6: 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 6: StakingHelper
            /**
             *Submitted for verification at Etherscan.io on 2021-06-12
            */
            
            // SPDX-License-Identifier: AGPL-3.0-or-later
            pragma solidity 0.7.5;
            
            
            interface IERC20 {
                function decimals() external view returns (uint8);
              /**
               * @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);
            }
            
            interface IStaking {
                function stake( uint _amount, address _recipient ) external returns ( bool );
                function claim( address _recipient ) external;
            }
            
            contract StakingHelper {
            
                address public immutable staking;
                address public immutable OHM;
            
                constructor ( address _staking, address _OHM ) {
                    require( _staking != address(0) );
                    staking = _staking;
                    require( _OHM != address(0) );
                    OHM = _OHM;
                }
            
                function stake( uint _amount, address _recipient ) external {
                    IERC20( OHM ).transferFrom( msg.sender, address(this), _amount );
                    IERC20( OHM ).approve( staking, _amount );
                    IStaking( staking ).stake( _amount, _recipient );
                    IStaking( staking ).claim( _recipient );
                }
            }

            File 4 of 6: OlympusStaking
            // SPDX-License-Identifier: AGPL-3.0-or-later
            pragma solidity 0.7.5;
            
            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;
                }
            }
            
            interface IERC20 {
                function decimals() external view returns (uint8);
              /**
               * @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 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");
                    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);
            
                }
            }
            
            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 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;
                }
            }
            
            interface IsOHM {
                function rebase( uint256 ohmProfit_, uint epoch_) external returns (uint256);
            
                function circulatingSupply() external view returns (uint256);
            
                function balanceOf(address who) 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 );
            }
            
            interface IWarmup {
                function retrieve( address staker_, uint amount_ ) external;
            }
            
            interface IDistributor {
                function distribute() external returns ( bool );
            }
            
            contract OlympusStaking is Ownable {
            
                using SafeMath for uint256;
                using SafeERC20 for IERC20;
            
                address public immutable OHM;
                address public immutable sOHM;
            
                struct Epoch {
                    uint length;
                    uint number;
                    uint endBlock;
                    uint distribute;
                }
                Epoch public epoch;
            
                address public distributor;
                
                address public locker;
                uint public totalBonus;
                
                address public warmupContract;
                uint public warmupPeriod;
                
                constructor ( 
                    address _OHM, 
                    address _sOHM, 
                    uint _epochLength,
                    uint _firstEpochNumber,
                    uint _firstEpochBlock
                ) {
                    require( _OHM != address(0) );
                    OHM = _OHM;
                    require( _sOHM != address(0) );
                    sOHM = _sOHM;
                    
                    epoch = Epoch({
                        length: _epochLength,
                        number: _firstEpochNumber,
                        endBlock: _firstEpochBlock,
                        distribute: 0
                    });
                }
            
                struct Claim {
                    uint deposit;
                    uint gons;
                    uint expiry;
                    bool lock; // prevents malicious delays
                }
                mapping( address => Claim ) public warmupInfo;
            
                /**
                    @notice stake OHM to enter warmup
                    @param _amount uint
                    @return bool
                 */
                function stake( uint _amount, address _recipient ) external returns ( bool ) {
                    rebase();
                    
                    IERC20( OHM ).safeTransferFrom( msg.sender, address(this), _amount );
            
                    Claim memory info = warmupInfo[ _recipient ];
                    require( !info.lock, "Deposits for account are locked" );
            
                    warmupInfo[ _recipient ] = Claim ({
                        deposit: info.deposit.add( _amount ),
                        gons: info.gons.add( IsOHM( sOHM ).gonsForBalance( _amount ) ),
                        expiry: epoch.number.add( warmupPeriod ),
                        lock: false
                    });
                    
                    IERC20( sOHM ).safeTransfer( warmupContract, _amount );
                    return true;
                }
            
                /**
                    @notice retrieve sOHM from warmup
                    @param _recipient address
                 */
                function claim ( address _recipient ) public {
                    Claim memory info = warmupInfo[ _recipient ];
                    if ( epoch.number >= info.expiry && info.expiry != 0 ) {
                        delete warmupInfo[ _recipient ];
                        IWarmup( warmupContract ).retrieve( _recipient, IsOHM( sOHM ).balanceForGons( info.gons ) );
                    }
                }
            
                /**
                    @notice forfeit sOHM in warmup and retrieve OHM
                 */
                function forfeit() external {
                    Claim memory info = warmupInfo[ msg.sender ];
                    delete warmupInfo[ msg.sender ];
            
                    IWarmup( warmupContract ).retrieve( address(this), IsOHM( sOHM ).balanceForGons( info.gons ) );
                    IERC20( OHM ).safeTransfer( msg.sender, info.deposit );
                }
            
                /**
                    @notice prevent new deposits to address (protection from malicious activity)
                 */
                function toggleDepositLock() external {
                    warmupInfo[ msg.sender ].lock = !warmupInfo[ msg.sender ].lock;
                }
            
                /**
                    @notice redeem sOHM for OHM
                    @param _amount uint
                    @param _trigger bool
                 */
                function unstake( uint _amount, bool _trigger ) external {
                    if ( _trigger ) {
                        rebase();
                    }
                    IERC20( sOHM ).safeTransferFrom( msg.sender, address(this), _amount );
                    IERC20( OHM ).safeTransfer( msg.sender, _amount );
                }
            
                /**
                    @notice returns the sOHM index, which tracks rebase growth
                    @return uint
                 */
                function index() public view returns ( uint ) {
                    return IsOHM( sOHM ).index();
                }
            
                /**
                    @notice trigger rebase if epoch over
                 */
                function rebase() public {
                    if( epoch.endBlock <= block.number ) {
            
                        IsOHM( sOHM ).rebase( epoch.distribute, epoch.number );
            
                        epoch.endBlock = epoch.endBlock.add( epoch.length );
                        epoch.number++;
                        
                        if ( distributor != address(0) ) {
                            IDistributor( distributor ).distribute();
                        }
            
                        uint balance = contractBalance();
                        uint staked = IsOHM( sOHM ).circulatingSupply();
            
                        if( balance <= staked ) {
                            epoch.distribute = 0;
                        } else {
                            epoch.distribute = balance.sub( staked );
                        }
                    }
                }
            
                /**
                    @notice returns contract OHM holdings, including bonuses provided
                    @return uint
                 */
                function contractBalance() public view returns ( uint ) {
                    return IERC20( OHM ).balanceOf( address(this) ).add( totalBonus );
                }
            
                /**
                    @notice provide bonus to locked staking contract
                    @param _amount uint
                 */
                function giveLockBonus( uint _amount ) external {
                    require( msg.sender == locker );
                    totalBonus = totalBonus.add( _amount );
                    IERC20( sOHM ).safeTransfer( locker, _amount );
                }
            
                /**
                    @notice reclaim bonus from locked staking contract
                    @param _amount uint
                 */
                function returnLockBonus( uint _amount ) external {
                    require( msg.sender == locker );
                    totalBonus = totalBonus.sub( _amount );
                    IERC20( sOHM ).safeTransferFrom( locker, address(this), _amount );
                }
            
                enum CONTRACTS { DISTRIBUTOR, WARMUP, LOCKER }
            
                /**
                    @notice sets the contract address for LP staking
                    @param _contract address
                 */
                function setContract( CONTRACTS _contract, address _address ) external onlyManager() {
                    if( _contract == CONTRACTS.DISTRIBUTOR ) { // 0
                        distributor = _address;
                    } else if ( _contract == CONTRACTS.WARMUP ) { // 1
                        require( warmupContract == address( 0 ), "Warmup cannot be set more than once" );
                        warmupContract = _address;
                    } else if ( _contract == CONTRACTS.LOCKER ) { // 2
                        require( locker == address(0), "Locker cannot be set more than once" );
                        locker = _address;
                    }
                }
                
                /**
                 * @notice set warmup period for new stakers
                 * @param _warmupPeriod uint
                 */
                function setWarmup( uint _warmupPeriod ) external onlyManager() {
                    warmupPeriod = _warmupPeriod;
                }
            }

            File 5 of 6: StakingWarmup
            // SPDX-License-Identifier: AGPL-3.0-or-later
            pragma solidity 0.7.5;
            
            
            interface IERC20 {
                function decimals() external view returns (uint8);
              /**
               * @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);
            }
            
            contract StakingWarmup {
            
                address public immutable staking;
                address public immutable sOHM;
            
                constructor ( address _staking, address _sOHM ) {
                    require( _staking != address(0) );
                    staking = _staking;
                    require( _sOHM != address(0) );
                    sOHM = _sOHM;
                }
            
                function retrieve( address _staker, uint _amount ) external {
                    require( msg.sender == staking );
                    IERC20( sOHM ).transfer( _staker, _amount );
                }
            }

            File 6 of 6: 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;
                }
            }