ETH Price: $2,436.38 (-0.41%)

Transaction Decoder

Block:
21399651 at Dec-14-2024 08:46:59 AM +UTC
Transaction Fee:
0.001176991299275478 ETH $2.87
Gas Used:
143,634 Gas / 8.194378067 Gwei

Emitted Events:

128 LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000512daa85f8d2c863d0cfc8f65ab7842629d409f6, 0x0000000000000000000000003cbafbc98d4e38efc158b02c5334aeac309fbd51, 0000000000000000000000000000000000000000000000b515ded612a7d3e1f8 )
129 LinkToken.Approval( owner=[Receiver] GnosisSafeProxy, spender=TransparentUpgradeableProxy, value=3340436609636668531192 )
130 LinkToken.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000003cbafbc98d4e38efc158b02c5334aeac309fbd51, 0x000000000000000000000000c3d688b66703497daa19211eedff47f25384cdc3, 0000000000000000000000000000000000000000000000b515ded612a7d3e1f8 )
131 TransparentUpgradeableProxy.0xfa56f7b24f17183d81894d3ac2ee654e3c26388d17a28dbd9549b8114304e1f4( 0xfa56f7b24f17183d81894d3ac2ee654e3c26388d17a28dbd9549b8114304e1f4, 0x0000000000000000000000003cbafbc98d4e38efc158b02c5334aeac309fbd51, 0x0000000000000000000000003cbafbc98d4e38efc158b02c5334aeac309fbd51, 0x000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca, 0000000000000000000000000000000000000000000000b515ded612a7d3e1f8 )
132 DefisaverLogger.ActionDirectEvent( 0xf28c1e8e1a8c97027796e625e1ed041028c9642e14da6e7ad2c18838a59a2d8c, 0x0000000000000000000000003cbafbc98d4e38efc158b02c5334aeac309fbd51, 0x71643f58b92ebee5d04f04bd5c7b0b09cdf244800c5cfaeb55721fd93d562544, 0000000000000000000000000000000000000000000000000000000000000020, 00000000000000000000000000000000000000000000000000000000000000a0, 000000000000000000000000c3d688b66703497daa19211eedff47f25384cdc3, 000000000000000000000000514910771af9ca656af840dff83e8264ecf986ca, 0000000000000000000000000000000000000000000000b515ded612a7d3e1f8, 000000000000000000000000512daa85f8d2c863d0cfc8f65ab7842629d409f6, 0000000000000000000000003cbafbc98d4e38efc158b02c5334aeac309fbd51 )
133 GnosisSafeProxy.0x442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e( 0x442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e, e71300c9772fbb8dced9ac71abc898ac111c89989b29d457698d29e9b8043904, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x3cBafbC9...C309fBD51
(Titan Builder)
8.977593688153457633 Eth8.977665505153457633 Eth0.000071817
0x512dAa85...629D409F6
0.270888199117096848 Eth
Nonce: 347
0.26971120781782137 Eth
Nonce: 348
0.001176991299275478
0x51491077...4EcF986CA
0xc3d688B6...25384cdc3

Execution Trace

GnosisSafeProxy.6a761202( )
  • GnosisSafe.execTransaction( to=0xcc073e12fE8d8035389954Bf2BF6C3AAcaDaDC39, value=0, data=0x389F87FF000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000A0000000000000000000000000C3D688B66703497DAA19211EEDFF47F25384CDC3000000000000000000000000514910771AF9CA656AF840DFF83E8264ECF986CA0000000000000000000000000000000000000000000000B515DED612A7D3E1F8000000000000000000000000512DAA85F8D2C863D0CFC8F65AB7842629D409F60000000000000000000000003CBAFBC98D4E38EFC158B02C5334AEAC309FBD51, operation=1, safeTxGas=0, baseGas=0, gasPrice=0, gasToken=0x0000000000000000000000000000000000000000, refundReceiver=0x25aa0f9a42eE4Ea2Dc7f3c9fF02F558dcb0445a3, signatures=0x000000000000000000000000512DAA85F8D2C863D0CFC8F65AB7842629D409F6000000000000000000000000000000000000000000000000000000000000000001 ) => ( success=True )
    • CompV3Supply.executeActionDirect( _callData=0x000000000000000000000000C3D688B66703497DAA19211EEDFF47F25384CDC3000000000000000000000000514910771AF9CA656AF840DFF83E8264ECF986CA0000000000000000000000000000000000000000000000B515DED612A7D3E1F8000000000000000000000000512DAA85F8D2C863D0CFC8F65AB7842629D409F60000000000000000000000003CBAFBC98D4E38EFC158B02C5334AEAC309FBD51 )
      • LinkToken.transferFrom( _from=0x512dAa85f8d2c863D0CFC8F65ab7842629D409F6, _to=0x3cBafbC98D4e38eFc158B02c5334aEAC309fBD51, _value=3340436609636668531192 ) => ( True )
      • LinkToken.allowance( _owner=0x3cBafbC98D4e38eFc158B02c5334aEAC309fBD51, _spender=0xc3d688B66703497DAA19211EEdff47f25384cdc3 ) => ( remaining=0 )
      • LinkToken.approve( _spender=0xc3d688B66703497DAA19211EEdff47f25384cdc3, _value=3340436609636668531192 ) => ( True )
      • TransparentUpgradeableProxy.STATICCALL( )
        • Comet.DELEGATECALL( )
        • TransparentUpgradeableProxy.4232cd63( )
          • Comet.supplyTo( dst=0x3cBafbC98D4e38eFc158B02c5334aEAC309fBD51, asset=0x514910771AF9Ca656af840dff83E8264EcF986CA, amount=3340436609636668531192 )
            • LinkToken.transferFrom( _from=0x3cBafbC98D4e38eFc158B02c5334aEAC309fBD51, _to=0xc3d688B66703497DAA19211EEdff47f25384cdc3, _value=3340436609636668531192 ) => ( True )
            • DefisaverLogger.logActionDirectEvent( _logName=CompV3Supply, _data=0x000000000000000000000000C3D688B66703497DAA19211EEDFF47F25384CDC3000000000000000000000000514910771AF9CA656AF840DFF83E8264ECF986CA0000000000000000000000000000000000000000000000B515DED612A7D3E1F8000000000000000000000000512DAA85F8D2C863D0CFC8F65AB7842629D409F60000000000000000000000003CBAFBC98D4E38EFC158B02C5334AEAC309FBD51 )
              File 1 of 7: GnosisSafeProxy
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              
              /// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
              /// @author Richard Meissner - <[email protected]>
              interface IProxy {
                  function masterCopy() external view returns (address);
              }
              
              /// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
              /// @author Stefan George - <[email protected]>
              /// @author Richard Meissner - <[email protected]>
              contract GnosisSafeProxy {
                  // singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
                  // To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
                  address internal singleton;
              
                  /// @dev Constructor function sets address of singleton contract.
                  /// @param _singleton Singleton address.
                  constructor(address _singleton) {
                      require(_singleton != address(0), "Invalid singleton address provided");
                      singleton = _singleton;
                  }
              
                  /// @dev Fallback function forwards all transactions and returns all received return data.
                  fallback() external payable {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
                          // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
                          if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
                              mstore(0, _singleton)
                              return(0, 0x20)
                          }
                          calldatacopy(0, 0, calldatasize())
                          let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
                          returndatacopy(0, 0, returndatasize())
                          if eq(success, 0) {
                              revert(0, returndatasize())
                          }
                          return(0, returndatasize())
                      }
                  }
              }
              
              /// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
              /// @author Stefan George - <[email protected]>
              contract GnosisSafeProxyFactory {
                  event ProxyCreation(GnosisSafeProxy proxy, address singleton);
              
                  /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                  /// @param singleton Address of singleton contract.
                  /// @param data Payload for message call sent to new proxy contract.
                  function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
                      proxy = new GnosisSafeProxy(singleton);
                      if (data.length > 0)
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
                                  revert(0, 0)
                              }
                          }
                      emit ProxyCreation(proxy, singleton);
                  }
              
                  /// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
                  function proxyRuntimeCode() public pure returns (bytes memory) {
                      return type(GnosisSafeProxy).runtimeCode;
                  }
              
                  /// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
                  function proxyCreationCode() public pure returns (bytes memory) {
                      return type(GnosisSafeProxy).creationCode;
                  }
              
                  /// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
                  ///      This method is only meant as an utility to be called from other methods
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function deployProxyWithNonce(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce
                  ) internal returns (GnosisSafeProxy proxy) {
                      // If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
                      bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
                      bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
                      }
                      require(address(proxy) != address(0), "Create2 call failed");
                  }
              
                  /// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function createProxyWithNonce(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce
                  ) public returns (GnosisSafeProxy proxy) {
                      proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                      if (initializer.length > 0)
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
                                  revert(0, 0)
                              }
                          }
                      emit ProxyCreation(proxy, _singleton);
                  }
              
                  /// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  /// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
                  function createProxyWithCallback(
                      address _singleton,
                      bytes memory initializer,
                      uint256 saltNonce,
                      IProxyCreationCallback callback
                  ) public returns (GnosisSafeProxy proxy) {
                      uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
                      proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
                      if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
                  }
              
                  /// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
                  ///      This method is only meant for address calculation purpose when you use an initializer that would revert,
                  ///      therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
                  /// @param _singleton Address of singleton contract.
                  /// @param initializer Payload for message call sent to new proxy contract.
                  /// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
                  function calculateCreateProxyWithNonceAddress(
                      address _singleton,
                      bytes calldata initializer,
                      uint256 saltNonce
                  ) external returns (GnosisSafeProxy proxy) {
                      proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
                      revert(string(abi.encodePacked(proxy)));
                  }
              }
              
              interface IProxyCreationCallback {
                  function proxyCreated(
                      GnosisSafeProxy proxy,
                      address _singleton,
                      bytes calldata initializer,
                      uint256 saltNonce
                  ) external;
              }

              File 2 of 7: LinkToken
              pragma solidity ^0.4.16;
              
              
              /**
               * @title SafeMath
               * @dev Math operations with safety checks that throw on error
               */
              library SafeMath {
                function mul(uint256 a, uint256 b) internal constant returns (uint256) {
                  uint256 c = a * b;
                  assert(a == 0 || c / a == b);
                  return c;
                }
              
                function div(uint256 a, uint256 b) internal constant returns (uint256) {
                  // assert(b > 0); // Solidity automatically throws when dividing by 0
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
                }
              
                function sub(uint256 a, uint256 b) internal constant returns (uint256) {
                  assert(b <= a);
                  return a - b;
                }
              
                function add(uint256 a, uint256 b) internal constant returns (uint256) {
                  uint256 c = a + b;
                  assert(c >= a);
                  return c;
                }
              }
              
              
              /**
               * @title ERC20Basic
               * @dev Simpler version of ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/179
               */
              contract ERC20Basic {
                uint256 public totalSupply;
                function balanceOf(address who) constant returns (uint256);
                function transfer(address to, uint256 value) returns (bool);
                event Transfer(address indexed from, address indexed to, uint256 value);
              }
              /**
               * @title ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/20
               */
              contract ERC20 is ERC20Basic {
                function allowance(address owner, address spender) constant returns (uint256);
                function transferFrom(address from, address to, uint256 value) returns (bool);
                function approve(address spender, uint256 value) returns (bool);
                event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              
              contract ERC677 is ERC20 {
                function transferAndCall(address to, uint value, bytes data) returns (bool success);
              
                event Transfer(address indexed from, address indexed to, uint value, bytes data);
              }
              
              contract ERC677Receiver {
                function onTokenTransfer(address _sender, uint _value, bytes _data);
              }
              
              /**
               * @title Basic token
               * @dev Basic version of StandardToken, with no allowances. 
               */
              contract BasicToken is ERC20Basic {
                using SafeMath for uint256;
              
                mapping(address => uint256) balances;
              
                /**
                * @dev transfer token for a specified address
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                */
                function transfer(address _to, uint256 _value) returns (bool) {
                  balances[msg.sender] = balances[msg.sender].sub(_value);
                  balances[_to] = balances[_to].add(_value);
                  Transfer(msg.sender, _to, _value);
                  return true;
                }
              
                /**
                * @dev Gets the balance of the specified address.
                * @param _owner The address to query the the balance of. 
                * @return An uint256 representing the amount owned by the passed address.
                */
                function balanceOf(address _owner) constant returns (uint256 balance) {
                  return balances[_owner];
                }
              
              }
              
              
              /**
               * @title Standard ERC20 token
               *
               * @dev Implementation of the basic standard token.
               * @dev https://github.com/ethereum/EIPs/issues/20
               * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
               */
              contract StandardToken is ERC20, BasicToken {
              
                mapping (address => mapping (address => uint256)) allowed;
              
              
                /**
                 * @dev Transfer tokens from one address to another
                 * @param _from address The address which you want to send tokens from
                 * @param _to address The address which you want to transfer to
                 * @param _value uint256 the amount of tokens to be transferred
                 */
                function transferFrom(address _from, address _to, uint256 _value) returns (bool) {
                  var _allowance = allowed[_from][msg.sender];
              
                  // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
                  // require (_value <= _allowance);
              
                  balances[_from] = balances[_from].sub(_value);
                  balances[_to] = balances[_to].add(_value);
                  allowed[_from][msg.sender] = _allowance.sub(_value);
                  Transfer(_from, _to, _value);
                  return true;
                }
              
                /**
                 * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                 * @param _spender The address which will spend the funds.
                 * @param _value The amount of tokens to be spent.
                 */
                function approve(address _spender, uint256 _value) returns (bool) {
                  allowed[msg.sender][_spender] = _value;
                  Approval(msg.sender, _spender, _value);
                  return true;
                }
              
                /**
                 * @dev Function to check the amount of tokens that an owner allowed to a spender.
                 * @param _owner address The address which owns the funds.
                 * @param _spender address The address which will spend the funds.
                 * @return A uint256 specifying the amount of tokens still available for the spender.
                 */
                function allowance(address _owner, address _spender) constant returns (uint256 remaining) {
                  return allowed[_owner][_spender];
                }
                
                  /*
                 * approve should be called when allowed[_spender] == 0. To increment
                 * allowed value is better to use this function to avoid 2 calls (and wait until 
                 * the first transaction is mined)
                 * From MonolithDAO Token.sol
                 */
                function increaseApproval (address _spender, uint _addedValue) 
                  returns (bool success) {
                  allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
                  Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
                  return true;
                }
              
                function decreaseApproval (address _spender, uint _subtractedValue) 
                  returns (bool success) {
                  uint oldValue = allowed[msg.sender][_spender];
                  if (_subtractedValue > oldValue) {
                    allowed[msg.sender][_spender] = 0;
                  } else {
                    allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
                  }
                  Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
                  return true;
                }
              
              }
              
              contract ERC677Token is ERC677 {
              
                /**
                * @dev transfer token to a contract address with additional data if the recipient is a contact.
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                * @param _data The extra data to be passed to the receiving contract.
                */
                function transferAndCall(address _to, uint _value, bytes _data)
                  public
                  returns (bool success)
                {
                  super.transfer(_to, _value);
                  Transfer(msg.sender, _to, _value, _data);
                  if (isContract(_to)) {
                    contractFallback(_to, _value, _data);
                  }
                  return true;
                }
              
              
                // PRIVATE
              
                function contractFallback(address _to, uint _value, bytes _data)
                  private
                {
                  ERC677Receiver receiver = ERC677Receiver(_to);
                  receiver.onTokenTransfer(msg.sender, _value, _data);
                }
              
                function isContract(address _addr)
                  private
                  returns (bool hasCode)
                {
                  uint length;
                  assembly { length := extcodesize(_addr) }
                  return length > 0;
                }
              
              }
              
              contract LinkToken is StandardToken, ERC677Token {
              
                uint public constant totalSupply = 10**27;
                string public constant name = 'ChainLink Token';
                uint8 public constant decimals = 18;
                string public constant symbol = 'LINK';
              
                function LinkToken()
                  public
                {
                  balances[msg.sender] = totalSupply;
                }
              
                /**
                * @dev transfer token to a specified address with additional data if the recipient is a contract.
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                * @param _data The extra data to be passed to the receiving contract.
                */
                function transferAndCall(address _to, uint _value, bytes _data)
                  public
                  validRecipient(_to)
                  returns (bool success)
                {
                  return super.transferAndCall(_to, _value, _data);
                }
              
                /**
                * @dev transfer token to a specified address.
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                */
                function transfer(address _to, uint _value)
                  public
                  validRecipient(_to)
                  returns (bool success)
                {
                  return super.transfer(_to, _value);
                }
              
                /**
                 * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                 * @param _spender The address which will spend the funds.
                 * @param _value The amount of tokens to be spent.
                 */
                function approve(address _spender, uint256 _value)
                  public
                  validRecipient(_spender)
                  returns (bool)
                {
                  return super.approve(_spender,  _value);
                }
              
                /**
                 * @dev Transfer tokens from one address to another
                 * @param _from address The address which you want to send tokens from
                 * @param _to address The address which you want to transfer to
                 * @param _value uint256 the amount of tokens to be transferred
                 */
                function transferFrom(address _from, address _to, uint256 _value)
                  public
                  validRecipient(_to)
                  returns (bool)
                {
                  return super.transferFrom(_from, _to, _value);
                }
              
              
                // MODIFIERS
              
                modifier validRecipient(address _recipient) {
                  require(_recipient != address(0) && _recipient != address(this));
                  _;
                }
              
              }

              File 3 of 7: TransparentUpgradeableProxy
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/transparent/TransparentUpgradeableProxy.sol)
              pragma solidity ^0.8.0;
              import "../ERC1967/ERC1967Proxy.sol";
              /**
               * @dev This contract implements a proxy that is upgradeable by an admin.
               *
               * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
               * clashing], which can potentially be used in an attack, this contract uses the
               * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
               * things that go hand in hand:
               *
               * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
               * that call matches one of the admin functions exposed by the proxy itself.
               * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
               * implementation. If the admin tries to call a function on the implementation it will fail with an error that says
               * "admin cannot fallback to proxy target".
               *
               * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
               * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
               * to sudden errors when trying to call a function from the proxy implementation.
               *
               * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
               * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
               */
              contract TransparentUpgradeableProxy is ERC1967Proxy {
                  /**
                   * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
                   * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
                   */
                  constructor(
                      address _logic,
                      address admin_,
                      bytes memory _data
                  ) payable ERC1967Proxy(_logic, _data) {
                      assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
                      _changeAdmin(admin_);
                  }
                  /**
                   * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
                   */
                  modifier ifAdmin() {
                      if (msg.sender == _getAdmin()) {
                          _;
                      } else {
                          _fallback();
                      }
                  }
                  /**
                   * @dev Returns the current admin.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
                   */
                  function admin() external ifAdmin returns (address admin_) {
                      admin_ = _getAdmin();
                  }
                  /**
                   * @dev Returns the current implementation.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
                   *
                   * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
                   * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
                   * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
                   */
                  function implementation() external ifAdmin returns (address implementation_) {
                      implementation_ = _implementation();
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
                   */
                  function changeAdmin(address newAdmin) external virtual ifAdmin {
                      _changeAdmin(newAdmin);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
                   */
                  function upgradeTo(address newImplementation) external ifAdmin {
                      _upgradeToAndCall(newImplementation, bytes(""), false);
                  }
                  /**
                   * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
                   * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
                   * proxied contract.
                   *
                   * NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
                   */
                  function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
                      _upgradeToAndCall(newImplementation, data, true);
                  }
                  /**
                   * @dev Returns the current admin.
                   */
                  function _admin() internal view virtual returns (address) {
                      return _getAdmin();
                  }
                  /**
                   * @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
                   */
                  function _beforeFallback() internal virtual override {
                      require(msg.sender != _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
                      super._beforeFallback();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/ERC1967/ERC1967Proxy.sol)
              pragma solidity ^0.8.0;
              import "../Proxy.sol";
              import "./ERC1967Upgrade.sol";
              /**
               * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
               * implementation address that can be changed. This address is stored in storage in the location specified by
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
               * implementation behind the proxy.
               */
              contract ERC1967Proxy is Proxy, ERC1967Upgrade {
                  /**
                   * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
                   *
                   * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
                   * function call, and allows initializating the storage of the proxy like a Solidity constructor.
                   */
                  constructor(address _logic, bytes memory _data) payable {
                      assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
                      _upgradeToAndCall(_logic, _data, false);
                  }
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _implementation() internal view virtual override returns (address impl) {
                      return ERC1967Upgrade._getImplementation();
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (proxy/Proxy.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
               * be specified by overriding the virtual {_implementation} function.
               *
               * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
               * different contract through the {_delegate} function.
               *
               * The success and return data of the delegated call will be returned back to the caller of the proxy.
               */
              abstract contract Proxy {
                  /**
                   * @dev Delegates the current call to `implementation`.
                   *
                   * This function does not return to its internal call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal virtual {
                      assembly {
                          // Copy msg.data. We take full control of memory in this inline assembly
                          // block because it will not return to Solidity code. We overwrite the
                          // Solidity scratch pad at memory position 0.
                          calldatacopy(0, 0, calldatasize())
                          // Call the implementation.
                          // out and outsize are 0 because we don't know the size yet.
                          let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                          // Copy the returned data.
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          // delegatecall returns 0 on error.
                          case 0 {
                              revert(0, returndatasize())
                          }
                          default {
                              return(0, returndatasize())
                          }
                      }
                  }
                  /**
                   * @dev This is a virtual function that should be overriden so it returns the address to which the fallback function
                   * and {_fallback} should delegate.
                   */
                  function _implementation() internal view virtual returns (address);
                  /**
                   * @dev Delegates the current call to the address returned by `_implementation()`.
                   *
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback() internal virtual {
                      _beforeFallback();
                      _delegate(_implementation());
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
                   * function in the contract matches the call data.
                   */
                  fallback() external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                   * is empty.
                   */
                  receive() external payable virtual {
                      _fallback();
                  }
                  /**
                   * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
                   * call, or as part of the Solidity `fallback` or `receive` functions.
                   *
                   * If overriden should call `super._beforeFallback()`.
                   */
                  function _beforeFallback() internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)
              pragma solidity ^0.8.2;
              import "../beacon/IBeacon.sol";
              import "../../interfaces/draft-IERC1822.sol";
              import "../../utils/Address.sol";
              import "../../utils/StorageSlot.sol";
              /**
               * @dev This abstract contract provides getters and event emitting update functions for
               * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
               *
               * _Available since v4.1._
               *
               * @custom:oz-upgrades-unsafe-allow delegatecall
               */
              abstract contract ERC1967Upgrade {
                  // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
                  bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
                  /**
                   * @dev Storage slot with the address of the current implementation.
                   * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @dev Emitted when the implementation is upgraded.
                   */
                  event Upgraded(address indexed implementation);
                  /**
                   * @dev Returns the current implementation address.
                   */
                  function _getImplementation() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 implementation slot.
                   */
                  function _setImplementation(address newImplementation) private {
                      require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
                      StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
                  }
                  /**
                   * @dev Perform implementation upgrade
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeTo(address newImplementation) internal {
                      _setImplementation(newImplementation);
                      emit Upgraded(newImplementation);
                  }
                  /**
                   * @dev Perform implementation upgrade with additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCall(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _upgradeTo(newImplementation);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(newImplementation, data);
                      }
                  }
                  /**
                   * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
                   *
                   * Emits an {Upgraded} event.
                   */
                  function _upgradeToAndCallUUPS(
                      address newImplementation,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      // Upgrades from old implementations will perform a rollback test. This test requires the new
                      // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
                      // this special case will break upgrade paths from old UUPS implementation to new ones.
                      if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
                          _setImplementation(newImplementation);
                      } else {
                          try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
                              require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
                          } catch {
                              revert("ERC1967Upgrade: new implementation is not UUPS");
                          }
                          _upgradeToAndCall(newImplementation, data, forceCall);
                      }
                  }
                  /**
                   * @dev Storage slot with the admin of the contract.
                   * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
                   * validated in the constructor.
                   */
                  bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
                  /**
                   * @dev Emitted when the admin account has changed.
                   */
                  event AdminChanged(address previousAdmin, address newAdmin);
                  /**
                   * @dev Returns the current admin.
                   */
                  function _getAdmin() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
                  }
                  /**
                   * @dev Stores a new address in the EIP1967 admin slot.
                   */
                  function _setAdmin(address newAdmin) private {
                      require(newAdmin != address(0), "ERC1967: new admin is the zero address");
                      StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
                  }
                  /**
                   * @dev Changes the admin of the proxy.
                   *
                   * Emits an {AdminChanged} event.
                   */
                  function _changeAdmin(address newAdmin) internal {
                      emit AdminChanged(_getAdmin(), newAdmin);
                      _setAdmin(newAdmin);
                  }
                  /**
                   * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
                   * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
                   */
                  bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
                  /**
                   * @dev Emitted when the beacon is upgraded.
                   */
                  event BeaconUpgraded(address indexed beacon);
                  /**
                   * @dev Returns the current beacon.
                   */
                  function _getBeacon() internal view returns (address) {
                      return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
                  }
                  /**
                   * @dev Stores a new beacon in the EIP1967 beacon slot.
                   */
                  function _setBeacon(address newBeacon) private {
                      require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
                      require(
                          Address.isContract(IBeacon(newBeacon).implementation()),
                          "ERC1967: beacon implementation is not a contract"
                      );
                      StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
                  }
                  /**
                   * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
                   * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
                   *
                   * Emits a {BeaconUpgraded} event.
                   */
                  function _upgradeBeaconToAndCall(
                      address newBeacon,
                      bytes memory data,
                      bool forceCall
                  ) internal {
                      _setBeacon(newBeacon);
                      emit BeaconUpgraded(newBeacon);
                      if (data.length > 0 || forceCall) {
                          Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev This is the interface that {BeaconProxy} expects of its beacon.
               */
              interface IBeacon {
                  /**
                   * @dev Must return an address that can be used as a delegate call target.
                   *
                   * {BeaconProxy} will check that this address is a contract.
                   */
                  function implementation() external view returns (address);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
               * proxy whose upgrades are fully controlled by the current implementation.
               */
              interface IERC1822Proxiable {
                  /**
                   * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
                   * address.
                   *
                   * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
                   * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
                   * function revert if invoked through a proxy.
                   */
                  function proxiableUUID() external view returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
              pragma solidity ^0.8.1;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                  /**
                   * @dev Returns true if `account` is a contract.
                   *
                   * [IMPORTANT]
                   * ====
                   * It is unsafe to assume that an address for which this function returns
                   * false is an externally-owned account (EOA) and not a contract.
                   *
                   * Among others, `isContract` will return false for the following
                   * types of addresses:
                   *
                   *  - an externally-owned account
                   *  - a contract in construction
                   *  - an address where a contract will be created
                   *  - an address where a contract lived, but was destroyed
                   * ====
                   *
                   * [IMPORTANT]
                   * ====
                   * You shouldn't rely on `isContract` to protect against flash loan attacks!
                   *
                   * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
                   * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
                   * constructor.
                   * ====
                   */
                  function isContract(address account) internal view returns (bool) {
                      // This method relies on extcodesize/address.code.length, which returns 0
                      // for contracts in construction, since the code is only stored at the end
                      // of the constructor execution.
                      return account.code.length > 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");
                      (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");
                      (bool success, bytes memory returndata) = target.call{value: value}(data);
                      return verifyCallResult(success, returndata, 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");
                      (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.4._
                   */
                  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.4._
                   */
                  function functionDelegateCall(
                      address target,
                      bytes memory data,
                      string memory errorMessage
                  ) internal returns (bytes memory) {
                      require(isContract(target), "Address: delegate call to non-contract");
                      (bool success, bytes memory returndata) = target.delegatecall(data);
                      return verifyCallResult(success, returndata, errorMessage);
                  }
                  /**
                   * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
                   * revert reason using the provided one.
                   *
                   * _Available since v4.3._
                   */
                  function verifyCallResult(
                      bool success,
                      bytes memory returndata,
                      string memory errorMessage
                  ) internal 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
                              assembly {
                                  let returndata_size := mload(returndata)
                                  revert(add(32, returndata), returndata_size)
                              }
                          } else {
                              revert(errorMessage);
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              // OpenZeppelin Contracts v4.4.1 (utils/StorageSlot.sol)
              pragma solidity ^0.8.0;
              /**
               * @dev Library for reading and writing primitive types to specific storage slots.
               *
               * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
               * This library helps with reading and writing to such slots without the need for inline assembly.
               *
               * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
               *
               * Example usage to set ERC1967 implementation slot:
               * ```
               * contract ERC1967 {
               *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
               *
               *     function _getImplementation() internal view returns (address) {
               *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
               *     }
               *
               *     function _setImplementation(address newImplementation) internal {
               *         require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
               *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
               *     }
               * }
               * ```
               *
               * _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
               */
              library StorageSlot {
                  struct AddressSlot {
                      address value;
                  }
                  struct BooleanSlot {
                      bool value;
                  }
                  struct Bytes32Slot {
                      bytes32 value;
                  }
                  struct Uint256Slot {
                      uint256 value;
                  }
                  /**
                   * @dev Returns an `AddressSlot` with member `value` located at `slot`.
                   */
                  function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
                   */
                  function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
                   */
                  function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
                  /**
                   * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
                   */
                  function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
                      assembly {
                          r.slot := slot
                      }
                  }
              }
              

              File 4 of 7: DefisaverLogger
              // SPDX-License-Identifier: MIT
              
              pragma solidity =0.8.10;
              
              contract DefisaverLogger {
                  event RecipeEvent(
                      address indexed caller,
                      string indexed logName
                  );
              
                  event ActionDirectEvent(
                      address indexed caller,
                      string indexed logName,
                      bytes data
                  );
              
                  function logRecipeEvent(
                      string memory _logName
                  ) public {
                      emit RecipeEvent(msg.sender, _logName);
                  }
              
                  function logActionDirectEvent(
                      string memory _logName,
                      bytes memory _data
                  ) public {
                      emit ActionDirectEvent(msg.sender, _logName, _data);
                  }
              }

              File 5 of 7: GnosisSafe
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "./base/ModuleManager.sol";
              import "./base/OwnerManager.sol";
              import "./base/FallbackManager.sol";
              import "./base/GuardManager.sol";
              import "./common/EtherPaymentFallback.sol";
              import "./common/Singleton.sol";
              import "./common/SignatureDecoder.sol";
              import "./common/SecuredTokenTransfer.sol";
              import "./common/StorageAccessible.sol";
              import "./interfaces/ISignatureValidator.sol";
              import "./external/GnosisSafeMath.sol";
              /// @title Gnosis Safe - A multisignature wallet with support for confirmations using signed messages based on ERC191.
              /// @author Stefan George - <[email protected]>
              /// @author Richard Meissner - <[email protected]>
              contract GnosisSafe is
                  EtherPaymentFallback,
                  Singleton,
                  ModuleManager,
                  OwnerManager,
                  SignatureDecoder,
                  SecuredTokenTransfer,
                  ISignatureValidatorConstants,
                  FallbackManager,
                  StorageAccessible,
                  GuardManager
              {
                  using GnosisSafeMath for uint256;
                  string public constant VERSION = "1.3.0";
                  // keccak256(
                  //     "EIP712Domain(uint256 chainId,address verifyingContract)"
                  // );
                  bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
                  // keccak256(
                  //     "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
                  // );
                  bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;
                  event SafeSetup(address indexed initiator, address[] owners, uint256 threshold, address initializer, address fallbackHandler);
                  event ApproveHash(bytes32 indexed approvedHash, address indexed owner);
                  event SignMsg(bytes32 indexed msgHash);
                  event ExecutionFailure(bytes32 txHash, uint256 payment);
                  event ExecutionSuccess(bytes32 txHash, uint256 payment);
                  uint256 public nonce;
                  bytes32 private _deprecatedDomainSeparator;
                  // Mapping to keep track of all message hashes that have been approve by ALL REQUIRED owners
                  mapping(bytes32 => uint256) public signedMessages;
                  // Mapping to keep track of all hashes (message or transaction) that have been approve by ANY owners
                  mapping(address => mapping(bytes32 => uint256)) public approvedHashes;
                  // This constructor ensures that this contract can only be used as a master copy for Proxy contracts
                  constructor() {
                      // By setting the threshold it is not possible to call setup anymore,
                      // so we create a Safe with 0 owners and threshold 1.
                      // This is an unusable Safe, perfect for the singleton
                      threshold = 1;
                  }
                  /// @dev Setup function sets initial storage of contract.
                  /// @param _owners List of Safe owners.
                  /// @param _threshold Number of required confirmations for a Safe transaction.
                  /// @param to Contract address for optional delegate call.
                  /// @param data Data payload for optional delegate call.
                  /// @param fallbackHandler Handler for fallback calls to this contract
                  /// @param paymentToken Token that should be used for the payment (0 is ETH)
                  /// @param payment Value that should be paid
                  /// @param paymentReceiver Adddress that should receive the payment (or 0 if tx.origin)
                  function setup(
                      address[] calldata _owners,
                      uint256 _threshold,
                      address to,
                      bytes calldata data,
                      address fallbackHandler,
                      address paymentToken,
                      uint256 payment,
                      address payable paymentReceiver
                  ) external {
                      // setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
                      setupOwners(_owners, _threshold);
                      if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
                      // As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
                      setupModules(to, data);
                      if (payment > 0) {
                          // To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
                          // baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
                          handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
                      }
                      emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler);
                  }
                  /// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction.
                  ///      Note: The fees are always transferred, even if the user transaction fails.
                  /// @param to Destination address of Safe transaction.
                  /// @param value Ether value of Safe transaction.
                  /// @param data Data payload of Safe transaction.
                  /// @param operation Operation type of Safe transaction.
                  /// @param safeTxGas Gas that should be used for the Safe transaction.
                  /// @param baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
                  /// @param gasPrice Gas price that should be used for the payment calculation.
                  /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                  /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                  /// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
                  function execTransaction(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Enum.Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address payable refundReceiver,
                      bytes memory signatures
                  ) public payable virtual returns (bool success) {
                      bytes32 txHash;
                      // Use scope here to limit variable lifetime and prevent `stack too deep` errors
                      {
                          bytes memory txHashData =
                              encodeTransactionData(
                                  // Transaction info
                                  to,
                                  value,
                                  data,
                                  operation,
                                  safeTxGas,
                                  // Payment info
                                  baseGas,
                                  gasPrice,
                                  gasToken,
                                  refundReceiver,
                                  // Signature info
                                  nonce
                              );
                          // Increase nonce and execute transaction.
                          nonce++;
                          txHash = keccak256(txHashData);
                          checkSignatures(txHash, txHashData, signatures);
                      }
                      address guard = getGuard();
                      {
                          if (guard != address(0)) {
                              Guard(guard).checkTransaction(
                                  // Transaction info
                                  to,
                                  value,
                                  data,
                                  operation,
                                  safeTxGas,
                                  // Payment info
                                  baseGas,
                                  gasPrice,
                                  gasToken,
                                  refundReceiver,
                                  // Signature info
                                  signatures,
                                  msg.sender
                              );
                          }
                      }
                      // We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500)
                      // We also include the 1/64 in the check that is not send along with a call to counteract potential shortings because of EIP-150
                      require(gasleft() >= ((safeTxGas * 64) / 63).max(safeTxGas + 2500) + 500, "GS010");
                      // Use scope here to limit variable lifetime and prevent `stack too deep` errors
                      {
                          uint256 gasUsed = gasleft();
                          // If the gasPrice is 0 we assume that nearly all available gas can be used (it is always more than safeTxGas)
                          // We only substract 2500 (compared to the 3000 before) to ensure that the amount passed is still higher than safeTxGas
                          success = execute(to, value, data, operation, gasPrice == 0 ? (gasleft() - 2500) : safeTxGas);
                          gasUsed = gasUsed.sub(gasleft());
                          // If no safeTxGas and no gasPrice was set (e.g. both are 0), then the internal tx is required to be successful
                          // This makes it possible to use `estimateGas` without issues, as it searches for the minimum gas where the tx doesn't revert
                          require(success || safeTxGas != 0 || gasPrice != 0, "GS013");
                          // We transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls
                          uint256 payment = 0;
                          if (gasPrice > 0) {
                              payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver);
                          }
                          if (success) emit ExecutionSuccess(txHash, payment);
                          else emit ExecutionFailure(txHash, payment);
                      }
                      {
                          if (guard != address(0)) {
                              Guard(guard).checkAfterExecution(txHash, success);
                          }
                      }
                  }
                  function handlePayment(
                      uint256 gasUsed,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address payable refundReceiver
                  ) private returns (uint256 payment) {
                      // solhint-disable-next-line avoid-tx-origin
                      address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
                      if (gasToken == address(0)) {
                          // For ETH we will only adjust the gas price to not be higher than the actual used gas price
                          payment = gasUsed.add(baseGas).mul(gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
                          require(receiver.send(payment), "GS011");
                      } else {
                          payment = gasUsed.add(baseGas).mul(gasPrice);
                          require(transferToken(gasToken, receiver, payment), "GS012");
                      }
                  }
                  /**
                   * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
                   * @param dataHash Hash of the data (could be either a message hash or transaction hash)
                   * @param data That should be signed (this is passed to an external validator contract)
                   * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
                   */
                  function checkSignatures(
                      bytes32 dataHash,
                      bytes memory data,
                      bytes memory signatures
                  ) public view {
                      // Load threshold to avoid multiple storage loads
                      uint256 _threshold = threshold;
                      // Check that a threshold is set
                      require(_threshold > 0, "GS001");
                      checkNSignatures(dataHash, data, signatures, _threshold);
                  }
                  /**
                   * @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
                   * @param dataHash Hash of the data (could be either a message hash or transaction hash)
                   * @param data That should be signed (this is passed to an external validator contract)
                   * @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
                   * @param requiredSignatures Amount of required valid signatures.
                   */
                  function checkNSignatures(
                      bytes32 dataHash,
                      bytes memory data,
                      bytes memory signatures,
                      uint256 requiredSignatures
                  ) public view {
                      // Check that the provided signature data is not too short
                      require(signatures.length >= requiredSignatures.mul(65), "GS020");
                      // There cannot be an owner with address 0.
                      address lastOwner = address(0);
                      address currentOwner;
                      uint8 v;
                      bytes32 r;
                      bytes32 s;
                      uint256 i;
                      for (i = 0; i < requiredSignatures; i++) {
                          (v, r, s) = signatureSplit(signatures, i);
                          if (v == 0) {
                              // If v is 0 then it is a contract signature
                              // When handling contract signatures the address of the contract is encoded into r
                              currentOwner = address(uint160(uint256(r)));
                              // Check that signature data pointer (s) is not pointing inside the static part of the signatures bytes
                              // This check is not completely accurate, since it is possible that more signatures than the threshold are send.
                              // Here we only check that the pointer is not pointing inside the part that is being processed
                              require(uint256(s) >= requiredSignatures.mul(65), "GS021");
                              // Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
                              require(uint256(s).add(32) <= signatures.length, "GS022");
                              // Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
                              uint256 contractSignatureLen;
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  contractSignatureLen := mload(add(add(signatures, s), 0x20))
                              }
                              require(uint256(s).add(32).add(contractSignatureLen) <= signatures.length, "GS023");
                              // Check signature
                              bytes memory contractSignature;
                              // solhint-disable-next-line no-inline-assembly
                              assembly {
                                  // The signature data for contract signatures is appended to the concatenated signatures and the offset is stored in s
                                  contractSignature := add(add(signatures, s), 0x20)
                              }
                              require(ISignatureValidator(currentOwner).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "GS024");
                          } else if (v == 1) {
                              // If v is 1 then it is an approved hash
                              // When handling approved hashes the address of the approver is encoded into r
                              currentOwner = address(uint160(uint256(r)));
                              // Hashes are automatically approved by the sender of the message or when they have been pre-approved via a separate transaction
                              require(msg.sender == currentOwner || approvedHashes[currentOwner][dataHash] != 0, "GS025");
                          } else if (v > 30) {
                              // If v > 30 then default va (27,28) has been adjusted for eth_sign flow
                              // To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix before applying ecrecover
                              currentOwner = ecrecover(keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\
              32", dataHash)), v - 4, r, s);
                          } else {
                              // Default is the ecrecover flow with the provided data hash
                              // Use ecrecover with the messageHash for EOA signatures
                              currentOwner = ecrecover(dataHash, v, r, s);
                          }
                          require(currentOwner > lastOwner && owners[currentOwner] != address(0) && currentOwner != SENTINEL_OWNERS, "GS026");
                          lastOwner = currentOwner;
                      }
                  }
                  /// @dev Allows to estimate a Safe transaction.
                  ///      This method is only meant for estimation purpose, therefore the call will always revert and encode the result in the revert data.
                  ///      Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`
                  /// @param to Destination address of Safe transaction.
                  /// @param value Ether value of Safe transaction.
                  /// @param data Data payload of Safe transaction.
                  /// @param operation Operation type of Safe transaction.
                  /// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).
                  /// @notice Deprecated in favor of common/StorageAccessible.sol and will be removed in next version.
                  function requiredTxGas(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Enum.Operation operation
                  ) external returns (uint256) {
                      uint256 startGas = gasleft();
                      // We don't provide an error message here, as we use it to return the estimate
                      require(execute(to, value, data, operation, gasleft()));
                      uint256 requiredGas = startGas - gasleft();
                      // Convert response to string and return via error message
                      revert(string(abi.encodePacked(requiredGas)));
                  }
                  /**
                   * @dev Marks a hash as approved. This can be used to validate a hash that is used by a signature.
                   * @param hashToApprove The hash that should be marked as approved for signatures that are verified by this contract.
                   */
                  function approveHash(bytes32 hashToApprove) external {
                      require(owners[msg.sender] != address(0), "GS030");
                      approvedHashes[msg.sender][hashToApprove] = 1;
                      emit ApproveHash(hashToApprove, msg.sender);
                  }
                  /// @dev Returns the chain id used by this contract.
                  function getChainId() public view returns (uint256) {
                      uint256 id;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          id := chainid()
                      }
                      return id;
                  }
                  function domainSeparator() public view returns (bytes32) {
                      return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));
                  }
                  /// @dev Returns the bytes that are hashed to be signed by owners.
                  /// @param to Destination address.
                  /// @param value Ether value.
                  /// @param data Data payload.
                  /// @param operation Operation type.
                  /// @param safeTxGas Gas that should be used for the safe transaction.
                  /// @param baseGas Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
                  /// @param gasPrice Maximum gas price that should be used for this transaction.
                  /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                  /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                  /// @param _nonce Transaction nonce.
                  /// @return Transaction hash bytes.
                  function encodeTransactionData(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Enum.Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address refundReceiver,
                      uint256 _nonce
                  ) public view returns (bytes memory) {
                      bytes32 safeTxHash =
                          keccak256(
                              abi.encode(
                                  SAFE_TX_TYPEHASH,
                                  to,
                                  value,
                                  keccak256(data),
                                  operation,
                                  safeTxGas,
                                  baseGas,
                                  gasPrice,
                                  gasToken,
                                  refundReceiver,
                                  _nonce
                              )
                          );
                      return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
                  }
                  /// @dev Returns hash to be signed by owners.
                  /// @param to Destination address.
                  /// @param value Ether value.
                  /// @param data Data payload.
                  /// @param operation Operation type.
                  /// @param safeTxGas Fas that should be used for the safe transaction.
                  /// @param baseGas Gas costs for data used to trigger the safe transaction.
                  /// @param gasPrice Maximum gas price that should be used for this transaction.
                  /// @param gasToken Token address (or 0 if ETH) that is used for the payment.
                  /// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
                  /// @param _nonce Transaction nonce.
                  /// @return Transaction hash.
                  function getTransactionHash(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Enum.Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address refundReceiver,
                      uint256 _nonce
                  ) public view returns (bytes32) {
                      return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce));
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/Enum.sol";
              /// @title Executor - A contract that can execute transactions
              /// @author Richard Meissner - <[email protected]>
              contract Executor {
                  function execute(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Enum.Operation operation,
                      uint256 txGas
                  ) internal returns (bool success) {
                      if (operation == Enum.Operation.DelegateCall) {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
                          }
                      } else {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
                          }
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/SelfAuthorized.sol";
              /// @title Fallback Manager - A contract that manages fallback calls made to this contract
              /// @author Richard Meissner - <[email protected]>
              contract FallbackManager is SelfAuthorized {
                  event ChangedFallbackHandler(address handler);
                  // keccak256("fallback_manager.handler.address")
                  bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5;
                  function internalSetFallbackHandler(address handler) internal {
                      bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(slot, handler)
                      }
                  }
                  /// @dev Allows to add a contract to handle fallback calls.
                  ///      Only fallback calls without value and with data will be forwarded.
                  ///      This can only be done via a Safe transaction.
                  /// @param handler contract to handle fallbacks calls.
                  function setFallbackHandler(address handler) public authorized {
                      internalSetFallbackHandler(handler);
                      emit ChangedFallbackHandler(handler);
                  }
                  // solhint-disable-next-line payable-fallback,no-complex-fallback
                  fallback() external {
                      bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let handler := sload(slot)
                          if iszero(handler) {
                              return(0, 0)
                          }
                          calldatacopy(0, 0, calldatasize())
                          // The msg.sender address is shifted to the left by 12 bytes to remove the padding
                          // Then the address without padding is stored right after the calldata
                          mstore(calldatasize(), shl(96, caller()))
                          // Add 20 bytes for the address appended add the end
                          let success := call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)
                          returndatacopy(0, 0, returndatasize())
                          if iszero(success) {
                              revert(0, returndatasize())
                          }
                          return(0, returndatasize())
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/Enum.sol";
              import "../common/SelfAuthorized.sol";
              interface Guard {
                  function checkTransaction(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Enum.Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address payable refundReceiver,
                      bytes memory signatures,
                      address msgSender
                  ) external;
                  function checkAfterExecution(bytes32 txHash, bool success) external;
              }
              /// @title Fallback Manager - A contract that manages fallback calls made to this contract
              /// @author Richard Meissner - <[email protected]>
              contract GuardManager is SelfAuthorized {
                  event ChangedGuard(address guard);
                  // keccak256("guard_manager.guard.address")
                  bytes32 internal constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;
                  /// @dev Set a guard that checks transactions before execution
                  /// @param guard The address of the guard to be used or the 0 address to disable the guard
                  function setGuard(address guard) external authorized {
                      bytes32 slot = GUARD_STORAGE_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(slot, guard)
                      }
                      emit ChangedGuard(guard);
                  }
                  function getGuard() internal view returns (address guard) {
                      bytes32 slot = GUARD_STORAGE_SLOT;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          guard := sload(slot)
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/Enum.sol";
              import "../common/SelfAuthorized.sol";
              import "./Executor.sol";
              /// @title Module Manager - A contract that manages modules that can execute transactions via this contract
              /// @author Stefan George - <[email protected]>
              /// @author Richard Meissner - <[email protected]>
              contract ModuleManager is SelfAuthorized, Executor {
                  event EnabledModule(address module);
                  event DisabledModule(address module);
                  event ExecutionFromModuleSuccess(address indexed module);
                  event ExecutionFromModuleFailure(address indexed module);
                  address internal constant SENTINEL_MODULES = address(0x1);
                  mapping(address => address) internal modules;
                  function setupModules(address to, bytes memory data) internal {
                      require(modules[SENTINEL_MODULES] == address(0), "GS100");
                      modules[SENTINEL_MODULES] = SENTINEL_MODULES;
                      if (to != address(0))
                          // Setup has to complete successfully or transaction fails.
                          require(execute(to, 0, data, Enum.Operation.DelegateCall, gasleft()), "GS000");
                  }
                  /// @dev Allows to add a module to the whitelist.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Enables the module `module` for the Safe.
                  /// @param module Module to be whitelisted.
                  function enableModule(address module) public authorized {
                      // Module address cannot be null or sentinel.
                      require(module != address(0) && module != SENTINEL_MODULES, "GS101");
                      // Module cannot be added twice.
                      require(modules[module] == address(0), "GS102");
                      modules[module] = modules[SENTINEL_MODULES];
                      modules[SENTINEL_MODULES] = module;
                      emit EnabledModule(module);
                  }
                  /// @dev Allows to remove a module from the whitelist.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Disables the module `module` for the Safe.
                  /// @param prevModule Module that pointed to the module to be removed in the linked list
                  /// @param module Module to be removed.
                  function disableModule(address prevModule, address module) public authorized {
                      // Validate module address and check that it corresponds to module index.
                      require(module != address(0) && module != SENTINEL_MODULES, "GS101");
                      require(modules[prevModule] == module, "GS103");
                      modules[prevModule] = modules[module];
                      modules[module] = address(0);
                      emit DisabledModule(module);
                  }
                  /// @dev Allows a Module to execute a Safe transaction without any further confirmations.
                  /// @param to Destination address of module transaction.
                  /// @param value Ether value of module transaction.
                  /// @param data Data payload of module transaction.
                  /// @param operation Operation type of module transaction.
                  function execTransactionFromModule(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Enum.Operation operation
                  ) public virtual returns (bool success) {
                      // Only whitelisted modules are allowed.
                      require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "GS104");
                      // Execute transaction without further confirmations.
                      success = execute(to, value, data, operation, gasleft());
                      if (success) emit ExecutionFromModuleSuccess(msg.sender);
                      else emit ExecutionFromModuleFailure(msg.sender);
                  }
                  /// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data
                  /// @param to Destination address of module transaction.
                  /// @param value Ether value of module transaction.
                  /// @param data Data payload of module transaction.
                  /// @param operation Operation type of module transaction.
                  function execTransactionFromModuleReturnData(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Enum.Operation operation
                  ) public returns (bool success, bytes memory returnData) {
                      success = execTransactionFromModule(to, value, data, operation);
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // Load free memory location
                          let ptr := mload(0x40)
                          // We allocate memory for the return data by setting the free memory location to
                          // current free memory location + data size + 32 bytes for data size value
                          mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
                          // Store the size
                          mstore(ptr, returndatasize())
                          // Store the data
                          returndatacopy(add(ptr, 0x20), 0, returndatasize())
                          // Point the return data to the correct memory location
                          returnData := ptr
                      }
                  }
                  /// @dev Returns if an module is enabled
                  /// @return True if the module is enabled
                  function isModuleEnabled(address module) public view returns (bool) {
                      return SENTINEL_MODULES != module && modules[module] != address(0);
                  }
                  /// @dev Returns array of modules.
                  /// @param start Start of the page.
                  /// @param pageSize Maximum number of modules that should be returned.
                  /// @return array Array of modules.
                  /// @return next Start of the next page.
                  function getModulesPaginated(address start, uint256 pageSize) external view returns (address[] memory array, address next) {
                      // Init array with max page size
                      array = new address[](pageSize);
                      // Populate return array
                      uint256 moduleCount = 0;
                      address currentModule = modules[start];
                      while (currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) {
                          array[moduleCount] = currentModule;
                          currentModule = modules[currentModule];
                          moduleCount++;
                      }
                      next = currentModule;
                      // Set correct size of returned array
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          mstore(array, moduleCount)
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              import "../common/SelfAuthorized.sol";
              /// @title OwnerManager - Manages a set of owners and a threshold to perform actions.
              /// @author Stefan George - <[email protected]>
              /// @author Richard Meissner - <[email protected]>
              contract OwnerManager is SelfAuthorized {
                  event AddedOwner(address owner);
                  event RemovedOwner(address owner);
                  event ChangedThreshold(uint256 threshold);
                  address internal constant SENTINEL_OWNERS = address(0x1);
                  mapping(address => address) internal owners;
                  uint256 internal ownerCount;
                  uint256 internal threshold;
                  /// @dev Setup function sets initial storage of contract.
                  /// @param _owners List of Safe owners.
                  /// @param _threshold Number of required confirmations for a Safe transaction.
                  function setupOwners(address[] memory _owners, uint256 _threshold) internal {
                      // Threshold can only be 0 at initialization.
                      // Check ensures that setup function can only be called once.
                      require(threshold == 0, "GS200");
                      // Validate that threshold is smaller than number of added owners.
                      require(_threshold <= _owners.length, "GS201");
                      // There has to be at least one Safe owner.
                      require(_threshold >= 1, "GS202");
                      // Initializing Safe owners.
                      address currentOwner = SENTINEL_OWNERS;
                      for (uint256 i = 0; i < _owners.length; i++) {
                          // Owner address cannot be null.
                          address owner = _owners[i];
                          require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this) && currentOwner != owner, "GS203");
                          // No duplicate owners allowed.
                          require(owners[owner] == address(0), "GS204");
                          owners[currentOwner] = owner;
                          currentOwner = owner;
                      }
                      owners[currentOwner] = SENTINEL_OWNERS;
                      ownerCount = _owners.length;
                      threshold = _threshold;
                  }
                  /// @dev Allows to add a new owner to the Safe and update the threshold at the same time.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Adds the owner `owner` to the Safe and updates the threshold to `_threshold`.
                  /// @param owner New owner address.
                  /// @param _threshold New threshold.
                  function addOwnerWithThreshold(address owner, uint256 _threshold) public authorized {
                      // Owner address cannot be null, the sentinel or the Safe itself.
                      require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this), "GS203");
                      // No duplicate owners allowed.
                      require(owners[owner] == address(0), "GS204");
                      owners[owner] = owners[SENTINEL_OWNERS];
                      owners[SENTINEL_OWNERS] = owner;
                      ownerCount++;
                      emit AddedOwner(owner);
                      // Change threshold if threshold was changed.
                      if (threshold != _threshold) changeThreshold(_threshold);
                  }
                  /// @dev Allows to remove an owner from the Safe and update the threshold at the same time.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Removes the owner `owner` from the Safe and updates the threshold to `_threshold`.
                  /// @param prevOwner Owner that pointed to the owner to be removed in the linked list
                  /// @param owner Owner address to be removed.
                  /// @param _threshold New threshold.
                  function removeOwner(
                      address prevOwner,
                      address owner,
                      uint256 _threshold
                  ) public authorized {
                      // Only allow to remove an owner, if threshold can still be reached.
                      require(ownerCount - 1 >= _threshold, "GS201");
                      // Validate owner address and check that it corresponds to owner index.
                      require(owner != address(0) && owner != SENTINEL_OWNERS, "GS203");
                      require(owners[prevOwner] == owner, "GS205");
                      owners[prevOwner] = owners[owner];
                      owners[owner] = address(0);
                      ownerCount--;
                      emit RemovedOwner(owner);
                      // Change threshold if threshold was changed.
                      if (threshold != _threshold) changeThreshold(_threshold);
                  }
                  /// @dev Allows to swap/replace an owner from the Safe with another address.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Replaces the owner `oldOwner` in the Safe with `newOwner`.
                  /// @param prevOwner Owner that pointed to the owner to be replaced in the linked list
                  /// @param oldOwner Owner address to be replaced.
                  /// @param newOwner New owner address.
                  function swapOwner(
                      address prevOwner,
                      address oldOwner,
                      address newOwner
                  ) public authorized {
                      // Owner address cannot be null, the sentinel or the Safe itself.
                      require(newOwner != address(0) && newOwner != SENTINEL_OWNERS && newOwner != address(this), "GS203");
                      // No duplicate owners allowed.
                      require(owners[newOwner] == address(0), "GS204");
                      // Validate oldOwner address and check that it corresponds to owner index.
                      require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "GS203");
                      require(owners[prevOwner] == oldOwner, "GS205");
                      owners[newOwner] = owners[oldOwner];
                      owners[prevOwner] = newOwner;
                      owners[oldOwner] = address(0);
                      emit RemovedOwner(oldOwner);
                      emit AddedOwner(newOwner);
                  }
                  /// @dev Allows to update the number of required confirmations by Safe owners.
                  ///      This can only be done via a Safe transaction.
                  /// @notice Changes the threshold of the Safe to `_threshold`.
                  /// @param _threshold New threshold.
                  function changeThreshold(uint256 _threshold) public authorized {
                      // Validate that threshold is smaller than number of owners.
                      require(_threshold <= ownerCount, "GS201");
                      // There has to be at least one Safe owner.
                      require(_threshold >= 1, "GS202");
                      threshold = _threshold;
                      emit ChangedThreshold(threshold);
                  }
                  function getThreshold() public view returns (uint256) {
                      return threshold;
                  }
                  function isOwner(address owner) public view returns (bool) {
                      return owner != SENTINEL_OWNERS && owners[owner] != address(0);
                  }
                  /// @dev Returns array of owners.
                  /// @return Array of Safe owners.
                  function getOwners() public view returns (address[] memory) {
                      address[] memory array = new address[](ownerCount);
                      // populate return array
                      uint256 index = 0;
                      address currentOwner = owners[SENTINEL_OWNERS];
                      while (currentOwner != SENTINEL_OWNERS) {
                          array[index] = currentOwner;
                          currentOwner = owners[currentOwner];
                          index++;
                      }
                      return array;
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title Enum - Collection of enums
              /// @author Richard Meissner - <[email protected]>
              contract Enum {
                  enum Operation {Call, DelegateCall}
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title EtherPaymentFallback - A contract that has a fallback to accept ether payments
              /// @author Richard Meissner - <[email protected]>
              contract EtherPaymentFallback {
                  event SafeReceived(address indexed sender, uint256 value);
                  /// @dev Fallback function accepts Ether transactions.
                  receive() external payable {
                      emit SafeReceived(msg.sender, msg.value);
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title SecuredTokenTransfer - Secure token transfer
              /// @author Richard Meissner - <[email protected]>
              contract SecuredTokenTransfer {
                  /// @dev Transfers a token and returns if it was a success
                  /// @param token Token that should be transferred
                  /// @param receiver Receiver to whom the token should be transferred
                  /// @param amount The amount of tokens that should be transferred
                  function transferToken(
                      address token,
                      address receiver,
                      uint256 amount
                  ) internal returns (bool transferred) {
                      // 0xa9059cbb - keccack("transfer(address,uint256)")
                      bytes memory data = abi.encodeWithSelector(0xa9059cbb, receiver, amount);
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          // We write the return value to scratch space.
                          // See https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory
                          let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0x20)
                          switch returndatasize()
                              case 0 {
                                  transferred := success
                              }
                              case 0x20 {
                                  transferred := iszero(or(iszero(success), iszero(mload(0))))
                              }
                              default {
                                  transferred := 0
                              }
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title SelfAuthorized - authorizes current contract to perform actions
              /// @author Richard Meissner - <[email protected]>
              contract SelfAuthorized {
                  function requireSelfCall() private view {
                      require(msg.sender == address(this), "GS031");
                  }
                  modifier authorized() {
                      // This is a function call as it minimized the bytecode size
                      requireSelfCall();
                      _;
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title SignatureDecoder - Decodes signatures that a encoded as bytes
              /// @author Richard Meissner - <[email protected]>
              contract SignatureDecoder {
                  /// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`.
                  /// @notice Make sure to peform a bounds check for @param pos, to avoid out of bounds access on @param signatures
                  /// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access
                  /// @param signatures concatenated rsv signatures
                  function signatureSplit(bytes memory signatures, uint256 pos)
                      internal
                      pure
                      returns (
                          uint8 v,
                          bytes32 r,
                          bytes32 s
                      )
                  {
                      // The signature format is a compact form of:
                      //   {bytes32 r}{bytes32 s}{uint8 v}
                      // Compact means, uint8 is not padded to 32 bytes.
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let signaturePos := mul(0x41, pos)
                          r := mload(add(signatures, add(signaturePos, 0x20)))
                          s := mload(add(signatures, add(signaturePos, 0x40)))
                          // Here we are loading the last 32 bytes, including 31 bytes
                          // of 's'. There is no 'mload8' to do this.
                          //
                          // 'byte' is not working due to the Solidity parser, so lets
                          // use the second best option, 'and'
                          v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title Singleton - Base for singleton contracts (should always be first super contract)
              ///         This contract is tightly coupled to our proxy contract (see `proxies/GnosisSafeProxy.sol`)
              /// @author Richard Meissner - <[email protected]>
              contract Singleton {
                  // singleton always needs to be first declared variable, to ensure that it is at the same location as in the Proxy contract.
                  // It should also always be ensured that the address is stored alone (uses a full word)
                  address private singleton;
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /// @title StorageAccessible - generic base contract that allows callers to access all internal storage.
              /// @notice See https://github.com/gnosis/util-contracts/blob/bb5fe5fb5df6d8400998094fb1b32a178a47c3a1/contracts/StorageAccessible.sol
              contract StorageAccessible {
                  /**
                   * @dev Reads `length` bytes of storage in the currents contract
                   * @param offset - the offset in the current contract's storage in words to start reading from
                   * @param length - the number of words (32 bytes) of data to read
                   * @return the bytes that were read.
                   */
                  function getStorageAt(uint256 offset, uint256 length) public view returns (bytes memory) {
                      bytes memory result = new bytes(length * 32);
                      for (uint256 index = 0; index < length; index++) {
                          // solhint-disable-next-line no-inline-assembly
                          assembly {
                              let word := sload(add(offset, index))
                              mstore(add(add(result, 0x20), mul(index, 0x20)), word)
                          }
                      }
                      return result;
                  }
                  /**
                   * @dev Performs a delegetecall on a targetContract in the context of self.
                   * Internally reverts execution to avoid side effects (making it static).
                   *
                   * This method reverts with data equal to `abi.encode(bool(success), bytes(response))`.
                   * Specifically, the `returndata` after a call to this method will be:
                   * `success:bool || response.length:uint256 || response:bytes`.
                   *
                   * @param targetContract Address of the contract containing the code to execute.
                   * @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
                   */
                  function simulateAndRevert(address targetContract, bytes memory calldataPayload) external {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          let success := delegatecall(gas(), targetContract, add(calldataPayload, 0x20), mload(calldataPayload), 0, 0)
                          mstore(0x00, success)
                          mstore(0x20, returndatasize())
                          returndatacopy(0x40, 0, returndatasize())
                          revert(0, add(returndatasize(), 0x40))
                      }
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              /**
               * @title GnosisSafeMath
               * @dev Math operations with safety checks that revert on error
               * Renamed from SafeMath to GnosisSafeMath to avoid conflicts
               * TODO: remove once open zeppelin update to solc 0.5.0
               */
              library GnosisSafeMath {
                  /**
                   * @dev Multiplies two numbers, reverts on 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-solidity/pull/522
                      if (a == 0) {
                          return 0;
                      }
                      uint256 c = a * b;
                      require(c / a == b);
                      return c;
                  }
                  /**
                   * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
                   */
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a);
                      uint256 c = a - b;
                      return c;
                  }
                  /**
                   * @dev Adds two numbers, reverts on overflow.
                   */
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a);
                      return c;
                  }
                  /**
                   * @dev Returns the largest of two numbers.
                   */
                  function max(uint256 a, uint256 b) internal pure returns (uint256) {
                      return a >= b ? a : b;
                  }
              }
              // SPDX-License-Identifier: LGPL-3.0-only
              pragma solidity >=0.7.0 <0.9.0;
              contract ISignatureValidatorConstants {
                  // bytes4(keccak256("isValidSignature(bytes,bytes)")
                  bytes4 internal constant EIP1271_MAGIC_VALUE = 0x20c13b0b;
              }
              abstract contract ISignatureValidator is ISignatureValidatorConstants {
                  /**
                   * @dev Should return whether the signature provided is valid for the provided data
                   * @param _data Arbitrary length data signed on the behalf of address(this)
                   * @param _signature Signature byte array associated with _data
                   *
                   * MUST return the bytes4 magic value 0x20c13b0b when function passes.
                   * MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
                   * MUST allow external calls
                   */
                  function isValidSignature(bytes memory _data, bytes memory _signature) public view virtual returns (bytes4);
              }
              

              File 6 of 7: CompV3Supply
              // SPDX-License-Identifier: MIT
              pragma solidity =0.8.10;
              
              
              
              
              
              
              
              
              
              
              contract MainnetActionsUtilAddresses {
                  address internal constant DFS_REG_CONTROLLER_ADDR = 0xF8f8B3C98Cf2E63Df3041b73f80F362a4cf3A576;
                  address internal constant REGISTRY_ADDR = 0x287778F121F134C66212FB16c9b53eC991D32f5b;
                  address internal constant DFS_LOGGER_ADDR = 0xcE7a977Cac4a481bc84AC06b2Da0df614e621cf3;
                  address internal constant SUB_STORAGE_ADDR = 0x1612fc28Ee0AB882eC99842Cde0Fc77ff0691e90;
                  address internal constant PROXY_AUTH_ADDR = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70;
                  address internal constant LSV_PROXY_REGISTRY_ADDRESS = 0xa8a3c86c4A2DcCf350E84D2b3c46BDeBc711C16e;
                  address internal constant TRANSIENT_STORAGE = 0x2F7Ef2ea5E8c97B8687CA703A0e50Aa5a49B7eb2;
              }
              
              
              
              
              
              
              
              contract ActionsUtilHelper is MainnetActionsUtilAddresses {
              }
              
              
              
              
              
              
              
              contract MainnetAuthAddresses {
                  address internal constant ADMIN_VAULT_ADDR = 0xCCf3d848e08b94478Ed8f46fFead3008faF581fD;
                  address internal constant DSGUARD_FACTORY_ADDRESS = 0x5a15566417e6C1c9546523066500bDDBc53F88C7;
                  address internal constant ADMIN_ADDR = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9; // USED IN ADMIN VAULT CONSTRUCTOR
                  address internal constant PROXY_AUTH_ADDRESS = 0x149667b6FAe2c63D1B4317C716b0D0e4d3E2bD70;
                  address internal constant MODULE_AUTH_ADDRESS = 0x7407974DDBF539e552F1d051e44573090912CC3D;
              }
              
              
              
              
              
              
              
              contract AuthHelper is MainnetAuthAddresses {
              }
              
              
              
              
              
              
              
              
              contract AdminVault is AuthHelper {
                  address public owner;
                  address public admin;
              
                  error SenderNotAdmin();
              
                  constructor() {
                      owner = msg.sender;
                      admin = ADMIN_ADDR;
                  }
              
                  /// @notice Admin is able to change owner
                  /// @param _owner Address of new owner
                  function changeOwner(address _owner) public {
                      if (admin != msg.sender){
                          revert SenderNotAdmin();
                      }
                      owner = _owner;
                  }
              
                  /// @notice Admin is able to set new admin
                  /// @param _admin Address of multisig that becomes new admin
                  function changeAdmin(address _admin) public {
                      if (admin != msg.sender){
                          revert SenderNotAdmin();
                      }
                      admin = _admin;
                  }
              
              }
              
              
              
              
              
              
              
              interface IERC20 {
                  function name() external view returns (string memory);
                  function symbol() external view returns (string memory);
                  function decimals() external view returns (uint256 digits);
                  function totalSupply() external view returns (uint256 supply);
              
                  function balanceOf(address _owner) external view returns (uint256 balance);
              
                  function transfer(address _to, uint256 _value) external returns (bool success);
              
                  function transferFrom(
                      address _from,
                      address _to,
                      uint256 _value
                  ) external returns (bool success);
              
                  function approve(address _spender, uint256 _value) external returns (bool success);
              
                  function allowance(address _owner, address _spender) external view returns (uint256 remaining);
              
                  event Approval(address indexed _owner, address indexed _spender, uint256 _value);
              }
              
              
              
              
              
              
              
              library Address {
                  //insufficient balance
                  error InsufficientBalance(uint256 available, uint256 required);
                  //unable to send value, recipient may have reverted
                  error SendingValueFail();
                  //insufficient balance for call
                  error InsufficientBalanceForCall(uint256 available, uint256 required);
                  //call to non-contract
                  error NonContractCall();
                  
                  function isContract(address account) internal view returns (bool) {
                      // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                      // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                      // for accounts without code, i.e. `keccak256('')`
                      bytes32 codehash;
                      bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          codehash := extcodehash(account)
                      }
                      return (codehash != accountHash && codehash != 0x0);
                  }
              
                  function sendValue(address payable recipient, uint256 amount) internal {
                      uint256 balance = address(this).balance;
                      if (balance < amount){
                          revert InsufficientBalance(balance, amount);
                      }
              
                      // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                      (bool success, ) = recipient.call{value: amount}("");
                      if (!(success)){
                          revert SendingValueFail();
                      }
                  }
              
                  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) {
                      uint256 balance = address(this).balance;
                      if (balance < value){
                          revert InsufficientBalanceForCall(balance, value);
                      }
                      return _functionCallWithValue(target, data, value, errorMessage);
                  }
              
                  function _functionCallWithValue(
                      address target,
                      bytes memory data,
                      uint256 weiValue,
                      string memory errorMessage
                  ) private returns (bytes memory) {
                      if (!(isContract(target))){
                          revert NonContractCall();
                      }
              
                      // 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);
                          }
                      }
                  }
              }
              
              
              
              
              
              
              
              
              
              
              
              library SafeERC20 {
                  using Address for address;
              
                  /**
                   * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful.
                   */
                  function safeTransfer(IERC20 token, address to, uint256 value) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                  }
              
                  /**
                   * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
                   * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
                   */
                  function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                      _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                  }
              
                  /**
                   * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
                   * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to
                   * 0 before setting it to a non-zero value.
                   */
                  function safeApprove(IERC20 token, address spender, uint256 value) internal {
                      bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
              
                      if (!_callOptionalReturnBool(token, approvalCall)) {
                          _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                          _callOptionalReturn(token, approvalCall);
                      }
                  }
              
                  /**
                   * @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");
                      require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                  }
              
                  /**
                   * @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).
                   *
                   * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
                   */
                  function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
                      // 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 cannot use {Address-functionCall} here since this should return false
                      // and not revert is the subcall reverts.
              
                      (bool success, bytes memory returndata) = address(token).call(data);
                      return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
                  }
              }
              
              
              
              
              
              
              
              
              
              
              contract AdminAuth is AuthHelper {
                  using SafeERC20 for IERC20;
              
                  AdminVault public constant adminVault = AdminVault(ADMIN_VAULT_ADDR);
              
                  error SenderNotOwner();
                  error SenderNotAdmin();
              
                  modifier onlyOwner() {
                      if (adminVault.owner() != msg.sender){
                          revert SenderNotOwner();
                      }
                      _;
                  }
              
                  modifier onlyAdmin() {
                      if (adminVault.admin() != msg.sender){
                          revert SenderNotAdmin();
                      }
                      _;
                  }
              
                  /// @notice withdraw stuck funds
                  function withdrawStuckFunds(address _token, address _receiver, uint256 _amount) public onlyOwner {
                      if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
                          payable(_receiver).transfer(_amount);
                      } else {
                          IERC20(_token).safeTransfer(_receiver, _amount);
                      }
                  }
              
                  /// @notice Destroy the contract
                  /// @dev Deprecated method, selfdestruct will soon just send eth
                  function kill() public onlyAdmin {
                      selfdestruct(payable(msg.sender));
                  }
              }
              
              
              
              
              
              
              
              
              contract DFSRegistry is AdminAuth {
                  error EntryAlreadyExistsError(bytes4);
                  error EntryNonExistentError(bytes4);
                  error EntryNotInChangeError(bytes4);
                  error ChangeNotReadyError(uint256,uint256);
                  error EmptyPrevAddrError(bytes4);
                  error AlreadyInContractChangeError(bytes4);
                  error AlreadyInWaitPeriodChangeError(bytes4);
              
                  event AddNewContract(address,bytes4,address,uint256);
                  event RevertToPreviousAddress(address,bytes4,address,address);
                  event StartContractChange(address,bytes4,address,address);
                  event ApproveContractChange(address,bytes4,address,address);
                  event CancelContractChange(address,bytes4,address,address);
                  event StartWaitPeriodChange(address,bytes4,uint256);
                  event ApproveWaitPeriodChange(address,bytes4,uint256,uint256);
                  event CancelWaitPeriodChange(address,bytes4,uint256,uint256);
              
                  struct Entry {
                      address contractAddr;
                      uint256 waitPeriod;
                      uint256 changeStartTime;
                      bool inContractChange;
                      bool inWaitPeriodChange;
                      bool exists;
                  }
              
                  mapping(bytes4 => Entry) public entries;
                  mapping(bytes4 => address) public previousAddresses;
              
                  mapping(bytes4 => address) public pendingAddresses;
                  mapping(bytes4 => uint256) public pendingWaitTimes;
              
                  /// @notice Given an contract id returns the registered address
                  /// @dev Id is keccak256 of the contract name
                  /// @param _id Id of contract
                  function getAddr(bytes4 _id) public view returns (address) {
                      return entries[_id].contractAddr;
                  }
              
                  /// @notice Helper function to easily query if id is registered
                  /// @param _id Id of contract
                  function isRegistered(bytes4 _id) public view returns (bool) {
                      return entries[_id].exists;
                  }
              
                  /////////////////////////// OWNER ONLY FUNCTIONS ///////////////////////////
              
                  /// @notice Adds a new contract to the registry
                  /// @param _id Id of contract
                  /// @param _contractAddr Address of the contract
                  /// @param _waitPeriod Amount of time to wait before a contract address can be changed
                  function addNewContract(
                      bytes4 _id,
                      address _contractAddr,
                      uint256 _waitPeriod
                  ) public onlyOwner {
                      if (entries[_id].exists){
                          revert EntryAlreadyExistsError(_id);
                      }
              
                      entries[_id] = Entry({
                          contractAddr: _contractAddr,
                          waitPeriod: _waitPeriod,
                          changeStartTime: 0,
                          inContractChange: false,
                          inWaitPeriodChange: false,
                          exists: true
                      });
              
                      emit AddNewContract(msg.sender, _id, _contractAddr, _waitPeriod);
                  }
              
                  /// @notice Reverts to the previous address immediately
                  /// @dev In case the new version has a fault, a quick way to fallback to the old contract
                  /// @param _id Id of contract
                  function revertToPreviousAddress(bytes4 _id) public onlyOwner {
                      if (!(entries[_id].exists)){
                          revert EntryNonExistentError(_id);
                      }
                      if (previousAddresses[_id] == address(0)){
                          revert EmptyPrevAddrError(_id);
                      }
              
                      address currentAddr = entries[_id].contractAddr;
                      entries[_id].contractAddr = previousAddresses[_id];
              
                      emit RevertToPreviousAddress(msg.sender, _id, currentAddr, previousAddresses[_id]);
                  }
              
                  /// @notice Starts an address change for an existing entry
                  /// @dev Can override a change that is currently in progress
                  /// @param _id Id of contract
                  /// @param _newContractAddr Address of the new contract
                  function startContractChange(bytes4 _id, address _newContractAddr) public onlyOwner {
                      if (!entries[_id].exists){
                          revert EntryNonExistentError(_id);
                      }
                      if (entries[_id].inWaitPeriodChange){
                          revert AlreadyInWaitPeriodChangeError(_id);
                      }
              
                      entries[_id].changeStartTime = block.timestamp; // solhint-disable-line
                      entries[_id].inContractChange = true;
              
                      pendingAddresses[_id] = _newContractAddr;
              
                      emit StartContractChange(msg.sender, _id, entries[_id].contractAddr, _newContractAddr);
                  }
              
                  /// @notice Changes new contract address, correct time must have passed
                  /// @param _id Id of contract
                  function approveContractChange(bytes4 _id) public onlyOwner {
                      if (!entries[_id].exists){
                          revert EntryNonExistentError(_id);
                      }
                      if (!entries[_id].inContractChange){
                          revert EntryNotInChangeError(_id);
                      }
                      if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){// solhint-disable-line
                          revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod));
                      }
              
                      address oldContractAddr = entries[_id].contractAddr;
                      entries[_id].contractAddr = pendingAddresses[_id];
                      entries[_id].inContractChange = false;
                      entries[_id].changeStartTime = 0;
              
                      pendingAddresses[_id] = address(0);
                      previousAddresses[_id] = oldContractAddr;
              
                      emit ApproveContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr);
                  }
              
                  /// @notice Cancel pending change
                  /// @param _id Id of contract
                  function cancelContractChange(bytes4 _id) public onlyOwner {
                      if (!entries[_id].exists){
                          revert EntryNonExistentError(_id);
                      }
                      if (!entries[_id].inContractChange){
                          revert EntryNotInChangeError(_id);
                      }
              
                      address oldContractAddr = pendingAddresses[_id];
              
                      pendingAddresses[_id] = address(0);
                      entries[_id].inContractChange = false;
                      entries[_id].changeStartTime = 0;
              
                      emit CancelContractChange(msg.sender, _id, oldContractAddr, entries[_id].contractAddr);
                  }
              
                  /// @notice Starts the change for waitPeriod
                  /// @param _id Id of contract
                  /// @param _newWaitPeriod New wait time
                  function startWaitPeriodChange(bytes4 _id, uint256 _newWaitPeriod) public onlyOwner {
                      if (!entries[_id].exists){
                          revert EntryNonExistentError(_id);
                      }
                      if (entries[_id].inContractChange){
                          revert AlreadyInContractChangeError(_id);
                      }
              
                      pendingWaitTimes[_id] = _newWaitPeriod;
              
                      entries[_id].changeStartTime = block.timestamp; // solhint-disable-line
                      entries[_id].inWaitPeriodChange = true;
              
                      emit StartWaitPeriodChange(msg.sender, _id, _newWaitPeriod);
                  }
              
                  /// @notice Changes new wait period, correct time must have passed
                  /// @param _id Id of contract
                  function approveWaitPeriodChange(bytes4 _id) public onlyOwner {
                      if (!entries[_id].exists){
                          revert EntryNonExistentError(_id);
                      }
                      if (!entries[_id].inWaitPeriodChange){
                          revert EntryNotInChangeError(_id);
                      }
                      if (block.timestamp < (entries[_id].changeStartTime + entries[_id].waitPeriod)){ // solhint-disable-line
                          revert ChangeNotReadyError(block.timestamp, (entries[_id].changeStartTime + entries[_id].waitPeriod));
                      }
              
                      uint256 oldWaitTime = entries[_id].waitPeriod;
                      entries[_id].waitPeriod = pendingWaitTimes[_id];
                      
                      entries[_id].inWaitPeriodChange = false;
                      entries[_id].changeStartTime = 0;
              
                      pendingWaitTimes[_id] = 0;
              
                      emit ApproveWaitPeriodChange(msg.sender, _id, oldWaitTime, entries[_id].waitPeriod);
                  }
              
                  /// @notice Cancel wait period change
                  /// @param _id Id of contract
                  function cancelWaitPeriodChange(bytes4 _id) public onlyOwner {
                      if (!entries[_id].exists){
                          revert EntryNonExistentError(_id);
                      }
                      if (!entries[_id].inWaitPeriodChange){
                          revert EntryNotInChangeError(_id);
                      }
              
                      uint256 oldWaitPeriod = pendingWaitTimes[_id];
              
                      pendingWaitTimes[_id] = 0;
                      entries[_id].inWaitPeriodChange = false;
                      entries[_id].changeStartTime = 0;
              
                      emit CancelWaitPeriodChange(msg.sender, _id, oldWaitPeriod, entries[_id].waitPeriod);
                  }
              }
              
              
              
              
              
              
              
              abstract contract DSAuthority {
                  function canCall(
                      address src,
                      address dst,
                      bytes4 sig
                  ) public view virtual returns (bool);
              }
              
              
              
              
              
              
              
              contract DSAuthEvents {
                  event LogSetAuthority(address indexed authority);
                  event LogSetOwner(address indexed owner);
              }
              
              contract DSAuth is DSAuthEvents {
                  DSAuthority public authority;
                  address public owner;
              
                  constructor() {
                      owner = msg.sender;
                      emit LogSetOwner(msg.sender);
                  }
              
                  function setOwner(address owner_) public auth {
                      owner = owner_;
                      emit LogSetOwner(owner);
                  }
              
                  function setAuthority(DSAuthority authority_) public auth {
                      authority = authority_;
                      emit LogSetAuthority(address(authority));
                  }
              
                  modifier auth {
                      require(isAuthorized(msg.sender, msg.sig), "Not authorized");
                      _;
                  }
              
                  function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
                      if (src == address(this)) {
                          return true;
                      } else if (src == owner) {
                          return true;
                      } else if (authority == DSAuthority(address(0))) {
                          return false;
                      } else {
                          return authority.canCall(src, address(this), sig);
                      }
                  }
              }
              
              
              
              
              
              
              
              contract DSNote {
                  event LogNote(
                      bytes4 indexed sig,
                      address indexed guy,
                      bytes32 indexed foo,
                      bytes32 indexed bar,
                      uint256 wad,
                      bytes fax
                  ) anonymous;
              
                  modifier note {
                      bytes32 foo;
                      bytes32 bar;
              
                      assembly {
                          foo := calldataload(4)
                          bar := calldataload(36)
                      }
              
                      emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
              
                      _;
                  }
              }
              
              
              
              
              
              
              
              
              abstract contract DSProxy is DSAuth, DSNote {
                  DSProxyCache public cache; // global cache for contracts
              
                  constructor(address _cacheAddr) {
                      if (!(setCache(_cacheAddr))){
                          require(isAuthorized(msg.sender, msg.sig), "Not authorized");
                      }
                  }
              
                  // solhint-disable-next-line no-empty-blocks
                  receive() external payable {}
              
                  // use the proxy to execute calldata _data on contract _code
                  function execute(bytes memory _code, bytes memory _data)
                      public
                      payable
                      virtual
                      returns (address target, bytes32 response);
              
                  function execute(address _target, bytes memory _data)
                      public
                      payable
                      virtual
                      returns (bytes32 response);
              
                  //set new cache
                  function setCache(address _cacheAddr) public payable virtual returns (bool);
              }
              
              contract DSProxyCache {
                  mapping(bytes32 => address) cache;
              
                  function read(bytes memory _code) public view returns (address) {
                      bytes32 hash = keccak256(_code);
                      return cache[hash];
                  }
              
                  function write(bytes memory _code) public returns (address target) {
                      assembly {
                          target := create(0, add(_code, 0x20), mload(_code))
                          switch iszero(extcodesize(target))
                              case 1 {
                                  // throw if contract failed to deploy
                                  revert(0, 0)
                              }
                      }
                      bytes32 hash = keccak256(_code);
                      cache[hash] = target;
                  }
              }
              
              
              
              
              
              
              
              interface ISafe {
                  enum Operation {
                      Call,
                      DelegateCall
                  }
              
                  function setup(
                      address[] calldata _owners,
                      uint256 _threshold,
                      address to,
                      bytes calldata data,
                      address fallbackHandler,
                      address paymentToken,
                      uint256 payment,
                      address payable paymentReceiver
                  ) external;
              
                  function execTransaction(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address payable refundReceiver,
                      bytes memory signatures
                  ) external payable returns (bool success);
              
                  function execTransactionFromModule(
                      address to,
                      uint256 value,
                      bytes memory data,
                      Operation operation
                  ) external returns (bool success);
              
                  function checkSignatures(
                      bytes32 dataHash,
                      bytes memory data,
                      bytes memory signatures
                  ) external view;
              
                  function checkNSignatures(
                      address executor,
                      bytes32 dataHash,
                      bytes memory /* data */,
                      bytes memory signatures,
                      uint256 requiredSignatures
                  ) external view;
              
                  function approveHash(bytes32 hashToApprove) external;
              
                  function domainSeparator() external view returns (bytes32);
              
                  function getTransactionHash(
                      address to,
                      uint256 value,
                      bytes calldata data,
                      Operation operation,
                      uint256 safeTxGas,
                      uint256 baseGas,
                      uint256 gasPrice,
                      address gasToken,
                      address refundReceiver,
                      uint256 _nonce
                  ) external view returns (bytes32);
              
                  function nonce() external view returns (uint256);
              
                  function setFallbackHandler(address handler) external;
              
                  function getOwners() external view returns (address[] memory);
              
                  function isOwner(address owner) external view returns (bool);
              
                  function getThreshold() external view returns (uint256);
              
                  function enableModule(address module) external;
              
                  function isModuleEnabled(address module) external view returns (bool);
              
                  function disableModule(address prevModule, address module) external;
              
                  function getModulesPaginated(
                      address start,
                      uint256 pageSize
                  ) external view returns (address[] memory array, address next);
              }
              
              
              
              
              
              
              
              interface IDSProxyFactory {
                  function isProxy(address _proxy) external view returns (bool);
              }
              
              
              
              
              
              
              
              contract MainnetProxyFactoryAddresses {
                  address internal constant PROXY_FACTORY_ADDR = 0xA26e15C895EFc0616177B7c1e7270A4C7D51C997;
              }
              
              
              
              
              
              
              
              contract DSProxyFactoryHelper is MainnetProxyFactoryAddresses {
              }
              
              
              
              
              
              
              
              
              
              contract CheckWalletType is DSProxyFactoryHelper {
                  function isDSProxy(address _proxy) public view returns (bool) {
                      return IDSProxyFactory(PROXY_FACTORY_ADDR).isProxy(_proxy);
                  }
              }
              
              
              
              
              
              
              
              contract DefisaverLogger {
                  event RecipeEvent(
                      address indexed caller,
                      string indexed logName
                  );
              
                  event ActionDirectEvent(
                      address indexed caller,
                      string indexed logName,
                      bytes data
                  );
              
                  function logRecipeEvent(
                      string memory _logName
                  ) public {
                      emit RecipeEvent(msg.sender, _logName);
                  }
              
                  function logActionDirectEvent(
                      string memory _logName,
                      bytes memory _data
                  ) public {
                      emit ActionDirectEvent(msg.sender, _logName, _data);
                  }
              }
              
              
              
              
              
              
              
              
              
              
              
              
              
              abstract contract ActionBase is AdminAuth, ActionsUtilHelper, CheckWalletType {
                  event ActionEvent(
                      string indexed logName,
                      bytes data
                  );
              
                  DFSRegistry public constant registry = DFSRegistry(REGISTRY_ADDR);
              
                  DefisaverLogger public constant logger = DefisaverLogger(
                      DFS_LOGGER_ADDR
                  );
              
                  //Wrong sub index value
                  error SubIndexValueError();
                  //Wrong return index value
                  error ReturnIndexValueError();
              
                  /// @dev Subscription params index range [128, 255]
                  uint8 public constant SUB_MIN_INDEX_VALUE = 128;
                  uint8 public constant SUB_MAX_INDEX_VALUE = 255;
              
                  /// @dev Return params index range [1, 127]
                  uint8 public constant RETURN_MIN_INDEX_VALUE = 1;
                  uint8 public constant RETURN_MAX_INDEX_VALUE = 127;
              
                  /// @dev If the input value should not be replaced
                  uint8 public constant NO_PARAM_MAPPING = 0;
              
                  /// @dev We need to parse Flash loan actions in a different way
                  enum ActionType { FL_ACTION, STANDARD_ACTION, FEE_ACTION, CHECK_ACTION, CUSTOM_ACTION }
              
                  /// @notice Parses inputs and runs the implemented action through a user wallet
                  /// @dev Is called by the RecipeExecutor chaining actions together
                  /// @param _callData Array of input values each value encoded as bytes
                  /// @param _subData Array of subscribed vales, replaces input values if specified
                  /// @param _paramMapping Array that specifies how return and subscribed values are mapped in input
                  /// @param _returnValues Returns values from actions before, which can be injected in inputs
                  /// @return Returns a bytes32 value through user wallet, each actions implements what that value is
                  function executeAction(
                      bytes memory _callData,
                      bytes32[] memory _subData,
                      uint8[] memory _paramMapping,
                      bytes32[] memory _returnValues
                  ) public payable virtual returns (bytes32);
              
                  /// @notice Parses inputs and runs the single implemented action through a user wallet
                  /// @dev Used to save gas when executing a single action directly
                  function executeActionDirect(bytes memory _callData) public virtual payable;
              
                  /// @notice Returns the type of action we are implementing
                  function actionType() public pure virtual returns (uint8);
              
              
                  //////////////////////////// HELPER METHODS ////////////////////////////
              
                  /// @notice Given an uint256 input, injects return/sub values if specified
                  /// @param _param The original input value
                  /// @param _mapType Indicated the type of the input in paramMapping
                  /// @param _subData Array of subscription data we can replace the input value with
                  /// @param _returnValues Array of subscription data we can replace the input value with
                  function _parseParamUint(
                      uint _param,
                      uint8 _mapType,
                      bytes32[] memory _subData,
                      bytes32[] memory _returnValues
                  ) internal pure returns (uint) {
                      if (isReplaceable(_mapType)) {
                          if (isReturnInjection(_mapType)) {
                              _param = uint(_returnValues[getReturnIndex(_mapType)]);
                          } else {
                              _param = uint256(_subData[getSubIndex(_mapType)]);
                          }
                      }
              
                      return _param;
                  }
              
              
                  /// @notice Given an addr input, injects return/sub values if specified
                  /// @param _param The original input value
                  /// @param _mapType Indicated the type of the input in paramMapping
                  /// @param _subData Array of subscription data we can replace the input value with
                  /// @param _returnValues Array of subscription data we can replace the input value with
                  function _parseParamAddr(
                      address _param,
                      uint8 _mapType,
                      bytes32[] memory _subData,
                      bytes32[] memory _returnValues
                  ) internal view returns (address) {
                      if (isReplaceable(_mapType)) {
                          if (isReturnInjection(_mapType)) {
                              _param = address(bytes20((_returnValues[getReturnIndex(_mapType)])));
                          } else {
                              /// @dev The last two values are specially reserved for proxy addr and owner addr
                              if (_mapType == 254) return address(this); // wallet address
                              if (_mapType == 255) return fetchOwnersOrWallet(); // owner if 1/1 wallet or the wallet itself
              
                              _param = address(uint160(uint256(_subData[getSubIndex(_mapType)])));
                          }
                      }
              
                      return _param;
                  }
              
                  /// @notice Given an bytes32 input, injects return/sub values if specified
                  /// @param _param The original input value
                  /// @param _mapType Indicated the type of the input in paramMapping
                  /// @param _subData Array of subscription data we can replace the input value with
                  /// @param _returnValues Array of subscription data we can replace the input value with
                  function _parseParamABytes32(
                      bytes32 _param,
                      uint8 _mapType,
                      bytes32[] memory _subData,
                      bytes32[] memory _returnValues
                  ) internal pure returns (bytes32) {
                      if (isReplaceable(_mapType)) {
                          if (isReturnInjection(_mapType)) {
                              _param = (_returnValues[getReturnIndex(_mapType)]);
                          } else {
                              _param = _subData[getSubIndex(_mapType)];
                          }
                      }
              
                      return _param;
                  }
              
                  /// @notice Checks if the paramMapping value indicated that we need to inject values
                  /// @param _type Indicated the type of the input
                  function isReplaceable(uint8 _type) internal pure returns (bool) {
                      return _type != NO_PARAM_MAPPING;
                  }
              
                  /// @notice Checks if the paramMapping value is in the return value range
                  /// @param _type Indicated the type of the input
                  function isReturnInjection(uint8 _type) internal pure returns (bool) {
                      return (_type >= RETURN_MIN_INDEX_VALUE) && (_type <= RETURN_MAX_INDEX_VALUE);
                  }
              
                  /// @notice Transforms the paramMapping value to the index in return array value
                  /// @param _type Indicated the type of the input
                  function getReturnIndex(uint8 _type) internal pure returns (uint8) {
                      if (!(isReturnInjection(_type))){
                          revert SubIndexValueError();
                      }
              
                      return (_type - RETURN_MIN_INDEX_VALUE);
                  }
              
                  /// @notice Transforms the paramMapping value to the index in sub array value
                  /// @param _type Indicated the type of the input
                  function getSubIndex(uint8 _type) internal pure returns (uint8) {
                      if (_type < SUB_MIN_INDEX_VALUE){
                          revert ReturnIndexValueError();
                      }
                      return (_type - SUB_MIN_INDEX_VALUE);
                  }
              
                  function fetchOwnersOrWallet() internal view returns (address) {
                      if (isDSProxy(address(this))) 
                          return DSProxy(payable(address(this))).owner();
              
                      // if not DSProxy, we assume we are in context of Safe
                      address[] memory owners = ISafe(address(this)).getOwners();
                      return owners.length == 1 ? owners[0] : address(this);
                  }
              }
              
              
              
              
              
              
              
              contract MainnetCompV3Addresses {
                  address internal constant COMET_REWARDS_ADDR = 0x1B0e765F6224C21223AeA2af16c1C46E38885a40;
                  address internal constant COMP_ETH_COMET = 0xA17581A9E3356d9A858b789D68B4d866e593aE94;
              }
              
              
              
              
              
              
              
              abstract contract IComet {
              
                  struct AssetInfo {
                      uint8 offset;
                      address asset;
                      address priceFeed;
                      uint64 scale;
                      uint64 borrowCollateralFactor;
                      uint64 liquidateCollateralFactor;
                      uint64 liquidationFactor;
                      uint128 supplyCap;
                  }
              
                  struct TotalsCollateral {
                      uint128 totalSupplyAsset;
                      uint128 _reserved;
                  }
              
                    struct UserCollateral {
                      uint128 balance;
                      uint128 _reserved;
                  }
              
              
                  struct UserBasic {
                      int104 principal;
                      uint64 baseTrackingIndex;
                      uint64 baseTrackingAccrued;
                      uint16 assetsIn;
                      uint8 _reserved;
                  }
              
                  struct TotalsBasic {
                      uint64 baseSupplyIndex;
                      uint64 baseBorrowIndex;
                      uint64 trackingSupplyIndex;
                      uint64 trackingBorrowIndex;
                      uint104 totalSupplyBase;
                      uint104 totalBorrowBase;
                      uint40 lastAccrualTime;
                      uint8 pauseFlags;
                  }
              
                  function totalsBasic() public virtual view returns (TotalsBasic memory);
              
                  function totalsCollateral(address) public virtual returns (TotalsCollateral memory);
                  
                  function supply(address asset, uint amount) virtual external;
                  function supplyTo(address dst, address asset, uint amount) virtual external;
                  function supplyFrom(address from, address dst, address asset, uint amount) virtual external;
              
                  function transfer(address dst, uint amount) virtual external returns (bool);
                  function transferFrom(address src, address dst, uint amount) virtual external returns (bool);
              
                  function transferAsset(address dst, address asset, uint amount) virtual external;
                  function transferAssetFrom(address src, address dst, address asset, uint amount) virtual external;
              
                  function withdraw(address asset, uint amount) virtual external;
                  function withdrawTo(address to, address asset, uint amount) virtual external;
                  function withdrawFrom(address src, address to, address asset, uint amount) virtual external;
              
                  function accrueAccount(address account) virtual external;
                  function getSupplyRate(uint utilization) virtual public view returns (uint64);
                  function getBorrowRate(uint utilization) virtual public view returns (uint64);
                  function getUtilization() virtual public view returns (uint);
              
                  function governor() virtual external view returns (address);
                  function baseToken() virtual external view returns (address);
                  function baseTokenPriceFeed() virtual external view returns (address);
              
                  function balanceOf(address account) virtual public view returns (uint256);
                  function collateralBalanceOf(address account, address asset) virtual external view returns (uint128);
                  function borrowBalanceOf(address account) virtual public view returns (uint256);
                  function totalSupply() virtual external view returns (uint256);
              
                  function numAssets() virtual public view returns (uint8);
                  function getAssetInfo(uint8 i) virtual public view returns (AssetInfo memory);
                  function getAssetInfoByAddress(address asset) virtual public view returns (AssetInfo memory);
                  function getPrice(address priceFeed) virtual public view returns (uint256);
              
                  function allow(address manager, bool isAllowed) virtual external;
                  function allowance(address owner, address spender) virtual external view returns (uint256);
              
                  function isSupplyPaused() virtual external view returns (bool);
                  function isTransferPaused() virtual external view returns (bool);
                  function isWithdrawPaused() virtual external view returns (bool);
                  function isAbsorbPaused() virtual external view returns (bool);
                  function baseIndexScale() virtual external pure returns (uint64);
              
                  function userBasic(address) virtual external view returns (UserBasic memory);
                  function userCollateral(address, address) virtual external view returns (UserCollateral memory);
                  function priceScale() virtual external pure returns (uint64);
                  function factorScale() virtual external pure returns (uint64);
              
                  function baseBorrowMin() virtual external pure returns (uint256);
                  function baseTrackingBorrowSpeed() virtual external pure returns (uint256);
                  function baseTrackingSupplySpeed() virtual external pure returns (uint256);
              
              }
              
              
              
              
              
              
              
              
              contract CompV3Helper is MainnetCompV3Addresses {
                  
              }
              
              
              
              
              
              
              
              abstract contract IWETH {
                  function allowance(address, address) public virtual view returns (uint256);
              
                  function balanceOf(address) public virtual view returns (uint256);
              
                  function approve(address, uint256) public virtual;
              
                  function transfer(address, uint256) public virtual returns (bool);
              
                  function transferFrom(
                      address,
                      address,
                      uint256
                  ) public virtual returns (bool);
              
                  function deposit() public payable virtual;
              
                  function withdraw(uint256) public virtual;
              }
              
              
              
              
              
              
              
              
              library TokenUtils {
                  using SafeERC20 for IERC20;
              
                  address public constant WETH_ADDR = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
                  address public constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
              
                  /// @dev Only approves the amount if allowance is lower than amount, does not decrease allowance
                  function approveToken(
                      address _tokenAddr,
                      address _to,
                      uint256 _amount
                  ) internal {
                      if (_tokenAddr == ETH_ADDR) return;
              
                      if (IERC20(_tokenAddr).allowance(address(this), _to) < _amount) {
                          IERC20(_tokenAddr).safeApprove(_to, _amount);
                      }
                  }
              
                  function pullTokensIfNeeded(
                      address _token,
                      address _from,
                      uint256 _amount
                  ) internal returns (uint256) {
                      // handle max uint amount
                      if (_amount == type(uint256).max) {
                          _amount = getBalance(_token, _from);
                      }
              
                      if (_from != address(0) && _from != address(this) && _token != ETH_ADDR && _amount != 0) {
                          IERC20(_token).safeTransferFrom(_from, address(this), _amount);
                      }
              
                      return _amount;
                  }
              
                  function withdrawTokens(
                      address _token,
                      address _to,
                      uint256 _amount
                  ) internal returns (uint256) {
                      if (_amount == type(uint256).max) {
                          _amount = getBalance(_token, address(this));
                      }
              
                      if (_to != address(0) && _to != address(this) && _amount != 0) {
                          if (_token != ETH_ADDR) {
                              IERC20(_token).safeTransfer(_to, _amount);
                          } else {
                              (bool success, ) = _to.call{value: _amount}("");
                              require(success, "Eth send fail");
                          }
                      }
              
                      return _amount;
                  }
              
                  function depositWeth(uint256 _amount) internal {
                      IWETH(WETH_ADDR).deposit{value: _amount}();
                  }
              
                  function withdrawWeth(uint256 _amount) internal {
                      IWETH(WETH_ADDR).withdraw(_amount);
                  }
              
                  function getBalance(address _tokenAddr, address _acc) internal view returns (uint256) {
                      if (_tokenAddr == ETH_ADDR) {
                          return _acc.balance;
                      } else {
                          return IERC20(_tokenAddr).balanceOf(_acc);
                      }
                  }
              
                  function getTokenDecimals(address _token) internal view returns (uint256) {
                      if (_token == ETH_ADDR) return 18;
              
                      return IERC20(_token).decimals();
                  }
              }
              
              
              
              
              
              
              
              
              
              
              contract CompV3Supply is ActionBase, CompV3Helper {
                  using TokenUtils for address;
              
                  /// @param market Main Comet proxy contract that is different for each compound market
                  /// @param tokenAddr  Address of the token we are supplying
                  /// @param amount Amount in wei of tokens we are supplying
                  /// @param from Address from which we are pulling the tokens
                  /// @param onBehalf Address where we are supplying the tokens to
                  struct Params {
                      address market;
                      address tokenAddr;
                      uint256 amount;
                      address from;
                      address onBehalf;
                  }
              
                  error CompV3SupplyWithDebtError();
              
                  /// @inheritdoc ActionBase
                  function executeAction(
                      bytes memory _callData,
                      bytes32[] memory _subData,
                      uint8[] memory _paramMapping,
                      bytes32[] memory _returnValues
                  ) public payable virtual override returns (bytes32) {
                      Params memory params = parseInputs(_callData);
              
                      params.market = _parseParamAddr(params.market, _paramMapping[0], _subData, _returnValues);
                      params.tokenAddr = _parseParamAddr(params.tokenAddr, _paramMapping[1], _subData, _returnValues);
                      params.amount = _parseParamUint(params.amount, _paramMapping[2], _subData, _returnValues);
                      params.from = _parseParamAddr(params.from, _paramMapping[3], _subData, _returnValues);
              
                      // param was added later on so we check if it's sent
                      if (_paramMapping.length == 5) {
                          params.onBehalf = _parseParamAddr(params.onBehalf, _paramMapping[4], _subData, _returnValues);
                      }
              
                      (uint256 withdrawAmount, bytes memory logData) = _supply(params);
                      emit ActionEvent("CompV3Supply", logData);
                      return bytes32(withdrawAmount);
                  }
              
                  /// @inheritdoc ActionBase
                  function executeActionDirect(bytes memory _callData) public payable override {
                      Params memory params = parseInputs(_callData);
                      (, bytes memory logData) = _supply(params);
                      logger.logActionDirectEvent("CompV3Supply", logData);
                  }
              
                  /// @inheritdoc ActionBase
                  function actionType() public pure virtual override returns (uint8) {
                      return uint8(ActionType.STANDARD_ACTION);
                  }
              
                  //////////////////////////// ACTION LOGIC ////////////////////////////
              
                  /// @notice Supplies a token to the CompoundV3 protocol
                  /// @dev If supply is baseToken it must not borrow balance or the action will revert
                  /// @dev If onBehalf == address(0) we default to user's wallet address
                  /// @param _params Supply input struct documented above
                  function _supply(Params memory _params) internal returns (uint256, bytes memory) {
                      if (_params.onBehalf == address(0)) {
                          _params.onBehalf = address(this);
                      }
              
                      // pull the tokens _from to the user's wallet
                      _params.amount = _params.tokenAddr.pullTokensIfNeeded(_params.from, _params.amount);
              
                      _params.tokenAddr.approveToken(_params.market, _params.amount);
              
                      // if the user has baseToken debt, use payback
                      if(_params.tokenAddr == IComet(_params.market).baseToken()) {
                          uint256 debt = IComet(_params.market).borrowBalanceOf(_params.onBehalf);
                          if(debt > 0) {
                              revert CompV3SupplyWithDebtError();
                          }
                      }
                      
                      IComet(_params.market).supplyTo(_params.onBehalf, _params.tokenAddr, _params.amount);
                      
                      bytes memory logData = abi.encode(_params);
                      return (_params.amount, logData);
                  }
              
                  function parseInputs(bytes memory _callData) public pure returns (Params memory params) {
                      params = abi.decode(_callData, (Params));
                  }
              }
              

              File 7 of 7: Comet
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.15;
              import "./CometMainInterface.sol";
              import "./ERC20.sol";
              import "./vendor/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
              /**
               * @title Compound's Comet Contract
               * @notice An efficient monolithic money market protocol
               * @author Compound
               */
              contract Comet is CometMainInterface {
                  /** General configuration constants **/
                  /// @notice The admin of the protocol
                  address public override immutable governor;
                  /// @notice The account which may trigger pauses
                  address public override immutable pauseGuardian;
                  /// @notice The address of the base token contract
                  address public override immutable baseToken;
                  /// @notice The address of the price feed for the base token
                  address public override immutable baseTokenPriceFeed;
                  /// @notice The address of the extension contract delegate
                  address public override immutable extensionDelegate;
                  /// @notice The point in the supply rates separating the low interest rate slope and the high interest rate slope (factor)
                  /// @dev uint64
                  uint public override immutable supplyKink;
                  /// @notice Per second supply interest rate slope applied when utilization is below kink (factor)
                  /// @dev uint64
                  uint public override immutable supplyPerSecondInterestRateSlopeLow;
                  /// @notice Per second supply interest rate slope applied when utilization is above kink (factor)
                  /// @dev uint64
                  uint public override immutable supplyPerSecondInterestRateSlopeHigh;
                  /// @notice Per second supply base interest rate (factor)
                  /// @dev uint64
                  uint public override immutable supplyPerSecondInterestRateBase;
                  /// @notice The point in the borrow rate separating the low interest rate slope and the high interest rate slope (factor)
                  /// @dev uint64
                  uint public override immutable borrowKink;
                  /// @notice Per second borrow interest rate slope applied when utilization is below kink (factor)
                  /// @dev uint64
                  uint public override immutable borrowPerSecondInterestRateSlopeLow;
                  /// @notice Per second borrow interest rate slope applied when utilization is above kink (factor)
                  /// @dev uint64
                  uint public override immutable borrowPerSecondInterestRateSlopeHigh;
                  /// @notice Per second borrow base interest rate (factor)
                  /// @dev uint64
                  uint public override immutable borrowPerSecondInterestRateBase;
                  /// @notice The fraction of the liquidation penalty that goes to buyers of collateral instead of the protocol
                  /// @dev uint64
                  uint public override immutable storeFrontPriceFactor;
                  /// @notice The scale for base token (must be less than 18 decimals)
                  /// @dev uint64
                  uint public override immutable baseScale;
                  /// @notice The scale for reward tracking
                  /// @dev uint64
                  uint public override immutable trackingIndexScale;
                  /// @notice The speed at which supply rewards are tracked (in trackingIndexScale)
                  /// @dev uint64
                  uint public override immutable baseTrackingSupplySpeed;
                  /// @notice The speed at which borrow rewards are tracked (in trackingIndexScale)
                  /// @dev uint64
                  uint public override immutable baseTrackingBorrowSpeed;
                  /// @notice The minimum amount of base principal wei for rewards to accrue
                  /// @dev This must be large enough so as to prevent division by base wei from overflowing the 64 bit indices
                  /// @dev uint104
                  uint public override immutable baseMinForRewards;
                  /// @notice The minimum base amount required to initiate a borrow
                  uint public override immutable baseBorrowMin;
                  /// @notice The minimum base token reserves which must be held before collateral is hodled
                  uint public override immutable targetReserves;
                  /// @notice The number of decimals for wrapped base token
                  uint8 public override immutable decimals;
                  /// @notice The number of assets this contract actually supports
                  uint8 public override immutable numAssets;
                  /// @notice Factor to divide by when accruing rewards in order to preserve 6 decimals (i.e. baseScale / 1e6)
                  uint internal immutable accrualDescaleFactor;
                  /** Collateral asset configuration (packed) **/
                  uint256 internal immutable asset00_a;
                  uint256 internal immutable asset00_b;
                  uint256 internal immutable asset01_a;
                  uint256 internal immutable asset01_b;
                  uint256 internal immutable asset02_a;
                  uint256 internal immutable asset02_b;
                  uint256 internal immutable asset03_a;
                  uint256 internal immutable asset03_b;
                  uint256 internal immutable asset04_a;
                  uint256 internal immutable asset04_b;
                  uint256 internal immutable asset05_a;
                  uint256 internal immutable asset05_b;
                  uint256 internal immutable asset06_a;
                  uint256 internal immutable asset06_b;
                  uint256 internal immutable asset07_a;
                  uint256 internal immutable asset07_b;
                  uint256 internal immutable asset08_a;
                  uint256 internal immutable asset08_b;
                  uint256 internal immutable asset09_a;
                  uint256 internal immutable asset09_b;
                  uint256 internal immutable asset10_a;
                  uint256 internal immutable asset10_b;
                  uint256 internal immutable asset11_a;
                  uint256 internal immutable asset11_b;
                  uint256 internal immutable asset12_a;
                  uint256 internal immutable asset12_b;
                  uint256 internal immutable asset13_a;
                  uint256 internal immutable asset13_b;
                  uint256 internal immutable asset14_a;
                  uint256 internal immutable asset14_b;
                  /**
                   * @notice Construct a new protocol instance
                   * @param config The mapping of initial/constant parameters
                   **/
                  constructor(Configuration memory config) {
                      // Sanity checks
                      uint8 decimals_ = ERC20(config.baseToken).decimals();
                      if (decimals_ > MAX_BASE_DECIMALS) revert BadDecimals();
                      if (config.storeFrontPriceFactor > FACTOR_SCALE) revert BadDiscount();
                      if (config.assetConfigs.length > MAX_ASSETS) revert TooManyAssets();
                      if (config.baseMinForRewards == 0) revert BadMinimum();
                      if (AggregatorV3Interface(config.baseTokenPriceFeed).decimals() != PRICE_FEED_DECIMALS) revert BadDecimals();
                      // Copy configuration
                      unchecked {
                          governor = config.governor;
                          pauseGuardian = config.pauseGuardian;
                          baseToken = config.baseToken;
                          baseTokenPriceFeed = config.baseTokenPriceFeed;
                          extensionDelegate = config.extensionDelegate;
                          storeFrontPriceFactor = config.storeFrontPriceFactor;
                          decimals = decimals_;
                          baseScale = uint64(10 ** decimals_);
                          trackingIndexScale = config.trackingIndexScale;
                          if (baseScale < BASE_ACCRUAL_SCALE) revert BadDecimals();
                          accrualDescaleFactor = baseScale / BASE_ACCRUAL_SCALE;
                          baseMinForRewards = config.baseMinForRewards;
                          baseTrackingSupplySpeed = config.baseTrackingSupplySpeed;
                          baseTrackingBorrowSpeed = config.baseTrackingBorrowSpeed;
                          baseBorrowMin = config.baseBorrowMin;
                          targetReserves = config.targetReserves;
                      }
                      // Set interest rate model configs
                      unchecked {
                          supplyKink = config.supplyKink;
                          supplyPerSecondInterestRateSlopeLow = config.supplyPerYearInterestRateSlopeLow / SECONDS_PER_YEAR;
                          supplyPerSecondInterestRateSlopeHigh = config.supplyPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR;
                          supplyPerSecondInterestRateBase = config.supplyPerYearInterestRateBase / SECONDS_PER_YEAR;
                          borrowKink = config.borrowKink;
                          borrowPerSecondInterestRateSlopeLow = config.borrowPerYearInterestRateSlopeLow / SECONDS_PER_YEAR;
                          borrowPerSecondInterestRateSlopeHigh = config.borrowPerYearInterestRateSlopeHigh / SECONDS_PER_YEAR;
                          borrowPerSecondInterestRateBase = config.borrowPerYearInterestRateBase / SECONDS_PER_YEAR;
                      }
                      // Set asset info
                      numAssets = uint8(config.assetConfigs.length);
                      (asset00_a, asset00_b) = getPackedAssetInternal(config.assetConfigs, 0);
                      (asset01_a, asset01_b) = getPackedAssetInternal(config.assetConfigs, 1);
                      (asset02_a, asset02_b) = getPackedAssetInternal(config.assetConfigs, 2);
                      (asset03_a, asset03_b) = getPackedAssetInternal(config.assetConfigs, 3);
                      (asset04_a, asset04_b) = getPackedAssetInternal(config.assetConfigs, 4);
                      (asset05_a, asset05_b) = getPackedAssetInternal(config.assetConfigs, 5);
                      (asset06_a, asset06_b) = getPackedAssetInternal(config.assetConfigs, 6);
                      (asset07_a, asset07_b) = getPackedAssetInternal(config.assetConfigs, 7);
                      (asset08_a, asset08_b) = getPackedAssetInternal(config.assetConfigs, 8);
                      (asset09_a, asset09_b) = getPackedAssetInternal(config.assetConfigs, 9);
                      (asset10_a, asset10_b) = getPackedAssetInternal(config.assetConfigs, 10);
                      (asset11_a, asset11_b) = getPackedAssetInternal(config.assetConfigs, 11);
                      (asset12_a, asset12_b) = getPackedAssetInternal(config.assetConfigs, 12);
                      (asset13_a, asset13_b) = getPackedAssetInternal(config.assetConfigs, 13);
                      (asset14_a, asset14_b) = getPackedAssetInternal(config.assetConfigs, 14);
                  }
                  /**
                   * @notice Initialize storage for the contract
                   * @dev Can be used from constructor or proxy
                   */
                  function initializeStorage() override external {
                      if (lastAccrualTime != 0) revert AlreadyInitialized();
                      // Initialize aggregates
                      lastAccrualTime = getNowInternal();
                      baseSupplyIndex = BASE_INDEX_SCALE;
                      baseBorrowIndex = BASE_INDEX_SCALE;
                      // Implicit initialization (not worth increasing contract size)
                      // trackingSupplyIndex = 0;
                      // trackingBorrowIndex = 0;
                  }
                  /**
                   * @dev Checks and gets the packed asset info for storage
                   */
                  function getPackedAssetInternal(AssetConfig[] memory assetConfigs, uint i) internal view returns (uint256, uint256) {
                      AssetConfig memory assetConfig;
                      if (i < assetConfigs.length) {
                          assembly {
                              assetConfig := mload(add(add(assetConfigs, 0x20), mul(i, 0x20)))
                          }
                      } else {
                          return (0, 0);
                      }
                      address asset = assetConfig.asset;
                      address priceFeed = assetConfig.priceFeed;
                      uint8 decimals_ = assetConfig.decimals;
                      // Short-circuit if asset is nil
                      if (asset == address(0)) {
                          return (0, 0);
                      }
                      // Sanity check price feed and asset decimals
                      if (AggregatorV3Interface(priceFeed).decimals() != PRICE_FEED_DECIMALS) revert BadDecimals();
                      if (ERC20(asset).decimals() != decimals_) revert BadDecimals();
                      // Ensure collateral factors are within range
                      if (assetConfig.borrowCollateralFactor >= assetConfig.liquidateCollateralFactor) revert BorrowCFTooLarge();
                      if (assetConfig.liquidateCollateralFactor > MAX_COLLATERAL_FACTOR) revert LiquidateCFTooLarge();
                      unchecked {
                          // Keep 4 decimals for each factor
                          uint64 descale = FACTOR_SCALE / 1e4;
                          uint16 borrowCollateralFactor = uint16(assetConfig.borrowCollateralFactor / descale);
                          uint16 liquidateCollateralFactor = uint16(assetConfig.liquidateCollateralFactor / descale);
                          uint16 liquidationFactor = uint16(assetConfig.liquidationFactor / descale);
                          // Be nice and check descaled values are still within range
                          if (borrowCollateralFactor >= liquidateCollateralFactor) revert BorrowCFTooLarge();
                          // Keep whole units of asset for supply cap
                          uint64 supplyCap = uint64(assetConfig.supplyCap / (10 ** decimals_));
                          uint256 word_a = (uint160(asset) << 0 |
                                            uint256(borrowCollateralFactor) << 160 |
                                            uint256(liquidateCollateralFactor) << 176 |
                                            uint256(liquidationFactor) << 192);
                          uint256 word_b = (uint160(priceFeed) << 0 |
                                            uint256(decimals_) << 160 |
                                            uint256(supplyCap) << 168);
                          return (word_a, word_b);
                      }
                  }
                  /**
                   * @notice Get the i-th asset info, according to the order they were passed in originally
                   * @param i The index of the asset info to get
                   * @return The asset info object
                   */
                  function getAssetInfo(uint8 i) override public view returns (AssetInfo memory) {
                      if (i >= numAssets) revert BadAsset();
                      uint256 word_a;
                      uint256 word_b;
                      if (i == 0) {
                          word_a = asset00_a;
                          word_b = asset00_b;
                      } else if (i == 1) {
                          word_a = asset01_a;
                          word_b = asset01_b;
                      } else if (i == 2) {
                          word_a = asset02_a;
                          word_b = asset02_b;
                      } else if (i == 3) {
                          word_a = asset03_a;
                          word_b = asset03_b;
                      } else if (i == 4) {
                          word_a = asset04_a;
                          word_b = asset04_b;
                      } else if (i == 5) {
                          word_a = asset05_a;
                          word_b = asset05_b;
                      } else if (i == 6) {
                          word_a = asset06_a;
                          word_b = asset06_b;
                      } else if (i == 7) {
                          word_a = asset07_a;
                          word_b = asset07_b;
                      } else if (i == 8) {
                          word_a = asset08_a;
                          word_b = asset08_b;
                      } else if (i == 9) {
                          word_a = asset09_a;
                          word_b = asset09_b;
                      } else if (i == 10) {
                          word_a = asset10_a;
                          word_b = asset10_b;
                      } else if (i == 11) {
                          word_a = asset11_a;
                          word_b = asset11_b;
                      } else if (i == 12) {
                          word_a = asset12_a;
                          word_b = asset12_b;
                      } else if (i == 13) {
                          word_a = asset13_a;
                          word_b = asset13_b;
                      } else if (i == 14) {
                          word_a = asset14_a;
                          word_b = asset14_b;
                      } else {
                          revert Absurd();
                      }
                      address asset = address(uint160(word_a & type(uint160).max));
                      uint64 rescale = FACTOR_SCALE / 1e4;
                      uint64 borrowCollateralFactor = uint64(((word_a >> 160) & type(uint16).max) * rescale);
                      uint64 liquidateCollateralFactor = uint64(((word_a >> 176) & type(uint16).max) * rescale);
                      uint64 liquidationFactor = uint64(((word_a >> 192) & type(uint16).max) * rescale);
                      address priceFeed = address(uint160(word_b & type(uint160).max));
                      uint8 decimals_ = uint8(((word_b >> 160) & type(uint8).max));
                      uint64 scale = uint64(10 ** decimals_);
                      uint128 supplyCap = uint128(((word_b >> 168) & type(uint64).max) * scale);
                      return AssetInfo({
                          offset: i,
                          asset: asset,
                          priceFeed: priceFeed,
                          scale: scale,
                          borrowCollateralFactor: borrowCollateralFactor,
                          liquidateCollateralFactor: liquidateCollateralFactor,
                          liquidationFactor: liquidationFactor,
                          supplyCap: supplyCap
                       });
                  }
                  /**
                   * @dev Determine index of asset that matches given address
                   */
                  function getAssetInfoByAddress(address asset) override public view returns (AssetInfo memory) {
                      for (uint8 i = 0; i < numAssets; ) {
                          AssetInfo memory assetInfo = getAssetInfo(i);
                          if (assetInfo.asset == asset) {
                              return assetInfo;
                          }
                          unchecked { i++; }
                      }
                      revert BadAsset();
                  }
                  /**
                   * @return The current timestamp
                   **/
                  function getNowInternal() virtual internal view returns (uint40) {
                      if (block.timestamp >= 2**40) revert TimestampTooLarge();
                      return uint40(block.timestamp);
                  }
                  /**
                   * @dev Calculate accrued interest indices for base token supply and borrows
                   **/
                  function accruedInterestIndices(uint timeElapsed) internal view returns (uint64, uint64) {
                      uint64 baseSupplyIndex_ = baseSupplyIndex;
                      uint64 baseBorrowIndex_ = baseBorrowIndex;
                      if (timeElapsed > 0) {
                          uint utilization = getUtilization();
                          uint supplyRate = getSupplyRate(utilization);
                          uint borrowRate = getBorrowRate(utilization);
                          baseSupplyIndex_ += safe64(mulFactor(baseSupplyIndex_, supplyRate * timeElapsed));
                          baseBorrowIndex_ += safe64(mulFactor(baseBorrowIndex_, borrowRate * timeElapsed));
                      }
                      return (baseSupplyIndex_, baseBorrowIndex_);
                  }
                  /**
                   * @dev Accrue interest (and rewards) in base token supply and borrows
                   **/
                  function accrueInternal() internal {
                      uint40 now_ = getNowInternal();
                      uint timeElapsed = uint256(now_ - lastAccrualTime);
                      if (timeElapsed > 0) {
                          (baseSupplyIndex, baseBorrowIndex) = accruedInterestIndices(timeElapsed);
                          if (totalSupplyBase >= baseMinForRewards) {
                              trackingSupplyIndex += safe64(divBaseWei(baseTrackingSupplySpeed * timeElapsed, totalSupplyBase));
                          }
                          if (totalBorrowBase >= baseMinForRewards) {
                              trackingBorrowIndex += safe64(divBaseWei(baseTrackingBorrowSpeed * timeElapsed, totalBorrowBase));
                          }
                          lastAccrualTime = now_;
                      }
                  }
                  /**
                   * @notice Accrue interest and rewards for an account
                   **/
                  function accrueAccount(address account) override external {
                      accrueInternal();
                      UserBasic memory basic = userBasic[account];
                      updateBasePrincipal(account, basic, basic.principal);
                  }
                  /**
                   * @dev Note: Does not accrue interest first
                   * @param utilization The utilization to check the supply rate for
                   * @return The per second supply rate at `utilization`
                   */
                  function getSupplyRate(uint utilization) override public view returns (uint64) {
                      if (utilization <= supplyKink) {
                          // interestRateBase + interestRateSlopeLow * utilization
                          return safe64(supplyPerSecondInterestRateBase + mulFactor(supplyPerSecondInterestRateSlopeLow, utilization));
                      } else {
                          // interestRateBase + interestRateSlopeLow * kink + interestRateSlopeHigh * (utilization - kink)
                          return safe64(supplyPerSecondInterestRateBase + mulFactor(supplyPerSecondInterestRateSlopeLow, supplyKink) + mulFactor(supplyPerSecondInterestRateSlopeHigh, (utilization - supplyKink)));
                      }
                  }
                  /**
                   * @dev Note: Does not accrue interest first
                   * @param utilization The utilization to check the borrow rate for
                   * @return The per second borrow rate at `utilization`
                   */
                  function getBorrowRate(uint utilization) override public view returns (uint64) {
                      if (utilization <= borrowKink) {
                          // interestRateBase + interestRateSlopeLow * utilization
                          return safe64(borrowPerSecondInterestRateBase + mulFactor(borrowPerSecondInterestRateSlopeLow, utilization));
                      } else {
                          // interestRateBase + interestRateSlopeLow * kink + interestRateSlopeHigh * (utilization - kink)
                          return safe64(borrowPerSecondInterestRateBase + mulFactor(borrowPerSecondInterestRateSlopeLow, borrowKink) + mulFactor(borrowPerSecondInterestRateSlopeHigh, (utilization - borrowKink)));
                      }
                  }
                  /**
                   * @dev Note: Does not accrue interest first
                   * @return The utilization rate of the base asset
                   */
                  function getUtilization() override public view returns (uint) {
                      uint totalSupply_ = presentValueSupply(baseSupplyIndex, totalSupplyBase);
                      uint totalBorrow_ = presentValueBorrow(baseBorrowIndex, totalBorrowBase);
                      if (totalSupply_ == 0) {
                          return 0;
                      } else {
                          return totalBorrow_ * FACTOR_SCALE / totalSupply_;
                      }
                  }
                  /**
                   * @notice Get the current price from a feed
                   * @param priceFeed The address of a price feed
                   * @return The price, scaled by `PRICE_SCALE`
                   */
                  function getPrice(address priceFeed) override public view returns (uint256) {
                      (, int price, , , ) = AggregatorV3Interface(priceFeed).latestRoundData();
                      if (price <= 0) revert BadPrice();
                      return uint256(price);
                  }
                  /**
                   * @notice Gets the total balance of protocol collateral reserves for an asset
                   * @dev Note: Reverts if collateral reserves are somehow negative, which should not be possible
                   * @param asset The collateral asset
                   */
                  function getCollateralReserves(address asset) override public view returns (uint) {
                      return ERC20(asset).balanceOf(address(this)) - totalsCollateral[asset].totalSupplyAsset;
                  }
                  /**
                   * @notice Gets the total amount of protocol reserves of the base asset
                   */
                  function getReserves() override public view returns (int) {
                      (uint64 baseSupplyIndex_, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
                      uint balance = ERC20(baseToken).balanceOf(address(this));
                      uint totalSupply_ = presentValueSupply(baseSupplyIndex_, totalSupplyBase);
                      uint totalBorrow_ = presentValueBorrow(baseBorrowIndex_, totalBorrowBase);
                      return signed256(balance) - signed256(totalSupply_) + signed256(totalBorrow_);
                  }
                  /**
                   * @notice Check whether an account has enough collateral to borrow
                   * @param account The address to check
                   * @return Whether the account is minimally collateralized enough to borrow
                   */
                  function isBorrowCollateralized(address account) override public view returns (bool) {
                      int104 principal = userBasic[account].principal;
                      if (principal >= 0) {
                          return true;
                      }
                      uint16 assetsIn = userBasic[account].assetsIn;
                      int liquidity = signedMulPrice(
                          presentValue(principal),
                          getPrice(baseTokenPriceFeed),
                          uint64(baseScale)
                      );
                      for (uint8 i = 0; i < numAssets; ) {
                          if (isInAsset(assetsIn, i)) {
                              if (liquidity >= 0) {
                                  return true;
                              }
                              AssetInfo memory asset = getAssetInfo(i);
                              uint newAmount = mulPrice(
                                  userCollateral[account][asset.asset].balance,
                                  getPrice(asset.priceFeed),
                                  asset.scale
                              );
                              liquidity += signed256(mulFactor(
                                  newAmount,
                                  asset.borrowCollateralFactor
                              ));
                          }
                          unchecked { i++; }
                      }
                      return liquidity >= 0;
                  }
                  /**
                   * @notice Check whether an account has enough collateral to not be liquidated
                   * @param account The address to check
                   * @return Whether the account is minimally collateralized enough to not be liquidated
                   */
                  function isLiquidatable(address account) override public view returns (bool) {
                      int104 principal = userBasic[account].principal;
                      if (principal >= 0) {
                          return false;
                      }
                      uint16 assetsIn = userBasic[account].assetsIn;
                      int liquidity = signedMulPrice(
                          presentValue(principal),
                          getPrice(baseTokenPriceFeed),
                          uint64(baseScale)
                      );
                      for (uint8 i = 0; i < numAssets; ) {
                          if (isInAsset(assetsIn, i)) {
                              if (liquidity >= 0) {
                                  return false;
                              }
                              AssetInfo memory asset = getAssetInfo(i);
                              uint newAmount = mulPrice(
                                  userCollateral[account][asset.asset].balance,
                                  getPrice(asset.priceFeed),
                                  asset.scale
                              );
                              liquidity += signed256(mulFactor(
                                  newAmount,
                                  asset.liquidateCollateralFactor
                              ));
                          }
                          unchecked { i++; }
                      }
                      return liquidity < 0;
                  }
                  /**
                   * @dev The change in principal broken into repay and supply amounts
                   */
                  function repayAndSupplyAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) {
                      // If the new principal is less than the old principal, then no amount has been repaid or supplied
                      if (newPrincipal < oldPrincipal) return (0, 0);
                      if (newPrincipal <= 0) {
                          return (uint104(newPrincipal - oldPrincipal), 0);
                      } else if (oldPrincipal >= 0) {
                          return (0, uint104(newPrincipal - oldPrincipal));
                      } else {
                          return (uint104(-oldPrincipal), uint104(newPrincipal));
                      }
                  }
                  /**
                   * @dev The change in principal broken into withdraw and borrow amounts
                   */
                  function withdrawAndBorrowAmount(int104 oldPrincipal, int104 newPrincipal) internal pure returns (uint104, uint104) {
                      // If the new principal is greater than the old principal, then no amount has been withdrawn or borrowed
                      if (newPrincipal > oldPrincipal) return (0, 0);
                      if (newPrincipal >= 0) {
                          return (uint104(oldPrincipal - newPrincipal), 0);
                      } else if (oldPrincipal <= 0) {
                          return (0, uint104(oldPrincipal - newPrincipal));
                      } else {
                          return (uint104(oldPrincipal), uint104(-newPrincipal));
                      }
                  }
                  /**
                   * @notice Pauses different actions within Comet
                   * @param supplyPaused Boolean for pausing supply actions
                   * @param transferPaused Boolean for pausing transfer actions
                   * @param withdrawPaused Boolean for pausing withdraw actions
                   * @param absorbPaused Boolean for pausing absorb actions
                   * @param buyPaused Boolean for pausing buy actions
                   */
                  function pause(
                      bool supplyPaused,
                      bool transferPaused,
                      bool withdrawPaused,
                      bool absorbPaused,
                      bool buyPaused
                  ) override external {
                      if (msg.sender != governor && msg.sender != pauseGuardian) revert Unauthorized();
                      pauseFlags =
                          uint8(0) |
                          (toUInt8(supplyPaused) << PAUSE_SUPPLY_OFFSET) |
                          (toUInt8(transferPaused) << PAUSE_TRANSFER_OFFSET) |
                          (toUInt8(withdrawPaused) << PAUSE_WITHDRAW_OFFSET) |
                          (toUInt8(absorbPaused) << PAUSE_ABSORB_OFFSET) |
                          (toUInt8(buyPaused) << PAUSE_BUY_OFFSET);
                      emit PauseAction(supplyPaused, transferPaused, withdrawPaused, absorbPaused, buyPaused);
                  }
                  /**
                   * @return Whether or not supply actions are paused
                   */
                  function isSupplyPaused() override public view returns (bool) {
                      return toBool(pauseFlags & (uint8(1) << PAUSE_SUPPLY_OFFSET));
                  }
                  /**
                   * @return Whether or not transfer actions are paused
                   */
                  function isTransferPaused() override public view returns (bool) {
                      return toBool(pauseFlags & (uint8(1) << PAUSE_TRANSFER_OFFSET));
                  }
                  /**
                   * @return Whether or not withdraw actions are paused
                   */
                  function isWithdrawPaused() override public view returns (bool) {
                      return toBool(pauseFlags & (uint8(1) << PAUSE_WITHDRAW_OFFSET));
                  }
                  /**
                   * @return Whether or not absorb actions are paused
                   */
                  function isAbsorbPaused() override public view returns (bool) {
                      return toBool(pauseFlags & (uint8(1) << PAUSE_ABSORB_OFFSET));
                  }
                  /**
                   * @return Whether or not buy actions are paused
                   */
                  function isBuyPaused() override public view returns (bool) {
                      return toBool(pauseFlags & (uint8(1) << PAUSE_BUY_OFFSET));
                  }
                  /**
                   * @dev Multiply a number by a factor
                   */
                  function mulFactor(uint n, uint factor) internal pure returns (uint) {
                      return n * factor / FACTOR_SCALE;
                  }
                  /**
                   * @dev Divide a number by an amount of base
                   */
                  function divBaseWei(uint n, uint baseWei) internal view returns (uint) {
                      return n * baseScale / baseWei;
                  }
                  /**
                   * @dev Multiply a `fromScale` quantity by a price, returning a common price quantity
                   */
                  function mulPrice(uint n, uint price, uint64 fromScale) internal pure returns (uint) {
                      return n * price / fromScale;
                  }
                  /**
                   * @dev Multiply a signed `fromScale` quantity by a price, returning a common price quantity
                   */
                  function signedMulPrice(int n, uint price, uint64 fromScale) internal pure returns (int) {
                      return n * signed256(price) / int256(uint256(fromScale));
                  }
                  /**
                   * @dev Divide a common price quantity by a price, returning a `toScale` quantity
                   */
                  function divPrice(uint n, uint price, uint64 toScale) internal pure returns (uint) {
                      return n * toScale / price;
                  }
                  /**
                   * @dev Whether user has a non-zero balance of an asset, given assetsIn flags
                   */
                  function isInAsset(uint16 assetsIn, uint8 assetOffset) internal pure returns (bool) {
                      return (assetsIn & (uint16(1) << assetOffset) != 0);
                  }
                  /**
                   * @dev Update assetsIn bit vector if user has entered or exited an asset
                   */
                  function updateAssetsIn(
                      address account,
                      AssetInfo memory assetInfo,
                      uint128 initialUserBalance,
                      uint128 finalUserBalance
                  ) internal {
                      if (initialUserBalance == 0 && finalUserBalance != 0) {
                          // set bit for asset
                          userBasic[account].assetsIn |= (uint16(1) << assetInfo.offset);
                      } else if (initialUserBalance != 0 && finalUserBalance == 0) {
                          // clear bit for asset
                          userBasic[account].assetsIn &= ~(uint16(1) << assetInfo.offset);
                      }
                  }
                  /**
                   * @dev Write updated principal to store and tracking participation
                   */
                  function updateBasePrincipal(address account, UserBasic memory basic, int104 principalNew) internal {
                      int104 principal = basic.principal;
                      basic.principal = principalNew;
                      if (principal >= 0) {
                          uint indexDelta = uint256(trackingSupplyIndex - basic.baseTrackingIndex);
                          basic.baseTrackingAccrued += safe64(uint104(principal) * indexDelta / trackingIndexScale / accrualDescaleFactor);
                      } else {
                          uint indexDelta = uint256(trackingBorrowIndex - basic.baseTrackingIndex);
                          basic.baseTrackingAccrued += safe64(uint104(-principal) * indexDelta / trackingIndexScale / accrualDescaleFactor);
                      }
                      if (principalNew >= 0) {
                          basic.baseTrackingIndex = trackingSupplyIndex;
                      } else {
                          basic.baseTrackingIndex = trackingBorrowIndex;
                      }
                      userBasic[account] = basic;
                  }
                  /**
                   * @dev Safe ERC20 transfer in, assumes no fee is charged and amount is transferred
                   */
                  function doTransferIn(address asset, address from, uint amount) internal {
                      bool success = ERC20(asset).transferFrom(from, address(this), amount);
                      if (!success) revert TransferInFailed();
                  }
                  /**
                   * @dev Safe ERC20 transfer out
                   */
                  function doTransferOut(address asset, address to, uint amount) internal {
                      bool success = ERC20(asset).transfer(to, amount);
                      if (!success) revert TransferOutFailed();
                  }
                  /**
                   * @notice Supply an amount of asset to the protocol
                   * @param asset The asset to supply
                   * @param amount The quantity to supply
                   */
                  function supply(address asset, uint amount) override external {
                      return supplyInternal(msg.sender, msg.sender, msg.sender, asset, amount);
                  }
                  /**
                   * @notice Supply an amount of asset to dst
                   * @param dst The address which will hold the balance
                   * @param asset The asset to supply
                   * @param amount The quantity to supply
                   */
                  function supplyTo(address dst, address asset, uint amount) override external {
                      return supplyInternal(msg.sender, msg.sender, dst, asset, amount);
                  }
                  /**
                   * @notice Supply an amount of asset from `from` to dst, if allowed
                   * @param from The supplier address
                   * @param dst The address which will hold the balance
                   * @param asset The asset to supply
                   * @param amount The quantity to supply
                   */
                  function supplyFrom(address from, address dst, address asset, uint amount) override external {
                      return supplyInternal(msg.sender, from, dst, asset, amount);
                  }
                  /**
                   * @dev Supply either collateral or base asset, depending on the asset, if operator is allowed
                   * @dev Note: Specifying an `amount` of uint256.max will repay all of `dst`'s accrued base borrow balance
                   */
                  function supplyInternal(address operator, address from, address dst, address asset, uint amount) internal {
                      if (isSupplyPaused()) revert Paused();
                      if (!hasPermission(from, operator)) revert Unauthorized();
                      if (asset == baseToken) {
                          if (amount == type(uint256).max) {
                              amount = borrowBalanceOf(dst);
                          }
                          return supplyBase(from, dst, amount);
                      } else {
                          return supplyCollateral(from, dst, asset, safe128(amount));
                      }
                  }
                  /**
                   * @dev Supply an amount of base asset from `from` to dst
                   */
                  function supplyBase(address from, address dst, uint256 amount) internal {
                      doTransferIn(baseToken, from, amount);
                      accrueInternal();
                      UserBasic memory dstUser = userBasic[dst];
                      int104 dstPrincipal = dstUser.principal;
                      int256 dstBalance = presentValue(dstPrincipal) + signed256(amount);
                      int104 dstPrincipalNew = principalValue(dstBalance);
                      (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(dstPrincipal, dstPrincipalNew);
                      totalSupplyBase += supplyAmount;
                      totalBorrowBase -= repayAmount;
                      updateBasePrincipal(dst, dstUser, dstPrincipalNew);
                      emit Supply(from, dst, amount);
                      if (supplyAmount > 0) {
                          emit Transfer(address(0), dst, presentValueSupply(baseSupplyIndex, supplyAmount));
                      }
                  }
                  /**
                   * @dev Supply an amount of collateral asset from `from` to dst
                   */
                  function supplyCollateral(address from, address dst, address asset, uint128 amount) internal {
                      doTransferIn(asset, from, amount);
                      AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
                      TotalsCollateral memory totals = totalsCollateral[asset];
                      totals.totalSupplyAsset += amount;
                      if (totals.totalSupplyAsset > assetInfo.supplyCap) revert SupplyCapExceeded();
                      uint128 dstCollateral = userCollateral[dst][asset].balance;
                      uint128 dstCollateralNew = dstCollateral + amount;
                      totalsCollateral[asset] = totals;
                      userCollateral[dst][asset].balance = dstCollateralNew;
                      updateAssetsIn(dst, assetInfo, dstCollateral, dstCollateralNew);
                      emit SupplyCollateral(from, dst, asset, amount);
                  }
                  /**
                   * @notice ERC20 transfer an amount of base token to dst
                   * @param dst The recipient address
                   * @param amount The quantity to transfer
                   * @return true
                   */
                  function transfer(address dst, uint amount) override external returns (bool) {
                      transferInternal(msg.sender, msg.sender, dst, baseToken, amount);
                      return true;
                  }
                  /**
                   * @notice ERC20 transfer an amount of base token from src to dst, if allowed
                   * @param src The sender address
                   * @param dst The recipient address
                   * @param amount The quantity to transfer
                   * @return true
                   */
                  function transferFrom(address src, address dst, uint amount) override external returns (bool) {
                      transferInternal(msg.sender, src, dst, baseToken, amount);
                      return true;
                  }
                  /**
                   * @notice Transfer an amount of asset to dst
                   * @param dst The recipient address
                   * @param asset The asset to transfer
                   * @param amount The quantity to transfer
                   */
                  function transferAsset(address dst, address asset, uint amount) override external {
                      return transferInternal(msg.sender, msg.sender, dst, asset, amount);
                  }
                  /**
                   * @notice Transfer an amount of asset from src to dst, if allowed
                   * @param src The sender address
                   * @param dst The recipient address
                   * @param asset The asset to transfer
                   * @param amount The quantity to transfer
                   */
                  function transferAssetFrom(address src, address dst, address asset, uint amount) override external {
                      return transferInternal(msg.sender, src, dst, asset, amount);
                  }
                  /**
                   * @dev Transfer either collateral or base asset, depending on the asset, if operator is allowed
                   * @dev Note: Specifying an `amount` of uint256.max will transfer all of `src`'s accrued base balance
                   */
                  function transferInternal(address operator, address src, address dst, address asset, uint amount) internal {
                      if (isTransferPaused()) revert Paused();
                      if (!hasPermission(src, operator)) revert Unauthorized();
                      if (src == dst) revert NoSelfTransfer();
                      if (asset == baseToken) {
                          if (amount == type(uint256).max) {
                              amount = balanceOf(src);
                          }
                          return transferBase(src, dst, amount);
                      } else {
                          return transferCollateral(src, dst, asset, safe128(amount));
                      }
                  }
                  /**
                   * @dev Transfer an amount of base asset from src to dst, borrowing if possible/necessary
                   */
                  function transferBase(address src, address dst, uint256 amount) internal {
                      accrueInternal();
                      UserBasic memory srcUser = userBasic[src];
                      UserBasic memory dstUser = userBasic[dst];
                      int104 srcPrincipal = srcUser.principal;
                      int104 dstPrincipal = dstUser.principal;
                      int256 srcBalance = presentValue(srcPrincipal) - signed256(amount);
                      int256 dstBalance = presentValue(dstPrincipal) + signed256(amount);
                      int104 srcPrincipalNew = principalValue(srcBalance);
                      int104 dstPrincipalNew = principalValue(dstBalance);
                      (uint104 withdrawAmount, uint104 borrowAmount) = withdrawAndBorrowAmount(srcPrincipal, srcPrincipalNew);
                      (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(dstPrincipal, dstPrincipalNew);
                      // Note: Instead of `total += addAmount - subAmount` to avoid underflow errors.
                      totalSupplyBase = totalSupplyBase + supplyAmount - withdrawAmount;
                      totalBorrowBase = totalBorrowBase + borrowAmount - repayAmount;
                      updateBasePrincipal(src, srcUser, srcPrincipalNew);
                      updateBasePrincipal(dst, dstUser, dstPrincipalNew);
                      if (srcBalance < 0) {
                          if (uint256(-srcBalance) < baseBorrowMin) revert BorrowTooSmall();
                          if (!isBorrowCollateralized(src)) revert NotCollateralized();
                      }
                      if (withdrawAmount > 0) {
                          emit Transfer(src, address(0), presentValueSupply(baseSupplyIndex, withdrawAmount));
                      }
                      if (supplyAmount > 0) {
                          emit Transfer(address(0), dst, presentValueSupply(baseSupplyIndex, supplyAmount));
                      }
                  }
                  /**
                   * @dev Transfer an amount of collateral asset from src to dst
                   */
                  function transferCollateral(address src, address dst, address asset, uint128 amount) internal {
                      uint128 srcCollateral = userCollateral[src][asset].balance;
                      uint128 dstCollateral = userCollateral[dst][asset].balance;
                      uint128 srcCollateralNew = srcCollateral - amount;
                      uint128 dstCollateralNew = dstCollateral + amount;
                      userCollateral[src][asset].balance = srcCollateralNew;
                      userCollateral[dst][asset].balance = dstCollateralNew;
                      AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
                      updateAssetsIn(src, assetInfo, srcCollateral, srcCollateralNew);
                      updateAssetsIn(dst, assetInfo, dstCollateral, dstCollateralNew);
                      // Note: no accrue interest, BorrowCF < LiquidationCF covers small changes
                      if (!isBorrowCollateralized(src)) revert NotCollateralized();
                      emit TransferCollateral(src, dst, asset, amount);
                  }
                  /**
                   * @notice Withdraw an amount of asset from the protocol
                   * @param asset The asset to withdraw
                   * @param amount The quantity to withdraw
                   */
                  function withdraw(address asset, uint amount) override external {
                      return withdrawInternal(msg.sender, msg.sender, msg.sender, asset, amount);
                  }
                  /**
                   * @notice Withdraw an amount of asset to `to`
                   * @param to The recipient address
                   * @param asset The asset to withdraw
                   * @param amount The quantity to withdraw
                   */
                  function withdrawTo(address to, address asset, uint amount) override external {
                      return withdrawInternal(msg.sender, msg.sender, to, asset, amount);
                  }
                  /**
                   * @notice Withdraw an amount of asset from src to `to`, if allowed
                   * @param src The sender address
                   * @param to The recipient address
                   * @param asset The asset to withdraw
                   * @param amount The quantity to withdraw
                   */
                  function withdrawFrom(address src, address to, address asset, uint amount) override external {
                      return withdrawInternal(msg.sender, src, to, asset, amount);
                  }
                  /**
                   * @dev Withdraw either collateral or base asset, depending on the asset, if operator is allowed
                   * @dev Note: Specifying an `amount` of uint256.max will withdraw all of `src`'s accrued base balance
                   */
                  function withdrawInternal(address operator, address src, address to, address asset, uint amount) internal {
                      if (isWithdrawPaused()) revert Paused();
                      if (!hasPermission(src, operator)) revert Unauthorized();
                      if (asset == baseToken) {
                          if (amount == type(uint256).max) {
                              amount = balanceOf(src);
                          }
                          return withdrawBase(src, to, amount);
                      } else {
                          return withdrawCollateral(src, to, asset, safe128(amount));
                      }
                  }
                  /**
                   * @dev Withdraw an amount of base asset from src to `to`, borrowing if possible/necessary
                   */
                  function withdrawBase(address src, address to, uint256 amount) internal {
                      accrueInternal();
                      UserBasic memory srcUser = userBasic[src];
                      int104 srcPrincipal = srcUser.principal;
                      int256 srcBalance = presentValue(srcPrincipal) - signed256(amount);
                      int104 srcPrincipalNew = principalValue(srcBalance);
                      (uint104 withdrawAmount, uint104 borrowAmount) = withdrawAndBorrowAmount(srcPrincipal, srcPrincipalNew);
                      totalSupplyBase -= withdrawAmount;
                      totalBorrowBase += borrowAmount;
                      updateBasePrincipal(src, srcUser, srcPrincipalNew);
                      if (srcBalance < 0) {
                          if (uint256(-srcBalance) < baseBorrowMin) revert BorrowTooSmall();
                          if (!isBorrowCollateralized(src)) revert NotCollateralized();
                      }
                      doTransferOut(baseToken, to, amount);
                      emit Withdraw(src, to, amount);
                      if (withdrawAmount > 0) {
                          emit Transfer(src, address(0), presentValueSupply(baseSupplyIndex, withdrawAmount));
                      }
                  }
                  /**
                   * @dev Withdraw an amount of collateral asset from src to `to`
                   */
                  function withdrawCollateral(address src, address to, address asset, uint128 amount) internal {
                      uint128 srcCollateral = userCollateral[src][asset].balance;
                      uint128 srcCollateralNew = srcCollateral - amount;
                      totalsCollateral[asset].totalSupplyAsset -= amount;
                      userCollateral[src][asset].balance = srcCollateralNew;
                      AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
                      updateAssetsIn(src, assetInfo, srcCollateral, srcCollateralNew);
                      // Note: no accrue interest, BorrowCF < LiquidationCF covers small changes
                      if (!isBorrowCollateralized(src)) revert NotCollateralized();
                      doTransferOut(asset, to, amount);
                      emit WithdrawCollateral(src, to, asset, amount);
                  }
                  /**
                   * @notice Absorb a list of underwater accounts onto the protocol balance sheet
                   * @param absorber The recipient of the incentive paid to the caller of absorb
                   * @param accounts The list of underwater accounts to absorb
                   */
                  function absorb(address absorber, address[] calldata accounts) override external {
                      if (isAbsorbPaused()) revert Paused();
                      uint startGas = gasleft();
                      accrueInternal();
                      for (uint i = 0; i < accounts.length; ) {
                          absorbInternal(absorber, accounts[i]);
                          unchecked { i++; }
                      }
                      uint gasUsed = startGas - gasleft();
                      // Note: liquidator points are an imperfect tool for governance,
                      //  to be used while evaluating strategies for incentivizing absorption.
                      // Using gas price instead of base fee would more accurately reflect spend,
                      //  but is also subject to abuse if refunds were to be given automatically.
                      LiquidatorPoints memory points = liquidatorPoints[absorber];
                      points.numAbsorbs++;
                      points.numAbsorbed += safe64(accounts.length);
                      points.approxSpend += safe128(gasUsed * block.basefee);
                      liquidatorPoints[absorber] = points;
                  }
                  /**
                   * @dev Transfer user's collateral and debt to the protocol itself.
                   */
                  function absorbInternal(address absorber, address account) internal {
                      if (!isLiquidatable(account)) revert NotLiquidatable();
                      UserBasic memory accountUser = userBasic[account];
                      int104 oldPrincipal = accountUser.principal;
                      int256 oldBalance = presentValue(oldPrincipal);
                      uint16 assetsIn = accountUser.assetsIn;
                      uint256 basePrice = getPrice(baseTokenPriceFeed);
                      uint256 deltaValue = 0;
                      for (uint8 i = 0; i < numAssets; ) {
                          if (isInAsset(assetsIn, i)) {
                              AssetInfo memory assetInfo = getAssetInfo(i);
                              address asset = assetInfo.asset;
                              uint128 seizeAmount = userCollateral[account][asset].balance;
                              userCollateral[account][asset].balance = 0;
                              totalsCollateral[asset].totalSupplyAsset -= seizeAmount;
                              uint256 value = mulPrice(seizeAmount, getPrice(assetInfo.priceFeed), assetInfo.scale);
                              deltaValue += mulFactor(value, assetInfo.liquidationFactor);
                              emit AbsorbCollateral(absorber, account, asset, seizeAmount, value);
                          }
                          unchecked { i++; }
                      }
                      uint256 deltaBalance = divPrice(deltaValue, basePrice, uint64(baseScale));
                      int256 newBalance = oldBalance + signed256(deltaBalance);
                      // New balance will not be negative, all excess debt absorbed by reserves
                      if (newBalance < 0) {
                          newBalance = 0;
                      }
                      int104 newPrincipal = principalValue(newBalance);
                      updateBasePrincipal(account, accountUser, newPrincipal);
                      // reset assetsIn
                      userBasic[account].assetsIn = 0;
                      (uint104 repayAmount, uint104 supplyAmount) = repayAndSupplyAmount(oldPrincipal, newPrincipal);
                      // Reserves are decreased by increasing total supply and decreasing borrows
                      //  the amount of debt repaid by reserves is `newBalance - oldBalance`
                      totalSupplyBase += supplyAmount;
                      totalBorrowBase -= repayAmount;
                      uint256 basePaidOut = unsigned256(newBalance - oldBalance);
                      uint256 valueOfBasePaidOut = mulPrice(basePaidOut, basePrice, uint64(baseScale));
                      emit AbsorbDebt(absorber, account, basePaidOut, valueOfBasePaidOut);
                      if (newPrincipal > 0) {
                          emit Transfer(address(0), account, presentValueSupply(baseSupplyIndex, unsigned104(newPrincipal)));
                      }
                  }
                  /**
                   * @notice Buy collateral from the protocol using base tokens, increasing protocol reserves
                     A minimum collateral amount should be specified to indicate the maximum slippage acceptable for the buyer.
                   * @param asset The asset to buy
                   * @param minAmount The minimum amount of collateral tokens that should be received by the buyer
                   * @param baseAmount The amount of base tokens used to buy the collateral
                   * @param recipient The recipient address
                   */
                  function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) override external {
                      if (isBuyPaused()) revert Paused();
                      int reserves = getReserves();
                      if (reserves >= 0 && uint(reserves) >= targetReserves) revert NotForSale();
                      // Note: Re-entrancy can skip the reserves check above on a second buyCollateral call.
                      doTransferIn(baseToken, msg.sender, baseAmount);
                      uint collateralAmount = quoteCollateral(asset, baseAmount);
                      if (collateralAmount < minAmount) revert TooMuchSlippage();
                      if (collateralAmount > getCollateralReserves(asset)) revert InsufficientReserves();
                      // Note: Pre-transfer hook can re-enter buyCollateral with a stale collateral ERC20 balance.
                      //  Assets should not be listed which allow re-entry from pre-transfer now, as too much collateral could be bought.
                      //  This is also a problem if quoteCollateral derives its discount from the collateral ERC20 balance.
                      doTransferOut(asset, recipient, safe128(collateralAmount));
                      emit BuyCollateral(msg.sender, asset, baseAmount, collateralAmount);
                  }
                  /**
                   * @notice Gets the quote for a collateral asset in exchange for an amount of base asset
                   * @param asset The collateral asset to get the quote for
                   * @param baseAmount The amount of the base asset to get the quote for
                   * @return The quote in terms of the collateral asset
                   */
                  function quoteCollateral(address asset, uint baseAmount) override public view returns (uint) {
                      AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
                      uint256 assetPrice = getPrice(assetInfo.priceFeed);
                      // Store front discount is derived from the collateral asset's liquidationFactor and storeFrontPriceFactor
                      // discount = storeFrontPriceFactor * (1e18 - liquidationFactor)
                      uint256 discountFactor = mulFactor(storeFrontPriceFactor, FACTOR_SCALE - assetInfo.liquidationFactor);
                      uint256 assetPriceDiscounted = mulFactor(assetPrice, FACTOR_SCALE - discountFactor);
                      uint256 basePrice = getPrice(baseTokenPriceFeed);
                      // # of collateral assets
                      // = (TotalValueOfBaseAmount / DiscountedPriceOfCollateralAsset) * assetScale
                      // = ((basePrice * baseAmount / baseScale) / assetPriceDiscounted) * assetScale
                      return basePrice * baseAmount * assetInfo.scale / assetPriceDiscounted / baseScale;
                  }
                  /**
                   * @notice Withdraws base token reserves if called by the governor
                   * @param to An address of the receiver of withdrawn reserves
                   * @param amount The amount of reserves to be withdrawn from the protocol
                   */
                  function withdrawReserves(address to, uint amount) override external {
                      if (msg.sender != governor) revert Unauthorized();
                      int reserves = getReserves();
                      if (reserves < 0 || amount > unsigned256(reserves)) revert InsufficientReserves();
                      doTransferOut(baseToken, to, amount);
                      emit WithdrawReserves(to, amount);
                  }
                  /**
                   * @notice Sets Comet's ERC20 allowance of an asset for a manager
                   * @dev Only callable by governor
                   * @dev Note: Setting the `asset` as Comet's address will allow the manager
                   * to withdraw from Comet's Comet balance
                   * @param asset The asset that the manager will gain approval of
                   * @param manager The account which will be allowed or disallowed
                   * @param amount The amount of an asset to approve
                   */
                  function approveThis(address manager, address asset, uint amount) override external {
                      if (msg.sender != governor) revert Unauthorized();
                      ERC20(asset).approve(manager, amount);
                  }
                  /**
                   * @notice Get the total number of tokens in circulation
                   * @dev Note: uses updated interest indices to calculate
                   * @return The supply of tokens
                   **/
                  function totalSupply() override external view returns (uint256) {
                      (uint64 baseSupplyIndex_, ) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
                      return presentValueSupply(baseSupplyIndex_, totalSupplyBase);
                  }
                  /**
                   * @notice Get the total amount of debt
                   * @dev Note: uses updated interest indices to calculate
                   * @return The amount of debt
                   **/
                  function totalBorrow() override external view returns (uint256) {
                      (, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
                      return presentValueBorrow(baseBorrowIndex_, totalBorrowBase);
                  }
                  /**
                   * @notice Query the current positive base balance of an account or zero
                   * @dev Note: uses updated interest indices to calculate
                   * @param account The account whose balance to query
                   * @return The present day base balance magnitude of the account, if positive
                   */
                  function balanceOf(address account) override public view returns (uint256) {
                      (uint64 baseSupplyIndex_, ) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
                      int104 principal = userBasic[account].principal;
                      return principal > 0 ? presentValueSupply(baseSupplyIndex_, unsigned104(principal)) : 0;
                  }
                  /**
                   * @notice Query the current negative base balance of an account or zero
                   * @dev Note: uses updated interest indices to calculate
                   * @param account The account whose balance to query
                   * @return The present day base balance magnitude of the account, if negative
                   */
                  function borrowBalanceOf(address account) override public view returns (uint256) {
                      (, uint64 baseBorrowIndex_) = accruedInterestIndices(getNowInternal() - lastAccrualTime);
                      int104 principal = userBasic[account].principal;
                      return principal < 0 ? presentValueBorrow(baseBorrowIndex_, unsigned104(-principal)) : 0;
                  }
                  /**
                   * @notice Fallback to calling the extension delegate for everything else
                   */
                  fallback() external payable {
                      address delegate = extensionDelegate;
                      assembly {
                          calldatacopy(0, 0, calldatasize())
                          let result := delegatecall(gas(), delegate, 0, calldatasize(), 0, 0)
                          returndatacopy(0, 0, returndatasize())
                          switch result
                          case 0 { revert(0, returndatasize()) }
                          default { return(0, returndatasize()) }
                      }
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.15;
              import "./CometCore.sol";
              /**
               * @title Compound's Comet Main Interface (without Ext)
               * @notice An efficient monolithic money market protocol
               * @author Compound
               */
              abstract contract CometMainInterface is CometCore {
                  error Absurd();
                  error AlreadyInitialized();
                  error BadAsset();
                  error BadDecimals();
                  error BadDiscount();
                  error BadMinimum();
                  error BadPrice();
                  error BorrowTooSmall();
                  error BorrowCFTooLarge();
                  error InsufficientReserves();
                  error LiquidateCFTooLarge();
                  error NoSelfTransfer();
                  error NotCollateralized();
                  error NotForSale();
                  error NotLiquidatable();
                  error Paused();
                  error SupplyCapExceeded();
                  error TimestampTooLarge();
                  error TooManyAssets();
                  error TooMuchSlippage();
                  error TransferInFailed();
                  error TransferOutFailed();
                  error Unauthorized();
                  event Supply(address indexed from, address indexed dst, uint amount);
                  event Transfer(address indexed from, address indexed to, uint amount);
                  event Withdraw(address indexed src, address indexed to, uint amount);
                  event SupplyCollateral(address indexed from, address indexed dst, address indexed asset, uint amount);
                  event TransferCollateral(address indexed from, address indexed to, address indexed asset, uint amount);
                  event WithdrawCollateral(address indexed src, address indexed to, address indexed asset, uint amount);
                  /// @notice Event emitted when a borrow position is absorbed by the protocol
                  event AbsorbDebt(address indexed absorber, address indexed borrower, uint basePaidOut, uint usdValue);
                  /// @notice Event emitted when a user's collateral is absorbed by the protocol
                  event AbsorbCollateral(address indexed absorber, address indexed borrower, address indexed asset, uint collateralAbsorbed, uint usdValue);
                  /// @notice Event emitted when a collateral asset is purchased from the protocol
                  event BuyCollateral(address indexed buyer, address indexed asset, uint baseAmount, uint collateralAmount);
                  /// @notice Event emitted when an action is paused/unpaused
                  event PauseAction(bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused);
                  /// @notice Event emitted when reserves are withdrawn by the governor
                  event WithdrawReserves(address indexed to, uint amount);
                  function supply(address asset, uint amount) virtual external;
                  function supplyTo(address dst, address asset, uint amount) virtual external;
                  function supplyFrom(address from, address dst, address asset, uint amount) virtual external;
                  function transfer(address dst, uint amount) virtual external returns (bool);
                  function transferFrom(address src, address dst, uint amount) virtual external returns (bool);
                  function transferAsset(address dst, address asset, uint amount) virtual external;
                  function transferAssetFrom(address src, address dst, address asset, uint amount) virtual external;
                  function withdraw(address asset, uint amount) virtual external;
                  function withdrawTo(address to, address asset, uint amount) virtual external;
                  function withdrawFrom(address src, address to, address asset, uint amount) virtual external;
                  function approveThis(address manager, address asset, uint amount) virtual external;
                  function withdrawReserves(address to, uint amount) virtual external;
                  function absorb(address absorber, address[] calldata accounts) virtual external;
                  function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) virtual external;
                  function quoteCollateral(address asset, uint baseAmount) virtual public view returns (uint);
                  function getAssetInfo(uint8 i) virtual public view returns (AssetInfo memory);
                  function getAssetInfoByAddress(address asset) virtual public view returns (AssetInfo memory);
                  function getCollateralReserves(address asset) virtual public view returns (uint);
                  function getReserves() virtual public view returns (int);
                  function getPrice(address priceFeed) virtual public view returns (uint);
                  function isBorrowCollateralized(address account) virtual public view returns (bool);
                  function isLiquidatable(address account) virtual public view returns (bool);
                  function totalSupply() virtual external view returns (uint256);
                  function totalBorrow() virtual external view returns (uint256);
                  function balanceOf(address owner) virtual public view returns (uint256);
                  function borrowBalanceOf(address account) virtual public view returns (uint256);
                  function pause(bool supplyPaused, bool transferPaused, bool withdrawPaused, bool absorbPaused, bool buyPaused) virtual external;
                  function isSupplyPaused() virtual public view returns (bool);
                  function isTransferPaused() virtual public view returns (bool);
                  function isWithdrawPaused() virtual public view returns (bool);
                  function isAbsorbPaused() virtual public view returns (bool);
                  function isBuyPaused() virtual public view returns (bool);
                  function accrueAccount(address account) virtual external;
                  function getSupplyRate(uint utilization) virtual public view returns (uint64);
                  function getBorrowRate(uint utilization) virtual public view returns (uint64);
                  function getUtilization() virtual public view returns (uint);
                  function governor() virtual external view returns (address);
                  function pauseGuardian() virtual external view returns (address);
                  function baseToken() virtual external view returns (address);
                  function baseTokenPriceFeed() virtual external view returns (address);
                  function extensionDelegate() virtual external view returns (address);
                  /// @dev uint64
                  function supplyKink() virtual external view returns (uint);
                  /// @dev uint64
                  function supplyPerSecondInterestRateSlopeLow() virtual external view returns (uint);
                  /// @dev uint64
                  function supplyPerSecondInterestRateSlopeHigh() virtual external view returns (uint);
                  /// @dev uint64
                  function supplyPerSecondInterestRateBase() virtual external view returns (uint);
                  /// @dev uint64
                  function borrowKink() virtual external view returns (uint);
                  /// @dev uint64
                  function borrowPerSecondInterestRateSlopeLow() virtual external view returns (uint);
                  /// @dev uint64
                  function borrowPerSecondInterestRateSlopeHigh() virtual external view returns (uint);
                  /// @dev uint64
                  function borrowPerSecondInterestRateBase() virtual external view returns (uint);
                  /// @dev uint64
                  function storeFrontPriceFactor() virtual external view returns (uint);
                  /// @dev uint64
                  function baseScale() virtual external view returns (uint);
                  /// @dev uint64
                  function trackingIndexScale() virtual external view returns (uint);
                  /// @dev uint64
                  function baseTrackingSupplySpeed() virtual external view returns (uint);
                  /// @dev uint64
                  function baseTrackingBorrowSpeed() virtual external view returns (uint);
                  /// @dev uint104
                  function baseMinForRewards() virtual external view returns (uint);
                  /// @dev uint104
                  function baseBorrowMin() virtual external view returns (uint);
                  /// @dev uint104
                  function targetReserves() virtual external view returns (uint);
                  function numAssets() virtual external view returns (uint8);
                  function decimals() virtual external view returns (uint8);
                  function initializeStorage() virtual external;
              }// SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.15;
              /**
               * @title ERC 20 Token Standard Interface
               *  https://eips.ethereum.org/EIPS/eip-20
               */
              interface ERC20 {
                  function name() external view returns (string memory);
                  function symbol() external view returns (string memory);
                  function decimals() external view returns (uint8);
                  /**
                    * @notice Get the total number of tokens in circulation
                    * @return The supply of tokens
                    */
                  function totalSupply() external view returns (uint256);
                  /**
                   * @notice Gets the balance of the specified address
                   * @param owner The address from which the balance will be retrieved
                   * @return The balance
                   */
                  function balanceOf(address owner) external view returns (uint256);
                  /**
                    * @notice Transfer `amount` tokens from `msg.sender` to `dst`
                    * @param dst The address of the destination account
                    * @param amount The number of tokens to transfer
                    * @return Whether or not the transfer succeeded
                    */
                  function transfer(address dst, uint256 amount) external returns (bool);
                  /**
                    * @notice Transfer `amount` tokens from `src` to `dst`
                    * @param src The address of the source account
                    * @param dst The address of the destination account
                    * @param amount The number of tokens to transfer
                    * @return Whether or not the transfer succeeded
                    */
                  function transferFrom(address src, address dst, uint256 amount) external returns (bool);
                  /**
                    * @notice Approve `spender` to transfer up to `amount` from `src`
                    * @dev This will overwrite the approval amount for `spender`
                    *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
                    * @param spender The address of the account which may transfer tokens
                    * @param amount The number of tokens that are approved (-1 means infinite)
                    * @return Whether or not the approval succeeded
                    */
                  function approve(address spender, uint256 amount) external returns (bool);
                  /**
                    * @notice Get the current allowance from `owner` for `spender`
                    * @param owner The address of the account which owns the tokens to be spent
                    * @param spender The address of the account which may transfer tokens
                    * @return The number of tokens allowed to be spent (-1 means infinite)
                    */
                  function allowance(address owner, address spender) external view returns (uint256);
                  event Transfer(address indexed from, address indexed to, uint256 amount);
                  event Approval(address indexed owner, address indexed spender, uint256 amount);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              interface AggregatorV3Interface {
                function decimals() external view returns (uint8);
                function description() external view returns (string memory);
                function version() external view returns (uint256);
                // getRoundData and latestRoundData should both raise "No data present"
                // if they do not have data to report, instead of returning unset values
                // which could be misinterpreted as actual reported values.
                function getRoundData(uint80 _roundId)
                  external
                  view
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  );
                function latestRoundData()
                  external
                  view
                  returns (
                    uint80 roundId,
                    int256 answer,
                    uint256 startedAt,
                    uint256 updatedAt,
                    uint80 answeredInRound
                  );
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.15;
              import "./CometConfiguration.sol";
              import "./CometStorage.sol";
              import "./CometMath.sol";
              import "./vendor/@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
              abstract contract CometCore is CometConfiguration, CometStorage, CometMath {
                  struct AssetInfo {
                      uint8 offset;
                      address asset;
                      address priceFeed;
                      uint64 scale;
                      uint64 borrowCollateralFactor;
                      uint64 liquidateCollateralFactor;
                      uint64 liquidationFactor;
                      uint128 supplyCap;
                  }
                  /** Internal constants **/
                  /// @dev The max number of assets this contract is hardcoded to support
                  ///  Do not change this variable without updating all the fields throughout the contract,
                  //    including the size of UserBasic.assetsIn and corresponding integer conversions.
                  uint8 internal constant MAX_ASSETS = 15;
                  /// @dev The max number of decimals base token can have
                  ///  Note this cannot just be increased arbitrarily.
                  uint8 internal constant MAX_BASE_DECIMALS = 18;
                  /// @dev The max value for a collateral factor (1)
                  uint64 internal constant MAX_COLLATERAL_FACTOR = FACTOR_SCALE;
                  /// @dev Offsets for specific actions in the pause flag bit array
                  uint8 internal constant PAUSE_SUPPLY_OFFSET = 0;
                  uint8 internal constant PAUSE_TRANSFER_OFFSET = 1;
                  uint8 internal constant PAUSE_WITHDRAW_OFFSET = 2;
                  uint8 internal constant PAUSE_ABSORB_OFFSET = 3;
                  uint8 internal constant PAUSE_BUY_OFFSET = 4;
                  /// @dev The decimals required for a price feed
                  uint8 internal constant PRICE_FEED_DECIMALS = 8;
                  /// @dev 365 days * 24 hours * 60 minutes * 60 seconds
                  uint64 internal constant SECONDS_PER_YEAR = 31_536_000;
                  /// @dev The scale for base tracking accrual
                  uint64 internal constant BASE_ACCRUAL_SCALE = 1e6;
                  /// @dev The scale for base index (depends on time/rate scales, not base token)
                  uint64 internal constant BASE_INDEX_SCALE = 1e15;
                  /// @dev The scale for prices (in USD)
                  uint64 internal constant PRICE_SCALE = uint64(10 ** PRICE_FEED_DECIMALS);
                  /// @dev The scale for factors
                  uint64 internal constant FACTOR_SCALE = 1e18;
                  /**
                   * @notice Determine if the manager has permission to act on behalf of the owner
                   * @param owner The owner account
                   * @param manager The manager account
                   * @return Whether or not the manager has permission
                   */
                  function hasPermission(address owner, address manager) public view returns (bool) {
                      return owner == manager || isAllowed[owner][manager];
                  }
                  /**
                   * @dev The positive present supply balance if positive or the negative borrow balance if negative
                   */
                  function presentValue(int104 principalValue_) internal view returns (int256) {
                      if (principalValue_ >= 0) {
                          return signed256(presentValueSupply(baseSupplyIndex, uint104(principalValue_)));
                      } else {
                          return -signed256(presentValueBorrow(baseBorrowIndex, uint104(-principalValue_)));
                      }
                  }
                  /**
                   * @dev The principal amount projected forward by the supply index
                   */
                  function presentValueSupply(uint64 baseSupplyIndex_, uint104 principalValue_) internal pure returns (uint256) {
                      return uint256(principalValue_) * baseSupplyIndex_ / BASE_INDEX_SCALE;
                  }
                  /**
                   * @dev The principal amount projected forward by the borrow index
                   */
                  function presentValueBorrow(uint64 baseBorrowIndex_, uint104 principalValue_) internal pure returns (uint256) {
                      return uint256(principalValue_) * baseBorrowIndex_ / BASE_INDEX_SCALE;
                  }
                  /**
                   * @dev The positive principal if positive or the negative principal if negative
                   */
                  function principalValue(int256 presentValue_) internal view returns (int104) {
                      if (presentValue_ >= 0) {
                          return signed104(principalValueSupply(baseSupplyIndex, uint256(presentValue_)));
                      } else {
                          return -signed104(principalValueBorrow(baseBorrowIndex, uint256(-presentValue_)));
                      }
                  }
                  /**
                   * @dev The present value projected backward by the supply index (rounded down)
                   *  Note: This will overflow (revert) at 2^104/1e18=~20 trillion principal for assets with 18 decimals.
                   */
                  function principalValueSupply(uint64 baseSupplyIndex_, uint256 presentValue_) internal pure returns (uint104) {
                      return safe104((presentValue_ * BASE_INDEX_SCALE) / baseSupplyIndex_);
                  }
                  /**
                   * @dev The present value projected backward by the borrow index (rounded up)
                   *  Note: This will overflow (revert) at 2^104/1e18=~20 trillion principal for assets with 18 decimals.
                   */
                  function principalValueBorrow(uint64 baseBorrowIndex_, uint256 presentValue_) internal pure returns (uint104) {
                      return safe104((presentValue_ * BASE_INDEX_SCALE + baseBorrowIndex_ - 1) / baseBorrowIndex_);
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.15;
              /**
               * @title Compound's Comet Configuration Interface
               * @author Compound
               */
              contract CometConfiguration {
                  struct ExtConfiguration {
                      bytes32 name32;
                      bytes32 symbol32;
                  }
                  struct Configuration {
                      address governor;
                      address pauseGuardian;
                      address baseToken;
                      address baseTokenPriceFeed;
                      address extensionDelegate;
                      uint64 supplyKink;
                      uint64 supplyPerYearInterestRateSlopeLow;
                      uint64 supplyPerYearInterestRateSlopeHigh;
                      uint64 supplyPerYearInterestRateBase;
                      uint64 borrowKink;
                      uint64 borrowPerYearInterestRateSlopeLow;
                      uint64 borrowPerYearInterestRateSlopeHigh;
                      uint64 borrowPerYearInterestRateBase;
                      uint64 storeFrontPriceFactor;
                      uint64 trackingIndexScale;
                      uint64 baseTrackingSupplySpeed;
                      uint64 baseTrackingBorrowSpeed;
                      uint104 baseMinForRewards;
                      uint104 baseBorrowMin;
                      uint104 targetReserves;
                      AssetConfig[] assetConfigs;
                  }
                  struct AssetConfig {
                      address asset;
                      address priceFeed;
                      uint8 decimals;
                      uint64 borrowCollateralFactor;
                      uint64 liquidateCollateralFactor;
                      uint64 liquidationFactor;
                      uint128 supplyCap;
                  }
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.15;
              /**
               * @title Compound's Comet Storage Interface
               * @dev Versions can enforce append-only storage slots via inheritance.
               * @author Compound
               */
              contract CometStorage {
                  // 512 bits total = 2 slots
                  struct TotalsBasic {
                      // 1st slot
                      uint64 baseSupplyIndex;
                      uint64 baseBorrowIndex;
                      uint64 trackingSupplyIndex;
                      uint64 trackingBorrowIndex;
                      // 2nd slot
                      uint104 totalSupplyBase;
                      uint104 totalBorrowBase;
                      uint40 lastAccrualTime;
                      uint8 pauseFlags;
                  }
                  struct TotalsCollateral {
                      uint128 totalSupplyAsset;
                      uint128 _reserved;
                  }
                  struct UserBasic {
                      int104 principal;
                      uint64 baseTrackingIndex;
                      uint64 baseTrackingAccrued;
                      uint16 assetsIn;
                      uint8 _reserved;
                  }
                  struct UserCollateral {
                      uint128 balance;
                      uint128 _reserved;
                  }
                  struct LiquidatorPoints {
                      uint32 numAbsorbs;
                      uint64 numAbsorbed;
                      uint128 approxSpend;
                      uint32 _reserved;
                  }
                  /// @dev Aggregate variables tracked for the entire market
                  uint64 internal baseSupplyIndex;
                  uint64 internal baseBorrowIndex;
                  uint64 internal trackingSupplyIndex;
                  uint64 internal trackingBorrowIndex;
                  uint104 internal totalSupplyBase;
                  uint104 internal totalBorrowBase;
                  uint40 internal lastAccrualTime;
                  uint8 internal pauseFlags;
                  /// @notice Aggregate variables tracked for each collateral asset
                  mapping(address => TotalsCollateral) public totalsCollateral;
                  /// @notice Mapping of users to accounts which may be permitted to manage the user account
                  mapping(address => mapping(address => bool)) public isAllowed;
                  /// @notice The next expected nonce for an address, for validating authorizations via signature
                  mapping(address => uint) public userNonce;
                  /// @notice Mapping of users to base principal and other basic data
                  mapping(address => UserBasic) public userBasic;
                  /// @notice Mapping of users to collateral data per collateral asset
                  mapping(address => mapping(address => UserCollateral)) public userCollateral;
                  /// @notice Mapping of magic liquidator points
                  mapping(address => LiquidatorPoints) public liquidatorPoints;
              }
              // SPDX-License-Identifier: BUSL-1.1
              pragma solidity 0.8.15;
              /**
               * @title Compound's Comet Math Contract
               * @dev Pure math functions
               * @author Compound
               */
              contract CometMath {
                  /** Custom errors **/
                  error InvalidUInt64();
                  error InvalidUInt104();
                  error InvalidUInt128();
                  error InvalidInt104();
                  error InvalidInt256();
                  error NegativeNumber();
                  function safe64(uint n) internal pure returns (uint64) {
                      if (n > type(uint64).max) revert InvalidUInt64();
                      return uint64(n);
                  }
                  function safe104(uint n) internal pure returns (uint104) {
                      if (n > type(uint104).max) revert InvalidUInt104();
                      return uint104(n);
                  }
                  function safe128(uint n) internal pure returns (uint128) {
                      if (n > type(uint128).max) revert InvalidUInt128();
                      return uint128(n);
                  }
                  function signed104(uint104 n) internal pure returns (int104) {
                      if (n > uint104(type(int104).max)) revert InvalidInt104();
                      return int104(n);
                  }
                  function signed256(uint256 n) internal pure returns (int256) {
                      if (n > uint256(type(int256).max)) revert InvalidInt256();
                      return int256(n);
                  }
                  function unsigned104(int104 n) internal pure returns (uint104) {
                      if (n < 0) revert NegativeNumber();
                      return uint104(n);
                  }
                  function unsigned256(int256 n) internal pure returns (uint256) {
                      if (n < 0) revert NegativeNumber();
                      return uint256(n);
                  }
                  function toUInt8(bool x) internal pure returns (uint8) {
                      return x ? 1 : 0;
                  }
                  function toBool(uint8 x) internal pure returns (bool) {
                      return x != 0;
                  }
              }