ETH Price: $2,062.80 (+0.80%)

Transaction Decoder

Block:
11669650 at Jan-17-2021 01:10:21 AM +UTC
Transaction Fee:
0.007256589550579203 ETH $14.97
Gas Used:
35,391 Gas / 205.040534333 Gwei

Account State Difference:

  Address   Before After State Difference Code
0x00000000...438691c04
0x0e2F2F9d...54d5BC841
0 Eth
Nonce: 0
0 Eth
Nonce: 0
From: 0 To: 0
(Spark Pool)
28.921883081946428033 Eth28.929139671497007236 Eth0.007256589550579203
0x6C0439f6...ad43d08a4
93.117447208122414188 Eth
Nonce: 3022
93.110190618571834985 Eth
Nonce: 3023
0.007256589550579203

Execution Trace

0xa6d96991becb2fb6b3fc1820a7764802c44664ee.67424255( )
  • 0x0000000000438f975cbde76d5fdd1aa76be46577.67424255( )
    • UniswapV2Pair.CALL( )
    • DelegateCallProxyManyToOne.f8b2cb4f( )
      • ManyToOneImplementationHolder.STATICCALL( )
      • IndexPool.getBalance( token=0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9 ) => ( 2898787248090090857040 )
      • GasToken2.freeUpTo( value=1 ) => ( freed=1 )
        • 0x0e2f2f9df9fda05b41ddb9d67596cbd54d5bc841.CALL( )
          • GasToken2.SELFDESTRUCT( )
            File 1 of 5: UniswapV2Pair
            // File: contracts/interfaces/IUniswapV2Pair.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Pair {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                function MINIMUM_LIQUIDITY() external pure returns (uint);
                function factory() external view returns (address);
                function token0() external view returns (address);
                function token1() external view returns (address);
                function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                function price0CumulativeLast() external view returns (uint);
                function price1CumulativeLast() external view returns (uint);
                function kLast() external view returns (uint);
            
                function mint(address to) external returns (uint liquidity);
                function burn(address to) external returns (uint amount0, uint amount1);
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                function skim(address to) external;
                function sync() external;
            
                function initialize(address, address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2ERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2ERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            }
            
            // File: contracts/libraries/SafeMath.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
            
            library SafeMath {
                function add(uint x, uint y) internal pure returns (uint z) {
                    require((z = x + y) >= x, 'ds-math-add-overflow');
                }
            
                function sub(uint x, uint y) internal pure returns (uint z) {
                    require((z = x - y) <= x, 'ds-math-sub-underflow');
                }
            
                function mul(uint x, uint y) internal pure returns (uint z) {
                    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
                }
            }
            
            // File: contracts/UniswapV2ERC20.sol
            
            pragma solidity =0.5.16;
            
            
            
            contract UniswapV2ERC20 is IUniswapV2ERC20 {
                using SafeMath for uint;
            
                string public constant name = 'Uniswap V2';
                string public constant symbol = 'UNI-V2';
                uint8 public constant decimals = 18;
                uint  public totalSupply;
                mapping(address => uint) public balanceOf;
                mapping(address => mapping(address => uint)) public allowance;
            
                bytes32 public DOMAIN_SEPARATOR;
                // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                mapping(address => uint) public nonces;
            
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                constructor() public {
                    uint chainId;
                    assembly {
                        chainId := chainid
                    }
                    DOMAIN_SEPARATOR = keccak256(
                        abi.encode(
                            keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                            keccak256(bytes(name)),
                            keccak256(bytes('1')),
                            chainId,
                            address(this)
                        )
                    );
                }
            
                function _mint(address to, uint value) internal {
                    totalSupply = totalSupply.add(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(address(0), to, value);
                }
            
                function _burn(address from, uint value) internal {
                    balanceOf[from] = balanceOf[from].sub(value);
                    totalSupply = totalSupply.sub(value);
                    emit Transfer(from, address(0), value);
                }
            
                function _approve(address owner, address spender, uint value) private {
                    allowance[owner][spender] = value;
                    emit Approval(owner, spender, value);
                }
            
                function _transfer(address from, address to, uint value) private {
                    balanceOf[from] = balanceOf[from].sub(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(from, to, value);
                }
            
                function approve(address spender, uint value) external returns (bool) {
                    _approve(msg.sender, spender, value);
                    return true;
                }
            
                function transfer(address to, uint value) external returns (bool) {
                    _transfer(msg.sender, to, value);
                    return true;
                }
            
                function transferFrom(address from, address to, uint value) external returns (bool) {
                    if (allowance[from][msg.sender] != uint(-1)) {
                        allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
                    }
                    _transfer(from, to, value);
                    return true;
                }
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
                    require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
                    bytes32 digest = keccak256(
                        abi.encodePacked(
                            '\x19\x01',
                            DOMAIN_SEPARATOR,
                            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                        )
                    );
                    address recoveredAddress = ecrecover(digest, v, r, s);
                    require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
                    _approve(owner, spender, value);
                }
            }
            
            // File: contracts/libraries/Math.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing various math operations
            
            library Math {
                function min(uint x, uint y) internal pure returns (uint z) {
                    z = x < y ? x : y;
                }
            
                // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
                function sqrt(uint y) internal pure returns (uint z) {
                    if (y > 3) {
                        z = y;
                        uint x = y / 2 + 1;
                        while (x < z) {
                            z = x;
                            x = (y / x + x) / 2;
                        }
                    } else if (y != 0) {
                        z = 1;
                    }
                }
            }
            
            // File: contracts/libraries/UQ112x112.sol
            
            pragma solidity =0.5.16;
            
            // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
            
            // range: [0, 2**112 - 1]
            // resolution: 1 / 2**112
            
            library UQ112x112 {
                uint224 constant Q112 = 2**112;
            
                // encode a uint112 as a UQ112x112
                function encode(uint112 y) internal pure returns (uint224 z) {
                    z = uint224(y) * Q112; // never overflows
                }
            
                // divide a UQ112x112 by a uint112, returning a UQ112x112
                function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
                    z = x / uint224(y);
                }
            }
            
            // File: contracts/interfaces/IERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function decimals() external view returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            }
            
            // File: contracts/interfaces/IUniswapV2Factory.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Factory {
                event PairCreated(address indexed token0, address indexed token1, address pair, uint);
            
                function feeTo() external view returns (address);
                function feeToSetter() external view returns (address);
            
                function getPair(address tokenA, address tokenB) external view returns (address pair);
                function allPairs(uint) external view returns (address pair);
                function allPairsLength() external view returns (uint);
            
                function createPair(address tokenA, address tokenB) external returns (address pair);
            
                function setFeeTo(address) external;
                function setFeeToSetter(address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2Callee.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Callee {
                function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
            }
            
            // File: contracts/UniswapV2Pair.sol
            
            pragma solidity =0.5.16;
            
            
            
            
            
            
            
            
            contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
                using SafeMath  for uint;
                using UQ112x112 for uint224;
            
                uint public constant MINIMUM_LIQUIDITY = 10**3;
                bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
            
                address public factory;
                address public token0;
                address public token1;
            
                uint112 private reserve0;           // uses single storage slot, accessible via getReserves
                uint112 private reserve1;           // uses single storage slot, accessible via getReserves
                uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
            
                uint public price0CumulativeLast;
                uint public price1CumulativeLast;
                uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
            
                uint private unlocked = 1;
                modifier lock() {
                    require(unlocked == 1, 'UniswapV2: LOCKED');
                    unlocked = 0;
                    _;
                    unlocked = 1;
                }
            
                function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
                    _reserve0 = reserve0;
                    _reserve1 = reserve1;
                    _blockTimestampLast = blockTimestampLast;
                }
            
                function _safeTransfer(address token, address to, uint value) private {
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
                }
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                constructor() public {
                    factory = msg.sender;
                }
            
                // called once by the factory at time of deployment
                function initialize(address _token0, address _token1) external {
                    require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
                    token0 = _token0;
                    token1 = _token1;
                }
            
                // update reserves and, on the first call per block, price accumulators
                function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
                    require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
                    uint32 blockTimestamp = uint32(block.timestamp % 2**32);
                    uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
                    if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                        // * never overflows, and + overflow is desired
                        price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                        price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
                    }
                    reserve0 = uint112(balance0);
                    reserve1 = uint112(balance1);
                    blockTimestampLast = blockTimestamp;
                    emit Sync(reserve0, reserve1);
                }
            
                // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
                function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
                    address feeTo = IUniswapV2Factory(factory).feeTo();
                    feeOn = feeTo != address(0);
                    uint _kLast = kLast; // gas savings
                    if (feeOn) {
                        if (_kLast != 0) {
                            uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                            uint rootKLast = Math.sqrt(_kLast);
                            if (rootK > rootKLast) {
                                uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                                uint denominator = rootK.mul(5).add(rootKLast);
                                uint liquidity = numerator / denominator;
                                if (liquidity > 0) _mint(feeTo, liquidity);
                            }
                        }
                    } else if (_kLast != 0) {
                        kLast = 0;
                    }
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function mint(address to) external lock returns (uint liquidity) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    uint balance0 = IERC20(token0).balanceOf(address(this));
                    uint balance1 = IERC20(token1).balanceOf(address(this));
                    uint amount0 = balance0.sub(_reserve0);
                    uint amount1 = balance1.sub(_reserve1);
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    if (_totalSupply == 0) {
                        liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                       _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
                    } else {
                        liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
                    }
                    require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
                    _mint(to, liquidity);
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Mint(msg.sender, amount0, amount1);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function burn(address to) external lock returns (uint amount0, uint amount1) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    address _token0 = token0;                                // gas savings
                    address _token1 = token1;                                // gas savings
                    uint balance0 = IERC20(_token0).balanceOf(address(this));
                    uint balance1 = IERC20(_token1).balanceOf(address(this));
                    uint liquidity = balanceOf[address(this)];
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
                    amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
                    require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
                    _burn(address(this), liquidity);
                    _safeTransfer(_token0, to, amount0);
                    _safeTransfer(_token1, to, amount1);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Burn(msg.sender, amount0, amount1, to);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
                    require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
            
                    uint balance0;
                    uint balance1;
                    { // scope for _token{0,1}, avoids stack too deep errors
                    address _token0 = token0;
                    address _token1 = token1;
                    require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
                    if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
                    if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
                    if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
                    }
                    uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
                    uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
                    require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
                    { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
                    uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
                    uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
                    require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
                    }
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
                }
            
                // force balances to match reserves
                function skim(address to) external lock {
                    address _token0 = token0; // gas savings
                    address _token1 = token1; // gas savings
                    _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
                    _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
                }
            
                // force reserves to match balances
                function sync() external lock {
                    _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
                }
            }

            File 2 of 5: DelegateCallProxyManyToOne
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            /**
             * @dev Because we use the code hashes of the proxy contracts for proxy address
             * derivation, it is important that other packages have access to the correct
             * values when they import the salt library.
             */
            library CodeHashes {
              bytes32 internal constant ONE_TO_ONE_CODEHASH = 0x63d9f7b5931b69188c8f6b806606f25892f1bb17b7f7e966fe3a32c04493aee4;
              bytes32 internal constant MANY_TO_ONE_CODEHASH = 0xa035ad05a1663db5bfd455b99cd7c6ac6bd49269738458eda140e0b78ed53f79;
              bytes32 internal constant IMPLEMENTATION_HOLDER_CODEHASH = 0x11c370493a726a0ffa93d42b399ad046f1b5a543b6e72f1a64f1488dc1c58f2c;
            }// SPDX-License-Identifier: GPL-3.0
            pragma solidity =0.6.12;
            /* ==========  External Libraries  ========== */
            import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
            import { Address } from "@openzeppelin/contracts/utils/Address.sol";
            import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
            /* ==========  Proxy Contracts  ========== */
            import "./ManyToOneImplementationHolder.sol";
            import { DelegateCallProxyManyToOne } from "./DelegateCallProxyManyToOne.sol";
            import { DelegateCallProxyOneToOne } from "./DelegateCallProxyOneToOne.sol";
            /* ==========  Internal Libraries  ========== */
            import { SaltyLib as Salty } from "./SaltyLib.sol";
            import { CodeHashes } from "./CodeHashes.sol";
            /* ==========  Inheritance  ========== */
            import "./interfaces/IDelegateCallProxyManager.sol";
            /**
             * @dev Contract that manages deployment and upgrades of delegatecall proxies.
             *
             * An implementation identifier can be created on the proxy manager which is
             * used to specify the logic address for a particular contract type, and to
             * upgrade the implementation as needed.
             *
             * ====== Proxy Types ======
             * A one-to-one proxy is a single proxy contract with an upgradeable implementation
             * address.
             *
             * A many-to-one proxy is a single upgradeable implementation address that may be
             * used by many proxy contracts.
             *
             * ====== Access Control ======
             * The proxy manager has a single address as its owner.
             *
             * The owner is the sole account with the following permissions:
             * - Create new many-to-one implementations
             * - Create new one-to-one proxies
             * - Modify the implementation address of existing proxies
             * - Lock proxies
             * - Designate approved deployers
             * - Remove approved deployers
             * - Modify the owner address
             *
             * Approved deployers may only deploy many-to-one proxies.
             *
             * ====== Upgrades ======
             * Proxies can be upgraded by the owner if they are not locked.
             *
             * Many-to-one proxy implementations are upgraded by calling the holder contract
             * for the implementation ID being upgraded.
             * One-to-one proxies are upgraded by calling the proxy contract directly.
             *
             * The owner can lock a one-to-one proxy or many-to-one implementation ID so that
             * it becomes impossible to upgrade.
             */
            contract DelegateCallProxyManager is Ownable, IDelegateCallProxyManager {
            /* ==========  Events  ========== */
              event DeploymentApprovalGranted(address deployer);
              event DeploymentApprovalRevoked(address deployer);
              event ManyToOne_ImplementationCreated(
                bytes32 implementationID,
                address implementationAddress
              );
              event ManyToOne_ImplementationUpdated(
                bytes32 implementationID,
                address implementationAddress
              );
              event ManyToOne_ImplementationLocked(bytes32 implementationID);
              event ManyToOne_ProxyDeployed(
                bytes32 implementationID,
                address proxyAddress
              );
              event OneToOne_ProxyDeployed(
                address proxyAddress,
                address implementationAddress
              );
              event OneToOne_ImplementationUpdated(
                address proxyAddress,
                address implementationAddress
              );
              event OneToOne_ImplementationLocked(address proxyAddress);
            /* ==========  Storage  ========== */
              // Addresses allowed to deploy many-to-one proxies.
              mapping(address => bool) internal _approvedDeployers;
              // Maps implementation holders to their implementation IDs.
              mapping(bytes32 => address) internal _implementationHolders;
              // Maps implementation holders & proxy addresses to bool stating if they are locked.
              mapping(address => bool) internal _lockedImplementations;
              // Temporary value used in the many-to-one proxy constructor.
              // The many-to-one proxy contract is deployed with create2 and
              // uses static initialization code for simple address derivation,
              // so it calls the proxy manager in the constructor to get this
              // address in order to save it as an immutable in the bytecode.
              address internal _implementationHolder;
            /* ==========  Modifiers  ========== */
              modifier onlyApprovedDeployer {
                address sender = _msgSender();
                require(_approvedDeployers[sender] || sender == owner(), "ERR_NOT_APPROVED");
                _;
              }
            /* ==========  Constructor  ========== */
              constructor() public Ownable() {}
            /* ==========  Access Control  ========== */
              /**
               * @dev Allows `deployer` to deploy many-to-one proxies.
               */
              function approveDeployer(address deployer) external override onlyOwner {
                _approvedDeployers[deployer] = true;
                emit DeploymentApprovalGranted(deployer);
              }
              /**
               * @dev Prevents `deployer` from deploying many-to-one proxies.
               */
              function revokeDeployerApproval(address deployer) external override onlyOwner {
                _approvedDeployers[deployer] = false;
                emit DeploymentApprovalRevoked(deployer);
              }
            /* ==========  Implementation Management  ========== */
              /**
               * @dev Creates a many-to-one proxy relationship.
               *
               * Deploys an implementation holder contract which stores the
               * implementation address for many proxies. The implementation
               * address can be updated on the holder to change the runtime
               * code used by all its proxies.
               *
               * @param implementationID ID for the implementation, used to identify the
               * proxies that use it. Also used as the salt in the create2 call when
               * deploying the implementation holder contract.
               * @param implementation Address with the runtime code the proxies
               * should use.
               */
              function createManyToOneProxyRelationship(
                bytes32 implementationID,
                address implementation
              )
                external
                override
                onlyOwner
              {
                // Deploy the implementation holder contract with the implementation
                // ID as the create2 salt.
                address implementationHolder = Create2.deploy(
                  0,
                  implementationID,
                  type(ManyToOneImplementationHolder).creationCode
                );
                // Store the implementation holder address
                _implementationHolders[implementationID] = implementationHolder;
                // Sets the implementation address.
                _setImplementation(implementationHolder, implementation);
                emit ManyToOne_ImplementationCreated(
                  implementationID,
                  implementation
                );
              }
              /**
               * @dev Lock the current implementation for `implementationID` so that it can never be upgraded again.
               */
              function lockImplementationManyToOne(bytes32 implementationID) external override onlyOwner {
                // Read the implementation holder address from storage.
                address implementationHolder = _implementationHolders[implementationID];
                // Verify that the implementation exists.
                require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
                _lockedImplementations[implementationHolder] = true;
                emit ManyToOne_ImplementationLocked(implementationID);
              }
              /**
               * @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
               */
              function lockImplementationOneToOne(address proxyAddress) external override onlyOwner {
                _lockedImplementations[proxyAddress] = true;
                emit OneToOne_ImplementationLocked(proxyAddress);
              }
              /**
               * @dev Updates the implementation address for a many-to-one
               * proxy relationship.
               *
               * @param implementationID Identifier for the implementation.
               * @param implementation Address with the runtime code the proxies
               * should use.
               */
              function setImplementationAddressManyToOne(
                bytes32 implementationID,
                address implementation
              )
                external
                override
                onlyOwner
              {
                // Read the implementation holder address from storage.
                address implementationHolder = _implementationHolders[implementationID];
                // Verify that the implementation exists.
                require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
                // Verify implementation is not locked
                require(!_lockedImplementations[implementationHolder], "ERR_IMPLEMENTATION_LOCKED");
                // Set the implementation address
                _setImplementation(implementationHolder, implementation);
                emit ManyToOne_ImplementationUpdated(
                  implementationID,
                  implementation
                );
              }
              /**
               * @dev Updates the implementation address for a one-to-one proxy.
               *
               * Note: This could work for many-to-one as well if the caller
               * provides the implementation holder address in place of the
               * proxy address, as they use the same access control and update
               * mechanism.
               *
               * @param proxyAddress Address of the deployed proxy
               * @param implementation Address with the runtime code for
               * the proxy to use.
               */
              function setImplementationAddressOneToOne(
                address proxyAddress,
                address implementation
              )
                external
                override
                onlyOwner
              {
                // Verify proxy is not locked
                require(!_lockedImplementations[proxyAddress], "ERR_IMPLEMENTATION_LOCKED");
                // Set the implementation address
                _setImplementation(proxyAddress, implementation);
                emit OneToOne_ImplementationUpdated(proxyAddress, implementation);
              }
            /* ==========  Proxy Deployment  ========== */
              /**
               * @dev Deploy a proxy contract with a one-to-one relationship
               * with its implementation.
               *
               * The proxy will have its own implementation address which can
               * be updated by the proxy manager.
               *
               * @param suppliedSalt Salt provided by the account requesting deployment.
               * @param implementation Address of the contract with the runtime
               * code that the proxy should use.
               */
              function deployProxyOneToOne(
                bytes32 suppliedSalt,
                address implementation
              )
                external
                override
                onlyOwner
                returns(address proxyAddress)
              {
                // Derive the create2 salt from the deployment requester's address
                // and the requester-supplied salt.
                bytes32 salt = Salty.deriveOneToOneSalt(_msgSender(), suppliedSalt);
                // Deploy the proxy
                proxyAddress = Create2.deploy(
                  0,
                  salt,
                  type(DelegateCallProxyOneToOne).creationCode
                );
                // Set the implementation address on the new proxy.
                _setImplementation(proxyAddress, implementation);
                emit OneToOne_ProxyDeployed(proxyAddress, implementation);
              }
              /**
               * @dev Deploy a proxy with a many-to-one relationship with its implemenation.
               *
               * The proxy will call the implementation holder for every transaction to
               * determine the address to use in calls.
               *
               * @param implementationID Identifier for the proxy's implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function deployProxyManyToOne(bytes32 implementationID, bytes32 suppliedSalt)
                external
                override
                onlyApprovedDeployer
                returns(address proxyAddress)
              {
                // Read the implementation holder address from storage.
                address implementationHolder = _implementationHolders[implementationID];
                // Verify that the implementation exists.
                require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
                // Derive the create2 salt from the deployment requester's address, the
                // implementation ID and the requester-supplied salt.
                bytes32 salt = Salty.deriveManyToOneSalt(
                  _msgSender(),
                  implementationID,
                  suppliedSalt
                );
                // Set the implementation holder address in storage so the proxy
                // constructor can query it.
                _implementationHolder = implementationHolder;
                // Deploy the proxy, which will query the implementation holder address
                // and save it as an immutable in the contract bytecode.
                proxyAddress = Create2.deploy(
                  0,
                  salt,
                  type(DelegateCallProxyManyToOne).creationCode
                );
                // Remove the address from temporary storage.
                _implementationHolder = address(0);
                emit ManyToOne_ProxyDeployed(
                  implementationID,
                  proxyAddress
                );
              }
            /* ==========  Queries  ========== */
              /**
               * @dev Returns a boolean stating whether `implementationID` is locked.
               */
              function isImplementationLocked(bytes32 implementationID) external override view returns (bool) {
                // Read the implementation holder address from storage.
                address implementationHolder = _implementationHolders[implementationID];
                // Verify that the implementation exists.
                require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
                return _lockedImplementations[implementationHolder];
              }
              /**
               * @dev Returns a boolean stating whether `proxyAddress` is locked.
               */
              function isImplementationLocked(address proxyAddress) external override view returns (bool) {
                return _lockedImplementations[proxyAddress];
              }
              /**
               * @dev Returns a boolean stating whether `deployer` is allowed to deploy many-to-one
               * proxies.
               */
              function isApprovedDeployer(address deployer) external override view returns (bool) {
                return _approvedDeployers[deployer];
              }
              /**
               * @dev Queries the temporary storage value `_implementationHolder`.
               * This is used in the constructor of the many-to-one proxy contract
               * so that the create2 address is static (adding constructor arguments
               * would change the codehash) and the implementation holder can be
               * stored as a constant.
               */
              function getImplementationHolder()
                external
                override
                view
                returns (address)
              {
                return _implementationHolder;
              }
              /**
               * @dev Returns the address of the implementation holder contract
               * for `implementationID`.
               */
              function getImplementationHolder(
                bytes32 implementationID
              )
                external
                override
                view
                returns (address)
              {
                return _implementationHolders[implementationID];
              }
              /**
               * @dev Computes the create2 address for a one-to-one proxy requested
               * by `originator` using `suppliedSalt`.
               *
               * @param originator Address of the account requesting deployment.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function computeProxyAddressOneToOne(
                address originator,
                bytes32 suppliedSalt
              )
                external
                override
                view
                returns (address)
              {
                bytes32 salt = Salty.deriveOneToOneSalt(originator, suppliedSalt);
                return Create2.computeAddress(salt, CodeHashes.ONE_TO_ONE_CODEHASH);
              }
              /**
               * @dev Computes the create2 address for a many-to-one proxy for the
               * implementation `implementationID` requested by `originator` using
               * `suppliedSalt`.
               *
               * @param originator Address of the account requesting deployment.
               * @param implementationID The identifier for the contract implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
              */
              function computeProxyAddressManyToOne(
                address originator,
                bytes32 implementationID,
                bytes32 suppliedSalt
              )
                external
                override
                view
                returns (address)
              {
                bytes32 salt = Salty.deriveManyToOneSalt(
                  originator,
                  implementationID,
                  suppliedSalt
                );
                return Create2.computeAddress(salt, CodeHashes.MANY_TO_ONE_CODEHASH);
              }
              /**
               * @dev Computes the create2 address of the implementation holder
               * for `implementationID`.
               *
               * @param implementationID The identifier for the contract implementation.
              */
              function computeHolderAddressManyToOne(bytes32 implementationID)
                public
                override
                view
                returns (address)
              {
                return Create2.computeAddress(
                  implementationID,
                  CodeHashes.IMPLEMENTATION_HOLDER_CODEHASH
                );
              }
            /* ==========  Internal Functions  ========== */
              /**
               * @dev Sets the implementation address for a one-to-one proxy or
               * many-to-one implementation holder. Both use the same access
               * control and update mechanism, which is the receipt of a call
               * from the proxy manager with the abi-encoded implementation address
               * as the only calldata.
               *
               * Note: Verifies that the implementation address is a contract.
               *
               * @param proxyOrHolder Address of the one-to-one proxy or
               * many-to-one implementation holder contract.
               * @param implementation Address of the contract with the runtime
               * code that the proxy or proxies should use.
               */
              function _setImplementation(
                address proxyOrHolder,
                address implementation
              ) internal {
                // Verify that the implementation address is a contract.
                require(Address.isContract(implementation), "ERR_NOT_CONTRACT");
                // Set the implementation address on the contract.
                // solium-disable-next-line security/no-low-level-calls
                (bool success,) = proxyOrHolder.call(abi.encode(implementation));
                require(success, "ERR_SET_ADDRESS_REVERT");
              }
            }// SPDX-License-Identifier: MIT
            pragma solidity ^0.6.0;
            /**
             * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
             * `CREATE2` can be used to compute in advance the address where a smart
             * contract will be deployed, which allows for interesting new mechanisms known
             * as 'counterfactual interactions'.
             *
             * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
             * information.
             */
            library Create2 {
                /**
                 * @dev Deploys a contract using `CREATE2`. The address where the contract
                 * will be deployed can be known in advance via {computeAddress}.
                 *
                 * The bytecode for a contract can be obtained from Solidity with
                 * `type(contractName).creationCode`.
                 *
                 * Requirements:
                 *
                 * - `bytecode` must not be empty.
                 * - `salt` must have not been used for `bytecode` already.
                 * - the factory must have a balance of at least `amount`.
                 * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
                 */
                function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address) {
                    address addr;
                    require(address(this).balance >= amount, "Create2: insufficient balance");
                    require(bytecode.length != 0, "Create2: bytecode length is zero");
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
                    }
                    require(addr != address(0), "Create2: Failed on deploy");
                    return addr;
                }
                /**
                 * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
                 * `bytecodeHash` or `salt` will result in a new destination address.
                 */
                function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
                    return computeAddress(salt, bytecodeHash, address(this));
                }
                /**
                 * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
                 * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
                 */
                function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address) {
                    bytes32 _data = keccak256(
                        abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)
                    );
                    return address(uint256(_data));
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.6.2;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies in extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { size := extcodesize(account) }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                    (bool success, ) = recipient.call{ value: amount }("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain`call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                    return _functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    return _functionCallWithValue(target, data, value, errorMessage);
                }
                function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                    require(isContract(target), "Address: call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.6.0;
            import "../GSN/Context.sol";
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            contract Ownable is Context {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor () internal {
                    address msgSender = _msgSender();
                    _owner = msgSender;
                    emit OwnershipTransferred(address(0), msgSender);
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    require(_owner == _msgSender(), "Ownable: caller is not the owner");
                    _;
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    emit OwnershipTransferred(_owner, address(0));
                    _owner = address(0);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    emit OwnershipTransferred(_owner, newOwner);
                    _owner = newOwner;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.6.0;
            /*
             * @dev Provides information about the current execution context, including the
             * sender of the transaction and its data. While these are generally available
             * via msg.sender and msg.data, they should not be accessed in such a direct
             * manner, since when dealing with GSN meta-transactions the account sending and
             * paying for execution may not be the actual sender (as far as an application
             * is concerned).
             *
             * This contract is only required for intermediate, library-like contracts.
             */
            abstract contract Context {
                function _msgSender() internal view virtual returns (address payable) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes memory) {
                    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                    return msg.data;
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity =0.6.12;
            /**
             * @dev The ManyToOneImplementationHolder stores an upgradeable implementation address
             * in storage, which many-to-one proxies query at execution time to determine which
             * contract to delegate to.
             *
             * The manager can upgrade the implementation address by calling the holder with the
             * abi-encoded address as calldata. If any other account calls the implementation holder,
             * it will return the implementation address.
             *
             * This pattern was inspired by the DharmaUpgradeBeacon from 0age
             * https://github.com/dharma-eng/dharma-smart-wallet/blob/master/contracts/upgradeability/smart-wallet/DharmaUpgradeBeacon.sol
             */
            contract ManyToOneImplementationHolder {
            /* ---  Storage  --- */
              address internal immutable _manager;
              address internal _implementation;
            /* ---  Constructor  --- */
              constructor() public {
                _manager = msg.sender;
              }
              /**
               * @dev Fallback function for the contract.
               *
               * Used by proxies to read the implementation address and used
               * by the proxy manager to set the implementation address.
               *
               * If called by the owner, reads the implementation address from
               * calldata (must be abi-encoded) and stores it to the first slot.
               *
               * Otherwise, returns the stored implementation address.
               */
              fallback() external payable {
                if (msg.sender != _manager) {
                  assembly {
                    mstore(0, sload(0))
                    return(0, 32)
                  }
                }
                assembly { sstore(0, calldataload(0)) }
              }
            }// SPDX-License-Identifier: GPL-3.0
            pragma solidity =0.6.12;
            import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
            /**
             * @dev Proxy contract which uses an implementation address shared with many
             * other proxies.
             *
             * An implementation holder contract stores the upgradeable implementation address.
             * When the proxy is called, it queries the implementation address from the holder
             * contract and delegatecalls the returned address, forwarding the received calldata
             * and ether.
             *
             * Note: This contract does not verify that the implementation
             * address is a valid delegation target. The manager must perform
             * this safety check before updating the implementation on the holder.
             */
            contract DelegateCallProxyManyToOne is Proxy {
            /* ==========  Constants  ========== */
              // Address that stores the implementation address.
              address internal immutable _implementationHolder;
            /* ==========  Constructor  ========== */
              constructor() public {
                // Calls the sender rather than receiving the address in the constructor
                // arguments so that the address is computable using create2.
                _implementationHolder = ProxyDeployer(msg.sender).getImplementationHolder();
              }
            /* ==========  Internal Overrides  ========== */
              /**
               * @dev Queries the implementation address from the implementation holder.
               */
              function _implementation() internal override view returns (address) {
                // Queries the implementation address from the implementation holder.
                (bool success, bytes memory data) = _implementationHolder.staticcall("");
                require(success, string(data));
                address implementation = abi.decode((data), (address));
                require(implementation != address(0), "ERR_NULL_IMPLEMENTATION");
                return implementation;
              }
            }
            interface ProxyDeployer {
              function getImplementationHolder() external view returns (address);
            }// SPDX-License-Identifier: MIT
            pragma solidity ^0.6.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 internall call site, it will return directly to the external caller.
                 */
                function _delegate(address implementation) internal {
                    // solhint-disable-next-line no-inline-assembly
                    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 virtual view 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 {
                    _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 () payable external {
                    _fallback();
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                 * is empty.
                 */
                receive () payable external {
                    _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: GPL-3.0
            pragma solidity =0.6.12;
            import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
            /**
             * @dev Upgradeable delegatecall proxy for a single contract.
             *
             * This proxy stores an implementation address which can be upgraded by the proxy manager.
             *
             * To upgrade the implementation, the manager calls the proxy with the abi encoded implementation address.
             *
             * If any other account calls the proxy, it will delegatecall the implementation address with the received
             * calldata and ether. If the call succeeds, it will return with the received returndata.
             * If it reverts, it will revert with the received revert data.
             *
             * Note: The storage slot for the implementation address is:
             * `bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)`
             * This slot must not be used by the implementation contract.
             *
             * Note: This contract does not verify that the implementation address is a valid delegation target.
             * The manager must perform this safety check.
             */
            contract DelegateCallProxyOneToOne is Proxy {
            /* ==========  Constants  ========== */
              address internal immutable _manager;
            /* ==========  Constructor  ========== */
              constructor() public {
                _manager = msg.sender ;
              }
            /* ==========  Internal Overrides  ========== */
              /**
               * @dev Reads the implementation address from storage.
               */
              function _implementation() internal override view returns (address) {
                address implementation;
                assembly {
                  implementation := sload(
                    // bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)
                    0x913bd12b32b36f36cedaeb6e043912bceb97022755958701789d3108d33a045a
                  )
                }
                return implementation;
              }
              /**
                * @dev Hook that is called before falling back to the implementation.
                *
                * Checks if the call is from the owner.
                * If it is, reads the abi-encoded implementation address from calldata and stores
                * it at the slot `bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)`,
                * then returns with no data.
                * If it is not, continues execution with the fallback function.
                */
              function _beforeFallback() internal override {
                if (msg.sender != _manager) {
                  super._beforeFallback();
                } else {
                  assembly {
                    sstore(
                      // bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)
                      0x913bd12b32b36f36cedaeb6e043912bceb97022755958701789d3108d33a045a,
                      calldataload(0)
                    )
                    return(0, 0)
                  }
                }
              }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            /* ---  External Libraries  --- */
            import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
            /* ---  Proxy Contracts  --- */
            import { CodeHashes } from "./CodeHashes.sol";
            /**
             * @dev Library for computing create2 salts and addresses for proxies
             * deployed by `DelegateCallProxyManager`.
             *
             * Because the proxy factory is meant to be used by multiple contracts,
             * we use a salt derivation pattern that includes the address of the
             * contract that requested the proxy deployment, a salt provided by that
             * contract and the implementation ID used (for many-to-one proxies only).
             */
            library SaltyLib {
            /* ---  Salt Derivation  --- */
              /**
               * @dev Derives the create2 salt for a many-to-one proxy.
               *
               * Many different contracts in the Indexed framework may use the
               * same implementation contract, and they all use the same init
               * code, so we derive the actual create2 salt from a combination
               * of the implementation ID, the address of the account requesting
               * deployment and the user-supplied salt.
               *
               * @param originator Address of the account requesting deployment.
               * @param implementationID The identifier for the contract implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function deriveManyToOneSalt(
                address originator,
                bytes32 implementationID,
                bytes32 suppliedSalt
              )
                internal
                pure
                returns (bytes32)
              {
                return keccak256(
                  abi.encodePacked(
                    originator,
                    implementationID,
                    suppliedSalt
                  )
                );
              }
              /**
               * @dev Derives the create2 salt for a one-to-one proxy.
               *
               * @param originator Address of the account requesting deployment.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function deriveOneToOneSalt(
                address originator,
                bytes32 suppliedSalt
              )
                internal
                pure
                returns (bytes32)
              {
                return keccak256(abi.encodePacked(originator, suppliedSalt));
              }
            /* ---  Address Derivation  --- */
              /**
               * @dev Computes the create2 address for a one-to-one proxy deployed
               * by `deployer` (the factory) when requested by `originator` using
               * `suppliedSalt`.
               *
               * @param deployer Address of the proxy factory.
               * @param originator Address of the account requesting deployment.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function computeProxyAddressOneToOne(
                address deployer,
                address originator,
                bytes32 suppliedSalt
              )
                internal
                pure
                returns (address)
              {
                bytes32 salt = deriveOneToOneSalt(originator, suppliedSalt);
                return Create2.computeAddress(salt, CodeHashes.ONE_TO_ONE_CODEHASH, deployer);
              }
              /**
               * @dev Computes the create2 address for a many-to-one proxy for the
               * implementation `implementationID` deployed by `deployer` (the factory)
               * when requested by `originator` using `suppliedSalt`.
               *
               * @param deployer Address of the proxy factory.
               * @param originator Address of the account requesting deployment.
               * @param implementationID The identifier for the contract implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
              */
              function computeProxyAddressManyToOne(
                address deployer,
                address originator,
                bytes32 implementationID,
                bytes32 suppliedSalt
              )
                internal
                pure
                returns (address)
              {
                bytes32 salt = deriveManyToOneSalt(
                  originator,
                  implementationID,
                  suppliedSalt
                );
                return Create2.computeAddress(salt, CodeHashes.MANY_TO_ONE_CODEHASH, deployer);
              }
              /**
               * @dev Computes the create2 address of the implementation holder
               * for `implementationID`.
               *
               * @param deployer Address of the proxy factory.
               * @param implementationID The identifier for the contract implementation.
              */
              function computeHolderAddressManyToOne(
                address deployer,
                bytes32 implementationID
              )
                internal
                pure
                returns (address)
              {
                return Create2.computeAddress(
                  implementationID,
                  CodeHashes.IMPLEMENTATION_HOLDER_CODEHASH,
                  deployer
                );
              }
            }// SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            /**
             * @dev Contract that manages deployment and upgrades of delegatecall proxies.
             *
             * An implementation identifier can be created on the proxy manager which is
             * used to specify the logic address for a particular contract type, and to
             * upgrade the implementation as needed.
             *
             * A one-to-one proxy is a single proxy contract with an upgradeable implementation
             * address.
             *
             * A many-to-one proxy is a single upgradeable implementation address that may be
             * used by many proxy contracts.
             */
            interface IDelegateCallProxyManager {
            /* ==========  Events  ========== */
              event DeploymentApprovalGranted(address deployer);
              event DeploymentApprovalRevoked(address deployer);
              event ManyToOne_ImplementationCreated(
                bytes32 implementationID,
                address implementationAddress
              );
              event ManyToOne_ImplementationUpdated(
                bytes32 implementationID,
                address implementationAddress
              );
              event ManyToOne_ProxyDeployed(
                bytes32 implementationID,
                address proxyAddress
              );
              event OneToOne_ProxyDeployed(
                address proxyAddress,
                address implementationAddress
              );
              event OneToOne_ImplementationUpdated(
                address proxyAddress,
                address implementationAddress
              );
            /* ==========  Controls  ========== */
              /**
               * @dev Allows `deployer` to deploy many-to-one proxies.
               */
              function approveDeployer(address deployer) external;
              /**
               * @dev Prevents `deployer` from deploying many-to-one proxies.
               */
              function revokeDeployerApproval(address deployer) external;
            /* ==========  Implementation Management  ========== */
              /**
               * @dev Creates a many-to-one proxy relationship.
               *
               * Deploys an implementation holder contract which stores the
               * implementation address for many proxies. The implementation
               * address can be updated on the holder to change the runtime
               * code used by all its proxies.
               *
               * @param implementationID ID for the implementation, used to identify the
               * proxies that use it. Also used as the salt in the create2 call when
               * deploying the implementation holder contract.
               * @param implementation Address with the runtime code the proxies
               * should use.
               */
              function createManyToOneProxyRelationship(
                bytes32 implementationID,
                address implementation
              ) external;
              /**
               * @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
               */
              function lockImplementationManyToOne(bytes32 implementationID) external;
              /**
               * @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
               */
              function lockImplementationOneToOne(address proxyAddress) external;
              /**
               * @dev Updates the implementation address for a many-to-one
               * proxy relationship.
               *
               * @param implementationID Identifier for the implementation.
               * @param implementation Address with the runtime code the proxies
               * should use.
               */
              function setImplementationAddressManyToOne(
                bytes32 implementationID,
                address implementation
              ) external;
              /**
               * @dev Updates the implementation address for a one-to-one proxy.
               *
               * Note: This could work for many-to-one as well if the caller
               * provides the implementation holder address in place of the
               * proxy address, as they use the same access control and update
               * mechanism.
               *
               * @param proxyAddress Address of the deployed proxy
               * @param implementation Address with the runtime code for
               * the proxy to use.
               */
              function setImplementationAddressOneToOne(
                address proxyAddress,
                address implementation
              ) external;
            /* ==========  Proxy Deployment  ========== */
              /**
               * @dev Deploy a proxy contract with a one-to-one relationship
               * with its implementation.
               *
               * The proxy will have its own implementation address which can
               * be updated by the proxy manager.
               *
               * @param suppliedSalt Salt provided by the account requesting deployment.
               * @param implementation Address of the contract with the runtime
               * code that the proxy should use.
               */
              function deployProxyOneToOne(
                bytes32 suppliedSalt,
                address implementation
              ) external returns(address proxyAddress);
              /**
               * @dev Deploy a proxy with a many-to-one relationship with its implemenation.
               *
               * The proxy will call the implementation holder for every transaction to
               * determine the address to use in calls.
               *
               * @param implementationID Identifier for the proxy's implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function deployProxyManyToOne(
                bytes32 implementationID,
                bytes32 suppliedSalt
              ) external returns(address proxyAddress);
            /* ==========  Queries  ========== */
              /**
               * @dev Returns a boolean stating whether `implementationID` is locked.
               */
              function isImplementationLocked(bytes32 implementationID) external view returns (bool);
              /**
               * @dev Returns a boolean stating whether `proxyAddress` is locked.
               */
              function isImplementationLocked(address proxyAddress) external view returns (bool);
              /**
               * @dev Returns a boolean stating whether `deployer` is allowed to deploy many-to-one
               * proxies.
               */
              function isApprovedDeployer(address deployer) external view returns (bool);
              /**
               * @dev Queries the temporary storage value `_implementationHolder`.
               * This is used in the constructor of the many-to-one proxy contract
               * so that the create2 address is static (adding constructor arguments
               * would change the codehash) and the implementation holder can be
               * stored as a constant.
               */
              function getImplementationHolder() external view returns (address);
              /**
               * @dev Returns the address of the implementation holder contract
               * for `implementationID`.
               */
              function getImplementationHolder(bytes32 implementationID) external view returns (address);
              /**
               * @dev Computes the create2 address for a one-to-one proxy requested
               * by `originator` using `suppliedSalt`.
               *
               * @param originator Address of the account requesting deployment.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function computeProxyAddressOneToOne(
                address originator,
                bytes32 suppliedSalt
              ) external view returns (address);
              /**
               * @dev Computes the create2 address for a many-to-one proxy for the
               * implementation `implementationID` requested by `originator` using
               * `suppliedSalt`.
               *
               * @param originator Address of the account requesting deployment.
               * @param implementationID The identifier for the contract implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
              */
              function computeProxyAddressManyToOne(
                address originator,
                bytes32 implementationID,
                bytes32 suppliedSalt
              ) external view returns (address);
              /**
               * @dev Computes the create2 address of the implementation holder
               * for `implementationID`.
               *
               * @param implementationID The identifier for the contract implementation.
              */
              function computeHolderAddressManyToOne(bytes32 implementationID) external view returns (address);
            }

            File 3 of 5: ManyToOneImplementationHolder
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            /**
             * @dev Because we use the code hashes of the proxy contracts for proxy address
             * derivation, it is important that other packages have access to the correct
             * values when they import the salt library.
             */
            library CodeHashes {
              bytes32 internal constant ONE_TO_ONE_CODEHASH = 0x63d9f7b5931b69188c8f6b806606f25892f1bb17b7f7e966fe3a32c04493aee4;
              bytes32 internal constant MANY_TO_ONE_CODEHASH = 0xa035ad05a1663db5bfd455b99cd7c6ac6bd49269738458eda140e0b78ed53f79;
              bytes32 internal constant IMPLEMENTATION_HOLDER_CODEHASH = 0x11c370493a726a0ffa93d42b399ad046f1b5a543b6e72f1a64f1488dc1c58f2c;
            }// SPDX-License-Identifier: GPL-3.0
            pragma solidity =0.6.12;
            /* ==========  External Libraries  ========== */
            import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
            import { Address } from "@openzeppelin/contracts/utils/Address.sol";
            import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
            /* ==========  Proxy Contracts  ========== */
            import "./ManyToOneImplementationHolder.sol";
            import { DelegateCallProxyManyToOne } from "./DelegateCallProxyManyToOne.sol";
            import { DelegateCallProxyOneToOne } from "./DelegateCallProxyOneToOne.sol";
            /* ==========  Internal Libraries  ========== */
            import { SaltyLib as Salty } from "./SaltyLib.sol";
            import { CodeHashes } from "./CodeHashes.sol";
            /* ==========  Inheritance  ========== */
            import "./interfaces/IDelegateCallProxyManager.sol";
            /**
             * @dev Contract that manages deployment and upgrades of delegatecall proxies.
             *
             * An implementation identifier can be created on the proxy manager which is
             * used to specify the logic address for a particular contract type, and to
             * upgrade the implementation as needed.
             *
             * ====== Proxy Types ======
             * A one-to-one proxy is a single proxy contract with an upgradeable implementation
             * address.
             *
             * A many-to-one proxy is a single upgradeable implementation address that may be
             * used by many proxy contracts.
             *
             * ====== Access Control ======
             * The proxy manager has a single address as its owner.
             *
             * The owner is the sole account with the following permissions:
             * - Create new many-to-one implementations
             * - Create new one-to-one proxies
             * - Modify the implementation address of existing proxies
             * - Lock proxies
             * - Designate approved deployers
             * - Remove approved deployers
             * - Modify the owner address
             *
             * Approved deployers may only deploy many-to-one proxies.
             *
             * ====== Upgrades ======
             * Proxies can be upgraded by the owner if they are not locked.
             *
             * Many-to-one proxy implementations are upgraded by calling the holder contract
             * for the implementation ID being upgraded.
             * One-to-one proxies are upgraded by calling the proxy contract directly.
             *
             * The owner can lock a one-to-one proxy or many-to-one implementation ID so that
             * it becomes impossible to upgrade.
             */
            contract DelegateCallProxyManager is Ownable, IDelegateCallProxyManager {
            /* ==========  Events  ========== */
              event DeploymentApprovalGranted(address deployer);
              event DeploymentApprovalRevoked(address deployer);
              event ManyToOne_ImplementationCreated(
                bytes32 implementationID,
                address implementationAddress
              );
              event ManyToOne_ImplementationUpdated(
                bytes32 implementationID,
                address implementationAddress
              );
              event ManyToOne_ImplementationLocked(bytes32 implementationID);
              event ManyToOne_ProxyDeployed(
                bytes32 implementationID,
                address proxyAddress
              );
              event OneToOne_ProxyDeployed(
                address proxyAddress,
                address implementationAddress
              );
              event OneToOne_ImplementationUpdated(
                address proxyAddress,
                address implementationAddress
              );
              event OneToOne_ImplementationLocked(address proxyAddress);
            /* ==========  Storage  ========== */
              // Addresses allowed to deploy many-to-one proxies.
              mapping(address => bool) internal _approvedDeployers;
              // Maps implementation holders to their implementation IDs.
              mapping(bytes32 => address) internal _implementationHolders;
              // Maps implementation holders & proxy addresses to bool stating if they are locked.
              mapping(address => bool) internal _lockedImplementations;
              // Temporary value used in the many-to-one proxy constructor.
              // The many-to-one proxy contract is deployed with create2 and
              // uses static initialization code for simple address derivation,
              // so it calls the proxy manager in the constructor to get this
              // address in order to save it as an immutable in the bytecode.
              address internal _implementationHolder;
            /* ==========  Modifiers  ========== */
              modifier onlyApprovedDeployer {
                address sender = _msgSender();
                require(_approvedDeployers[sender] || sender == owner(), "ERR_NOT_APPROVED");
                _;
              }
            /* ==========  Constructor  ========== */
              constructor() public Ownable() {}
            /* ==========  Access Control  ========== */
              /**
               * @dev Allows `deployer` to deploy many-to-one proxies.
               */
              function approveDeployer(address deployer) external override onlyOwner {
                _approvedDeployers[deployer] = true;
                emit DeploymentApprovalGranted(deployer);
              }
              /**
               * @dev Prevents `deployer` from deploying many-to-one proxies.
               */
              function revokeDeployerApproval(address deployer) external override onlyOwner {
                _approvedDeployers[deployer] = false;
                emit DeploymentApprovalRevoked(deployer);
              }
            /* ==========  Implementation Management  ========== */
              /**
               * @dev Creates a many-to-one proxy relationship.
               *
               * Deploys an implementation holder contract which stores the
               * implementation address for many proxies. The implementation
               * address can be updated on the holder to change the runtime
               * code used by all its proxies.
               *
               * @param implementationID ID for the implementation, used to identify the
               * proxies that use it. Also used as the salt in the create2 call when
               * deploying the implementation holder contract.
               * @param implementation Address with the runtime code the proxies
               * should use.
               */
              function createManyToOneProxyRelationship(
                bytes32 implementationID,
                address implementation
              )
                external
                override
                onlyOwner
              {
                // Deploy the implementation holder contract with the implementation
                // ID as the create2 salt.
                address implementationHolder = Create2.deploy(
                  0,
                  implementationID,
                  type(ManyToOneImplementationHolder).creationCode
                );
                // Store the implementation holder address
                _implementationHolders[implementationID] = implementationHolder;
                // Sets the implementation address.
                _setImplementation(implementationHolder, implementation);
                emit ManyToOne_ImplementationCreated(
                  implementationID,
                  implementation
                );
              }
              /**
               * @dev Lock the current implementation for `implementationID` so that it can never be upgraded again.
               */
              function lockImplementationManyToOne(bytes32 implementationID) external override onlyOwner {
                // Read the implementation holder address from storage.
                address implementationHolder = _implementationHolders[implementationID];
                // Verify that the implementation exists.
                require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
                _lockedImplementations[implementationHolder] = true;
                emit ManyToOne_ImplementationLocked(implementationID);
              }
              /**
               * @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
               */
              function lockImplementationOneToOne(address proxyAddress) external override onlyOwner {
                _lockedImplementations[proxyAddress] = true;
                emit OneToOne_ImplementationLocked(proxyAddress);
              }
              /**
               * @dev Updates the implementation address for a many-to-one
               * proxy relationship.
               *
               * @param implementationID Identifier for the implementation.
               * @param implementation Address with the runtime code the proxies
               * should use.
               */
              function setImplementationAddressManyToOne(
                bytes32 implementationID,
                address implementation
              )
                external
                override
                onlyOwner
              {
                // Read the implementation holder address from storage.
                address implementationHolder = _implementationHolders[implementationID];
                // Verify that the implementation exists.
                require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
                // Verify implementation is not locked
                require(!_lockedImplementations[implementationHolder], "ERR_IMPLEMENTATION_LOCKED");
                // Set the implementation address
                _setImplementation(implementationHolder, implementation);
                emit ManyToOne_ImplementationUpdated(
                  implementationID,
                  implementation
                );
              }
              /**
               * @dev Updates the implementation address for a one-to-one proxy.
               *
               * Note: This could work for many-to-one as well if the caller
               * provides the implementation holder address in place of the
               * proxy address, as they use the same access control and update
               * mechanism.
               *
               * @param proxyAddress Address of the deployed proxy
               * @param implementation Address with the runtime code for
               * the proxy to use.
               */
              function setImplementationAddressOneToOne(
                address proxyAddress,
                address implementation
              )
                external
                override
                onlyOwner
              {
                // Verify proxy is not locked
                require(!_lockedImplementations[proxyAddress], "ERR_IMPLEMENTATION_LOCKED");
                // Set the implementation address
                _setImplementation(proxyAddress, implementation);
                emit OneToOne_ImplementationUpdated(proxyAddress, implementation);
              }
            /* ==========  Proxy Deployment  ========== */
              /**
               * @dev Deploy a proxy contract with a one-to-one relationship
               * with its implementation.
               *
               * The proxy will have its own implementation address which can
               * be updated by the proxy manager.
               *
               * @param suppliedSalt Salt provided by the account requesting deployment.
               * @param implementation Address of the contract with the runtime
               * code that the proxy should use.
               */
              function deployProxyOneToOne(
                bytes32 suppliedSalt,
                address implementation
              )
                external
                override
                onlyOwner
                returns(address proxyAddress)
              {
                // Derive the create2 salt from the deployment requester's address
                // and the requester-supplied salt.
                bytes32 salt = Salty.deriveOneToOneSalt(_msgSender(), suppliedSalt);
                // Deploy the proxy
                proxyAddress = Create2.deploy(
                  0,
                  salt,
                  type(DelegateCallProxyOneToOne).creationCode
                );
                // Set the implementation address on the new proxy.
                _setImplementation(proxyAddress, implementation);
                emit OneToOne_ProxyDeployed(proxyAddress, implementation);
              }
              /**
               * @dev Deploy a proxy with a many-to-one relationship with its implemenation.
               *
               * The proxy will call the implementation holder for every transaction to
               * determine the address to use in calls.
               *
               * @param implementationID Identifier for the proxy's implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function deployProxyManyToOne(bytes32 implementationID, bytes32 suppliedSalt)
                external
                override
                onlyApprovedDeployer
                returns(address proxyAddress)
              {
                // Read the implementation holder address from storage.
                address implementationHolder = _implementationHolders[implementationID];
                // Verify that the implementation exists.
                require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
                // Derive the create2 salt from the deployment requester's address, the
                // implementation ID and the requester-supplied salt.
                bytes32 salt = Salty.deriveManyToOneSalt(
                  _msgSender(),
                  implementationID,
                  suppliedSalt
                );
                // Set the implementation holder address in storage so the proxy
                // constructor can query it.
                _implementationHolder = implementationHolder;
                // Deploy the proxy, which will query the implementation holder address
                // and save it as an immutable in the contract bytecode.
                proxyAddress = Create2.deploy(
                  0,
                  salt,
                  type(DelegateCallProxyManyToOne).creationCode
                );
                // Remove the address from temporary storage.
                _implementationHolder = address(0);
                emit ManyToOne_ProxyDeployed(
                  implementationID,
                  proxyAddress
                );
              }
            /* ==========  Queries  ========== */
              /**
               * @dev Returns a boolean stating whether `implementationID` is locked.
               */
              function isImplementationLocked(bytes32 implementationID) external override view returns (bool) {
                // Read the implementation holder address from storage.
                address implementationHolder = _implementationHolders[implementationID];
                // Verify that the implementation exists.
                require(implementationHolder != address(0), "ERR_IMPLEMENTATION_ID");
                return _lockedImplementations[implementationHolder];
              }
              /**
               * @dev Returns a boolean stating whether `proxyAddress` is locked.
               */
              function isImplementationLocked(address proxyAddress) external override view returns (bool) {
                return _lockedImplementations[proxyAddress];
              }
              /**
               * @dev Returns a boolean stating whether `deployer` is allowed to deploy many-to-one
               * proxies.
               */
              function isApprovedDeployer(address deployer) external override view returns (bool) {
                return _approvedDeployers[deployer];
              }
              /**
               * @dev Queries the temporary storage value `_implementationHolder`.
               * This is used in the constructor of the many-to-one proxy contract
               * so that the create2 address is static (adding constructor arguments
               * would change the codehash) and the implementation holder can be
               * stored as a constant.
               */
              function getImplementationHolder()
                external
                override
                view
                returns (address)
              {
                return _implementationHolder;
              }
              /**
               * @dev Returns the address of the implementation holder contract
               * for `implementationID`.
               */
              function getImplementationHolder(
                bytes32 implementationID
              )
                external
                override
                view
                returns (address)
              {
                return _implementationHolders[implementationID];
              }
              /**
               * @dev Computes the create2 address for a one-to-one proxy requested
               * by `originator` using `suppliedSalt`.
               *
               * @param originator Address of the account requesting deployment.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function computeProxyAddressOneToOne(
                address originator,
                bytes32 suppliedSalt
              )
                external
                override
                view
                returns (address)
              {
                bytes32 salt = Salty.deriveOneToOneSalt(originator, suppliedSalt);
                return Create2.computeAddress(salt, CodeHashes.ONE_TO_ONE_CODEHASH);
              }
              /**
               * @dev Computes the create2 address for a many-to-one proxy for the
               * implementation `implementationID` requested by `originator` using
               * `suppliedSalt`.
               *
               * @param originator Address of the account requesting deployment.
               * @param implementationID The identifier for the contract implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
              */
              function computeProxyAddressManyToOne(
                address originator,
                bytes32 implementationID,
                bytes32 suppliedSalt
              )
                external
                override
                view
                returns (address)
              {
                bytes32 salt = Salty.deriveManyToOneSalt(
                  originator,
                  implementationID,
                  suppliedSalt
                );
                return Create2.computeAddress(salt, CodeHashes.MANY_TO_ONE_CODEHASH);
              }
              /**
               * @dev Computes the create2 address of the implementation holder
               * for `implementationID`.
               *
               * @param implementationID The identifier for the contract implementation.
              */
              function computeHolderAddressManyToOne(bytes32 implementationID)
                public
                override
                view
                returns (address)
              {
                return Create2.computeAddress(
                  implementationID,
                  CodeHashes.IMPLEMENTATION_HOLDER_CODEHASH
                );
              }
            /* ==========  Internal Functions  ========== */
              /**
               * @dev Sets the implementation address for a one-to-one proxy or
               * many-to-one implementation holder. Both use the same access
               * control and update mechanism, which is the receipt of a call
               * from the proxy manager with the abi-encoded implementation address
               * as the only calldata.
               *
               * Note: Verifies that the implementation address is a contract.
               *
               * @param proxyOrHolder Address of the one-to-one proxy or
               * many-to-one implementation holder contract.
               * @param implementation Address of the contract with the runtime
               * code that the proxy or proxies should use.
               */
              function _setImplementation(
                address proxyOrHolder,
                address implementation
              ) internal {
                // Verify that the implementation address is a contract.
                require(Address.isContract(implementation), "ERR_NOT_CONTRACT");
                // Set the implementation address on the contract.
                // solium-disable-next-line security/no-low-level-calls
                (bool success,) = proxyOrHolder.call(abi.encode(implementation));
                require(success, "ERR_SET_ADDRESS_REVERT");
              }
            }// SPDX-License-Identifier: MIT
            pragma solidity ^0.6.0;
            /**
             * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
             * `CREATE2` can be used to compute in advance the address where a smart
             * contract will be deployed, which allows for interesting new mechanisms known
             * as 'counterfactual interactions'.
             *
             * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
             * information.
             */
            library Create2 {
                /**
                 * @dev Deploys a contract using `CREATE2`. The address where the contract
                 * will be deployed can be known in advance via {computeAddress}.
                 *
                 * The bytecode for a contract can be obtained from Solidity with
                 * `type(contractName).creationCode`.
                 *
                 * Requirements:
                 *
                 * - `bytecode` must not be empty.
                 * - `salt` must have not been used for `bytecode` already.
                 * - the factory must have a balance of at least `amount`.
                 * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
                 */
                function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address) {
                    address addr;
                    require(address(this).balance >= amount, "Create2: insufficient balance");
                    require(bytecode.length != 0, "Create2: bytecode length is zero");
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
                    }
                    require(addr != address(0), "Create2: Failed on deploy");
                    return addr;
                }
                /**
                 * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
                 * `bytecodeHash` or `salt` will result in a new destination address.
                 */
                function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
                    return computeAddress(salt, bytecodeHash, address(this));
                }
                /**
                 * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
                 * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
                 */
                function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address) {
                    bytes32 _data = keccak256(
                        abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash)
                    );
                    return address(uint256(_data));
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.6.2;
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // This method relies in extcodesize, which returns 0 for contracts in
                    // construction, since the code is only stored at the end of the
                    // constructor execution.
                    uint256 size;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { size := extcodesize(account) }
                    return size > 0;
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                    (bool success, ) = recipient.call{ value: amount }("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain`call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCall(target, data, "Address: low-level call failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                    return _functionCallWithValue(target, data, 0, errorMessage);
                }
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    return _functionCallWithValue(target, data, value, errorMessage);
                }
                function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                    require(isContract(target), "Address: call to non-contract");
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.6.0;
            import "../GSN/Context.sol";
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            contract Ownable is Context {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor () internal {
                    address msgSender = _msgSender();
                    _owner = msgSender;
                    emit OwnershipTransferred(address(0), msgSender);
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view returns (address) {
                    return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    require(_owner == _msgSender(), "Ownable: caller is not the owner");
                    _;
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    emit OwnershipTransferred(_owner, address(0));
                    _owner = address(0);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    emit OwnershipTransferred(_owner, newOwner);
                    _owner = newOwner;
                }
            }
            // SPDX-License-Identifier: MIT
            pragma solidity ^0.6.0;
            /*
             * @dev Provides information about the current execution context, including the
             * sender of the transaction and its data. While these are generally available
             * via msg.sender and msg.data, they should not be accessed in such a direct
             * manner, since when dealing with GSN meta-transactions the account sending and
             * paying for execution may not be the actual sender (as far as an application
             * is concerned).
             *
             * This contract is only required for intermediate, library-like contracts.
             */
            abstract contract Context {
                function _msgSender() internal view virtual returns (address payable) {
                    return msg.sender;
                }
                function _msgData() internal view virtual returns (bytes memory) {
                    this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                    return msg.data;
                }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity =0.6.12;
            /**
             * @dev The ManyToOneImplementationHolder stores an upgradeable implementation address
             * in storage, which many-to-one proxies query at execution time to determine which
             * contract to delegate to.
             *
             * The manager can upgrade the implementation address by calling the holder with the
             * abi-encoded address as calldata. If any other account calls the implementation holder,
             * it will return the implementation address.
             *
             * This pattern was inspired by the DharmaUpgradeBeacon from 0age
             * https://github.com/dharma-eng/dharma-smart-wallet/blob/master/contracts/upgradeability/smart-wallet/DharmaUpgradeBeacon.sol
             */
            contract ManyToOneImplementationHolder {
            /* ---  Storage  --- */
              address internal immutable _manager;
              address internal _implementation;
            /* ---  Constructor  --- */
              constructor() public {
                _manager = msg.sender;
              }
              /**
               * @dev Fallback function for the contract.
               *
               * Used by proxies to read the implementation address and used
               * by the proxy manager to set the implementation address.
               *
               * If called by the owner, reads the implementation address from
               * calldata (must be abi-encoded) and stores it to the first slot.
               *
               * Otherwise, returns the stored implementation address.
               */
              fallback() external payable {
                if (msg.sender != _manager) {
                  assembly {
                    mstore(0, sload(0))
                    return(0, 32)
                  }
                }
                assembly { sstore(0, calldataload(0)) }
              }
            }// SPDX-License-Identifier: GPL-3.0
            pragma solidity =0.6.12;
            import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
            /**
             * @dev Proxy contract which uses an implementation address shared with many
             * other proxies.
             *
             * An implementation holder contract stores the upgradeable implementation address.
             * When the proxy is called, it queries the implementation address from the holder
             * contract and delegatecalls the returned address, forwarding the received calldata
             * and ether.
             *
             * Note: This contract does not verify that the implementation
             * address is a valid delegation target. The manager must perform
             * this safety check before updating the implementation on the holder.
             */
            contract DelegateCallProxyManyToOne is Proxy {
            /* ==========  Constants  ========== */
              // Address that stores the implementation address.
              address internal immutable _implementationHolder;
            /* ==========  Constructor  ========== */
              constructor() public {
                // Calls the sender rather than receiving the address in the constructor
                // arguments so that the address is computable using create2.
                _implementationHolder = ProxyDeployer(msg.sender).getImplementationHolder();
              }
            /* ==========  Internal Overrides  ========== */
              /**
               * @dev Queries the implementation address from the implementation holder.
               */
              function _implementation() internal override view returns (address) {
                // Queries the implementation address from the implementation holder.
                (bool success, bytes memory data) = _implementationHolder.staticcall("");
                require(success, string(data));
                address implementation = abi.decode((data), (address));
                require(implementation != address(0), "ERR_NULL_IMPLEMENTATION");
                return implementation;
              }
            }
            interface ProxyDeployer {
              function getImplementationHolder() external view returns (address);
            }// SPDX-License-Identifier: MIT
            pragma solidity ^0.6.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 internall call site, it will return directly to the external caller.
                 */
                function _delegate(address implementation) internal {
                    // solhint-disable-next-line no-inline-assembly
                    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 virtual view 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 {
                    _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 () payable external {
                    _fallback();
                }
                /**
                 * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
                 * is empty.
                 */
                receive () payable external {
                    _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: GPL-3.0
            pragma solidity =0.6.12;
            import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol";
            /**
             * @dev Upgradeable delegatecall proxy for a single contract.
             *
             * This proxy stores an implementation address which can be upgraded by the proxy manager.
             *
             * To upgrade the implementation, the manager calls the proxy with the abi encoded implementation address.
             *
             * If any other account calls the proxy, it will delegatecall the implementation address with the received
             * calldata and ether. If the call succeeds, it will return with the received returndata.
             * If it reverts, it will revert with the received revert data.
             *
             * Note: The storage slot for the implementation address is:
             * `bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)`
             * This slot must not be used by the implementation contract.
             *
             * Note: This contract does not verify that the implementation address is a valid delegation target.
             * The manager must perform this safety check.
             */
            contract DelegateCallProxyOneToOne is Proxy {
            /* ==========  Constants  ========== */
              address internal immutable _manager;
            /* ==========  Constructor  ========== */
              constructor() public {
                _manager = msg.sender ;
              }
            /* ==========  Internal Overrides  ========== */
              /**
               * @dev Reads the implementation address from storage.
               */
              function _implementation() internal override view returns (address) {
                address implementation;
                assembly {
                  implementation := sload(
                    // bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)
                    0x913bd12b32b36f36cedaeb6e043912bceb97022755958701789d3108d33a045a
                  )
                }
                return implementation;
              }
              /**
                * @dev Hook that is called before falling back to the implementation.
                *
                * Checks if the call is from the owner.
                * If it is, reads the abi-encoded implementation address from calldata and stores
                * it at the slot `bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)`,
                * then returns with no data.
                * If it is not, continues execution with the fallback function.
                */
              function _beforeFallback() internal override {
                if (msg.sender != _manager) {
                  super._beforeFallback();
                } else {
                  assembly {
                    sstore(
                      // bytes32(uint256(keccak256("IMPLEMENTATION_ADDRESS")) + 1)
                      0x913bd12b32b36f36cedaeb6e043912bceb97022755958701789d3108d33a045a,
                      calldataload(0)
                    )
                    return(0, 0)
                  }
                }
              }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            /* ---  External Libraries  --- */
            import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";
            /* ---  Proxy Contracts  --- */
            import { CodeHashes } from "./CodeHashes.sol";
            /**
             * @dev Library for computing create2 salts and addresses for proxies
             * deployed by `DelegateCallProxyManager`.
             *
             * Because the proxy factory is meant to be used by multiple contracts,
             * we use a salt derivation pattern that includes the address of the
             * contract that requested the proxy deployment, a salt provided by that
             * contract and the implementation ID used (for many-to-one proxies only).
             */
            library SaltyLib {
            /* ---  Salt Derivation  --- */
              /**
               * @dev Derives the create2 salt for a many-to-one proxy.
               *
               * Many different contracts in the Indexed framework may use the
               * same implementation contract, and they all use the same init
               * code, so we derive the actual create2 salt from a combination
               * of the implementation ID, the address of the account requesting
               * deployment and the user-supplied salt.
               *
               * @param originator Address of the account requesting deployment.
               * @param implementationID The identifier for the contract implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function deriveManyToOneSalt(
                address originator,
                bytes32 implementationID,
                bytes32 suppliedSalt
              )
                internal
                pure
                returns (bytes32)
              {
                return keccak256(
                  abi.encodePacked(
                    originator,
                    implementationID,
                    suppliedSalt
                  )
                );
              }
              /**
               * @dev Derives the create2 salt for a one-to-one proxy.
               *
               * @param originator Address of the account requesting deployment.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function deriveOneToOneSalt(
                address originator,
                bytes32 suppliedSalt
              )
                internal
                pure
                returns (bytes32)
              {
                return keccak256(abi.encodePacked(originator, suppliedSalt));
              }
            /* ---  Address Derivation  --- */
              /**
               * @dev Computes the create2 address for a one-to-one proxy deployed
               * by `deployer` (the factory) when requested by `originator` using
               * `suppliedSalt`.
               *
               * @param deployer Address of the proxy factory.
               * @param originator Address of the account requesting deployment.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function computeProxyAddressOneToOne(
                address deployer,
                address originator,
                bytes32 suppliedSalt
              )
                internal
                pure
                returns (address)
              {
                bytes32 salt = deriveOneToOneSalt(originator, suppliedSalt);
                return Create2.computeAddress(salt, CodeHashes.ONE_TO_ONE_CODEHASH, deployer);
              }
              /**
               * @dev Computes the create2 address for a many-to-one proxy for the
               * implementation `implementationID` deployed by `deployer` (the factory)
               * when requested by `originator` using `suppliedSalt`.
               *
               * @param deployer Address of the proxy factory.
               * @param originator Address of the account requesting deployment.
               * @param implementationID The identifier for the contract implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
              */
              function computeProxyAddressManyToOne(
                address deployer,
                address originator,
                bytes32 implementationID,
                bytes32 suppliedSalt
              )
                internal
                pure
                returns (address)
              {
                bytes32 salt = deriveManyToOneSalt(
                  originator,
                  implementationID,
                  suppliedSalt
                );
                return Create2.computeAddress(salt, CodeHashes.MANY_TO_ONE_CODEHASH, deployer);
              }
              /**
               * @dev Computes the create2 address of the implementation holder
               * for `implementationID`.
               *
               * @param deployer Address of the proxy factory.
               * @param implementationID The identifier for the contract implementation.
              */
              function computeHolderAddressManyToOne(
                address deployer,
                bytes32 implementationID
              )
                internal
                pure
                returns (address)
              {
                return Create2.computeAddress(
                  implementationID,
                  CodeHashes.IMPLEMENTATION_HOLDER_CODEHASH,
                  deployer
                );
              }
            }// SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            /**
             * @dev Contract that manages deployment and upgrades of delegatecall proxies.
             *
             * An implementation identifier can be created on the proxy manager which is
             * used to specify the logic address for a particular contract type, and to
             * upgrade the implementation as needed.
             *
             * A one-to-one proxy is a single proxy contract with an upgradeable implementation
             * address.
             *
             * A many-to-one proxy is a single upgradeable implementation address that may be
             * used by many proxy contracts.
             */
            interface IDelegateCallProxyManager {
            /* ==========  Events  ========== */
              event DeploymentApprovalGranted(address deployer);
              event DeploymentApprovalRevoked(address deployer);
              event ManyToOne_ImplementationCreated(
                bytes32 implementationID,
                address implementationAddress
              );
              event ManyToOne_ImplementationUpdated(
                bytes32 implementationID,
                address implementationAddress
              );
              event ManyToOne_ProxyDeployed(
                bytes32 implementationID,
                address proxyAddress
              );
              event OneToOne_ProxyDeployed(
                address proxyAddress,
                address implementationAddress
              );
              event OneToOne_ImplementationUpdated(
                address proxyAddress,
                address implementationAddress
              );
            /* ==========  Controls  ========== */
              /**
               * @dev Allows `deployer` to deploy many-to-one proxies.
               */
              function approveDeployer(address deployer) external;
              /**
               * @dev Prevents `deployer` from deploying many-to-one proxies.
               */
              function revokeDeployerApproval(address deployer) external;
            /* ==========  Implementation Management  ========== */
              /**
               * @dev Creates a many-to-one proxy relationship.
               *
               * Deploys an implementation holder contract which stores the
               * implementation address for many proxies. The implementation
               * address can be updated on the holder to change the runtime
               * code used by all its proxies.
               *
               * @param implementationID ID for the implementation, used to identify the
               * proxies that use it. Also used as the salt in the create2 call when
               * deploying the implementation holder contract.
               * @param implementation Address with the runtime code the proxies
               * should use.
               */
              function createManyToOneProxyRelationship(
                bytes32 implementationID,
                address implementation
              ) external;
              /**
               * @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
               */
              function lockImplementationManyToOne(bytes32 implementationID) external;
              /**
               * @dev Lock the current implementation for `proxyAddress` so that it can never be upgraded again.
               */
              function lockImplementationOneToOne(address proxyAddress) external;
              /**
               * @dev Updates the implementation address for a many-to-one
               * proxy relationship.
               *
               * @param implementationID Identifier for the implementation.
               * @param implementation Address with the runtime code the proxies
               * should use.
               */
              function setImplementationAddressManyToOne(
                bytes32 implementationID,
                address implementation
              ) external;
              /**
               * @dev Updates the implementation address for a one-to-one proxy.
               *
               * Note: This could work for many-to-one as well if the caller
               * provides the implementation holder address in place of the
               * proxy address, as they use the same access control and update
               * mechanism.
               *
               * @param proxyAddress Address of the deployed proxy
               * @param implementation Address with the runtime code for
               * the proxy to use.
               */
              function setImplementationAddressOneToOne(
                address proxyAddress,
                address implementation
              ) external;
            /* ==========  Proxy Deployment  ========== */
              /**
               * @dev Deploy a proxy contract with a one-to-one relationship
               * with its implementation.
               *
               * The proxy will have its own implementation address which can
               * be updated by the proxy manager.
               *
               * @param suppliedSalt Salt provided by the account requesting deployment.
               * @param implementation Address of the contract with the runtime
               * code that the proxy should use.
               */
              function deployProxyOneToOne(
                bytes32 suppliedSalt,
                address implementation
              ) external returns(address proxyAddress);
              /**
               * @dev Deploy a proxy with a many-to-one relationship with its implemenation.
               *
               * The proxy will call the implementation holder for every transaction to
               * determine the address to use in calls.
               *
               * @param implementationID Identifier for the proxy's implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function deployProxyManyToOne(
                bytes32 implementationID,
                bytes32 suppliedSalt
              ) external returns(address proxyAddress);
            /* ==========  Queries  ========== */
              /**
               * @dev Returns a boolean stating whether `implementationID` is locked.
               */
              function isImplementationLocked(bytes32 implementationID) external view returns (bool);
              /**
               * @dev Returns a boolean stating whether `proxyAddress` is locked.
               */
              function isImplementationLocked(address proxyAddress) external view returns (bool);
              /**
               * @dev Returns a boolean stating whether `deployer` is allowed to deploy many-to-one
               * proxies.
               */
              function isApprovedDeployer(address deployer) external view returns (bool);
              /**
               * @dev Queries the temporary storage value `_implementationHolder`.
               * This is used in the constructor of the many-to-one proxy contract
               * so that the create2 address is static (adding constructor arguments
               * would change the codehash) and the implementation holder can be
               * stored as a constant.
               */
              function getImplementationHolder() external view returns (address);
              /**
               * @dev Returns the address of the implementation holder contract
               * for `implementationID`.
               */
              function getImplementationHolder(bytes32 implementationID) external view returns (address);
              /**
               * @dev Computes the create2 address for a one-to-one proxy requested
               * by `originator` using `suppliedSalt`.
               *
               * @param originator Address of the account requesting deployment.
               * @param suppliedSalt Salt provided by the account requesting deployment.
               */
              function computeProxyAddressOneToOne(
                address originator,
                bytes32 suppliedSalt
              ) external view returns (address);
              /**
               * @dev Computes the create2 address for a many-to-one proxy for the
               * implementation `implementationID` requested by `originator` using
               * `suppliedSalt`.
               *
               * @param originator Address of the account requesting deployment.
               * @param implementationID The identifier for the contract implementation.
               * @param suppliedSalt Salt provided by the account requesting deployment.
              */
              function computeProxyAddressManyToOne(
                address originator,
                bytes32 implementationID,
                bytes32 suppliedSalt
              ) external view returns (address);
              /**
               * @dev Computes the create2 address of the implementation holder
               * for `implementationID`.
               *
               * @param implementationID The identifier for the contract implementation.
              */
              function computeHolderAddressManyToOne(bytes32 implementationID) external view returns (address);
            }

            File 4 of 5: IndexPool
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            /************************************************************************************************
            Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol
            This source code has been modified from the original, which was copied from the github repository
            at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
            Subject to the GPL-3.0 license
            *************************************************************************************************/
            contract BConst {
              uint256 public constant VERSION_NUMBER = 0;
            /* ---  Weight Updates  --- */
              // Minimum time passed between each weight update for a token.
              uint256 internal constant WEIGHT_UPDATE_DELAY = 1 hours;
              // Maximum percent by which a weight can adjust at a time
              // relative to the current weight.
              // The number of iterations needed to move from weight A to weight B is the floor of:
              // (A > B): (ln(A) - ln(B)) / ln(1.01)
              // (B > A): (ln(A) - ln(B)) / ln(0.99)
              uint256 internal constant WEIGHT_CHANGE_PCT = BONE/100;
              uint256 internal constant BONE = 10**18;
              uint256 internal constant MIN_BOUND_TOKENS = 2;
              uint256 internal constant MAX_BOUND_TOKENS = 10;
              // Minimum swap fee.
              uint256 internal constant MIN_FEE = BONE / 10**6;
              // Maximum swap or exit fee.
              uint256 internal constant MAX_FEE = BONE / 10;
              // Actual exit fee.
              uint256 internal constant EXIT_FEE = 0;
              // Default total of all desired weights. Can differ by up to BONE.
              uint256 internal constant DEFAULT_TOTAL_WEIGHT = BONE * 25;
              // Minimum weight for any token (1/100).
              uint256 internal constant MIN_WEIGHT = BONE / 4;
              uint256 internal constant MAX_WEIGHT = BONE * 25;
              // Maximum total weight.
              uint256 internal constant MAX_TOTAL_WEIGHT = BONE * 26;
              // Minimum balance for a token (only applied at initialization)
              uint256 internal constant MIN_BALANCE = BONE / 10**12;
              // Initial pool tokens
              uint256 internal constant INIT_POOL_SUPPLY = BONE * 100;
              uint256 internal constant MIN_BPOW_BASE = 1 wei;
              uint256 internal constant MAX_BPOW_BASE = (2 * BONE) - 1 wei;
              uint256 internal constant BPOW_PRECISION = BONE / 10**10;
              // Maximum ratio of input tokens to balance for swaps.
              uint256 internal constant MAX_IN_RATIO = BONE / 2;
              // Maximum ratio of output tokens to balance for swaps.
              uint256 internal constant MAX_OUT_RATIO = (BONE / 3) + 1 wei;
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            import "./BNum.sol";
            /************************************************************************************************
            Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BMath.sol
            This source code has been modified from the original, which was copied from the github repository
            at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
            Subject to the GPL-3.0 license
            *************************************************************************************************/
            contract BMath is BConst, BNum {
              /**********************************************************************************************
                // calcSpotPrice                                                                             //
                // sP = spotPrice                                                                            //
                // bI = tokenBalanceIn                ( bI / wI )         1                                  //
                // bO = tokenBalanceOut         sP =  -----------  *  ----------                             //
                // wI = tokenWeightIn                 ( bO / wO )     ( 1 - sF )                             //
                // wO = tokenWeightOut                                                                       //
                // sF = swapFee                                                                              //
                **********************************************************************************************/
              function calcSpotPrice(
                uint256 tokenBalanceIn,
                uint256 tokenWeightIn,
                uint256 tokenBalanceOut,
                uint256 tokenWeightOut,
                uint256 swapFee
              ) internal pure returns (uint256 spotPrice) {
                uint256 numer = bdiv(tokenBalanceIn, tokenWeightIn);
                uint256 denom = bdiv(tokenBalanceOut, tokenWeightOut);
                uint256 ratio = bdiv(numer, denom);
                uint256 scale = bdiv(BONE, bsub(BONE, swapFee));
                return (spotPrice = bmul(ratio, scale));
              }
              /**********************************************************************************************
                // calcOutGivenIn                                                                            //
                // aO = tokenAmountOut                                                                       //
                // bO = tokenBalanceOut                                                                      //
                // bI = tokenBalanceIn              /      /            bI             \\    (wI / wO) \\      //
                // aI = tokenAmountIn    aO = bO * |  1 - | --------------------------  | ^            |     //
                // wI = tokenWeightIn               \\      \\ ( bI + ( aI * ( 1 - sF )) /              /      //
                // wO = tokenWeightOut                                                                       //
                // sF = swapFee                                                                              //
                **********************************************************************************************/
              function calcOutGivenIn(
                uint256 tokenBalanceIn,
                uint256 tokenWeightIn,
                uint256 tokenBalanceOut,
                uint256 tokenWeightOut,
                uint256 tokenAmountIn,
                uint256 swapFee
              ) internal pure returns (uint256 tokenAmountOut) {
                uint256 weightRatio = bdiv(tokenWeightIn, tokenWeightOut);
                uint256 adjustedIn = bsub(BONE, swapFee);
                adjustedIn = bmul(tokenAmountIn, adjustedIn);
                uint256 y = bdiv(tokenBalanceIn, badd(tokenBalanceIn, adjustedIn));
                uint256 foo = bpow(y, weightRatio);
                uint256 bar = bsub(BONE, foo);
                tokenAmountOut = bmul(tokenBalanceOut, bar);
                return tokenAmountOut;
              }
              /**********************************************************************************************
                // calcInGivenOut                                                                            //
                // aI = tokenAmountIn                                                                        //
                // bO = tokenBalanceOut               /  /     bO      \\    (wO / wI)      \\                 //
                // bI = tokenBalanceIn          bI * |  | ------------  | ^            - 1  |                //
                // aO = tokenAmountOut    aI =        \\  \\ ( bO - aO ) /                   /                 //
                // wI = tokenWeightIn           --------------------------------------------                 //
                // wO = tokenWeightOut                          ( 1 - sF )                                   //
                // sF = swapFee                                                                              //
                **********************************************************************************************/
              function calcInGivenOut(
                uint256 tokenBalanceIn,
                uint256 tokenWeightIn,
                uint256 tokenBalanceOut,
                uint256 tokenWeightOut,
                uint256 tokenAmountOut,
                uint256 swapFee
              ) internal pure returns (uint256 tokenAmountIn) {
                uint256 weightRatio = bdiv(tokenWeightOut, tokenWeightIn);
                uint256 diff = bsub(tokenBalanceOut, tokenAmountOut);
                uint256 y = bdiv(tokenBalanceOut, diff);
                uint256 foo = bpow(y, weightRatio);
                foo = bsub(foo, BONE);
                tokenAmountIn = bsub(BONE, swapFee);
                tokenAmountIn = bdiv(bmul(tokenBalanceIn, foo), tokenAmountIn);
                return tokenAmountIn;
              }
              /**********************************************************************************************
                // calcPoolOutGivenSingleIn                                                                  //
                // pAo = poolAmountOut         /                                              \\              //
                // tAi = tokenAmountIn        ///      /     //    wI \\      \\\\       \\     wI \\             //
                // wI = tokenWeightIn        //| tAi *| 1 - || 1 - --  | * sF || + tBi \\    --  \\            //
                // tW = totalWeight     pAo=||  \\      \\     \\\\    tW /      //         | ^ tW   | * pS - pS //
                // tBi = tokenBalanceIn      \\\\  ------------------------------------- /        /            //
                // pS = poolSupply            \\\\                    tBi               /        /             //
                // sF = swapFee                \\                                              /              //
                **********************************************************************************************/
              function calcPoolOutGivenSingleIn(
                uint256 tokenBalanceIn,
                uint256 tokenWeightIn,
                uint256 poolSupply,
                uint256 totalWeight,
                uint256 tokenAmountIn,
                uint256 swapFee
              ) internal pure returns (uint256 poolAmountOut) {
                // Charge the trading fee for the proportion of tokenAi
                ///  which is implicitly traded to the other pool tokens.
                // That proportion is (1- weightTokenIn)
                // tokenAiAfterFee = tAi * (1 - (1-weightTi) * poolFee);
                uint256 normalizedWeight = bdiv(tokenWeightIn, totalWeight);
                uint256 zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
                uint256 tokenAmountInAfterFee = bmul(tokenAmountIn, bsub(BONE, zaz));
                uint256 newTokenBalanceIn = badd(tokenBalanceIn, tokenAmountInAfterFee);
                uint256 tokenInRatio = bdiv(newTokenBalanceIn, tokenBalanceIn);
                // uint newPoolSupply = (ratioTi ^ weightTi) * poolSupply;
                uint256 poolRatio = bpow(tokenInRatio, normalizedWeight);
                uint256 newPoolSupply = bmul(poolRatio, poolSupply);
                poolAmountOut = bsub(newPoolSupply, poolSupply);
                return poolAmountOut;
              }
              /**********************************************************************************************
                // calcSingleInGivenPoolOut                                                                  //
                // tAi = tokenAmountIn              //(pS + pAo)\\     /    1    \\\\                           //
                // pS = poolSupply                 || ---------  | ^ | --------- || * bI - bI                //
                // pAo = poolAmountOut              \\\\    pS    /     \\(wI / tW)//                           //
                // bI = balanceIn          tAi =  --------------------------------------------               //
                // wI = weightIn                              /      wI  \\                                   //
                // tW = totalWeight                          |  1 - ----  |  * sF                            //
                // sF = swapFee                               \\      tW  /                                   //
                **********************************************************************************************/
              function calcSingleInGivenPoolOut(
                uint256 tokenBalanceIn,
                uint256 tokenWeightIn,
                uint256 poolSupply,
                uint256 totalWeight,
                uint256 poolAmountOut,
                uint256 swapFee
              ) internal pure returns (uint256 tokenAmountIn) {
                uint256 normalizedWeight = bdiv(tokenWeightIn, totalWeight);
                uint256 newPoolSupply = badd(poolSupply, poolAmountOut);
                uint256 poolRatio = bdiv(newPoolSupply, poolSupply);
                //uint newBalTi = poolRatio^(1/weightTi) * balTi;
                uint256 boo = bdiv(BONE, normalizedWeight);
                uint256 tokenInRatio = bpow(poolRatio, boo);
                uint256 newTokenBalanceIn = bmul(tokenInRatio, tokenBalanceIn);
                uint256 tokenAmountInAfterFee = bsub(newTokenBalanceIn, tokenBalanceIn);
                // Do reverse order of fees charged in joinswap_ExternAmountIn, this way
                //     ``` pAo == joinswap_ExternAmountIn(Ti, joinswap_PoolAmountOut(pAo, Ti)) ```
                //uint tAi = tAiAfterFee / (1 - (1-weightTi) * swapFee) ;
                uint256 zar = bmul(bsub(BONE, normalizedWeight), swapFee);
                tokenAmountIn = bdiv(tokenAmountInAfterFee, bsub(BONE, zar));
                return tokenAmountIn;
              }
              /**********************************************************************************************
                // calcSingleOutGivenPoolIn                                                                  //
                // tAo = tokenAmountOut            /      /                                             \\\\   //
                // bO = tokenBalanceOut           /      // pS - (pAi * (1 - eF)) \\     /    1    \\      \\\\  //
                // pAi = poolAmountIn            | bO - || ----------------------- | ^ | --------- | * b0 || //
                // ps = poolSupply                \\      \\\\          pS           /     \\(wO / tW)/      //  //
                // wI = tokenWeightIn      tAo =   \\      \\                                             //   //
                // tW = totalWeight                    /     /      wO \\       \\                             //
                // sF = swapFee                    *  | 1 - |  1 - ---- | * sF  |                            //
                // eF = exitFee                        \\     \\      tW /       /                             //
                **********************************************************************************************/
              function calcSingleOutGivenPoolIn(
                uint256 tokenBalanceOut,
                uint256 tokenWeightOut,
                uint256 poolSupply,
                uint256 totalWeight,
                uint256 poolAmountIn,
                uint256 swapFee
              ) internal pure returns (uint256 tokenAmountOut) {
                uint256 normalizedWeight = bdiv(tokenWeightOut, totalWeight);
                // charge exit fee on the pool token side
                // pAiAfterExitFee = pAi*(1-exitFee)
                uint256 poolAmountInAfterExitFee = bmul(poolAmountIn, bsub(BONE, EXIT_FEE));
                uint256 newPoolSupply = bsub(poolSupply, poolAmountInAfterExitFee);
                uint256 poolRatio = bdiv(newPoolSupply, poolSupply);
                // newBalTo = poolRatio^(1/weightTo) * balTo;
                uint256 tokenOutRatio = bpow(poolRatio, bdiv(BONE, normalizedWeight));
                uint256 newTokenBalanceOut = bmul(tokenOutRatio, tokenBalanceOut);
                uint256 tokenAmountOutBeforeSwapFee = bsub(
                  tokenBalanceOut,
                  newTokenBalanceOut
                );
                // charge swap fee on the output token side
                //uint tAo = tAoBeforeSwapFee * (1 - (1-weightTo) * swapFee)
                uint256 zaz = bmul(bsub(BONE, normalizedWeight), swapFee);
                tokenAmountOut = bmul(tokenAmountOutBeforeSwapFee, bsub(BONE, zaz));
                return tokenAmountOut;
              }
              /**********************************************************************************************
                // calcPoolInGivenSingleOut                                                                  //
                // pAi = poolAmountIn               // /               tAo             \\\\     / wO \\     \\   //
                // bO = tokenBalanceOut            // | bO - -------------------------- |\\   | ---- |     \\  //
                // tAo = tokenAmountOut      pS - ||   \\     1 - ((1 - (tO / tW)) * sF)/  | ^ \\ tW /  * pS | //
                // ps = poolSupply                 \\\\ -----------------------------------/                /  //
                // wO = tokenWeightOut  pAi =       \\\\               bO                 /                /   //
                // tW = totalWeight           -------------------------------------------------------------  //
                // sF = swapFee                                        ( 1 - eF )                            //
                // eF = exitFee                                                                              //
                **********************************************************************************************/
              function calcPoolInGivenSingleOut(
                uint256 tokenBalanceOut,
                uint256 tokenWeightOut,
                uint256 poolSupply,
                uint256 totalWeight,
                uint256 tokenAmountOut,
                uint256 swapFee
              ) internal pure returns (uint256 poolAmountIn) {
                // charge swap fee on the output token side
                uint256 normalizedWeight = bdiv(tokenWeightOut, totalWeight);
                //uint tAoBeforeSwapFee = tAo / (1 - (1-weightTo) * swapFee) ;
                uint256 zoo = bsub(BONE, normalizedWeight);
                uint256 zar = bmul(zoo, swapFee);
                uint256 tokenAmountOutBeforeSwapFee = bdiv(tokenAmountOut, bsub(BONE, zar));
                uint256 newTokenBalanceOut = bsub(
                  tokenBalanceOut,
                  tokenAmountOutBeforeSwapFee
                );
                uint256 tokenOutRatio = bdiv(newTokenBalanceOut, tokenBalanceOut);
                //uint newPoolSupply = (ratioTo ^ weightTo) * poolSupply;
                uint256 poolRatio = bpow(tokenOutRatio, normalizedWeight);
                uint256 newPoolSupply = bmul(poolRatio, poolSupply);
                uint256 poolAmountInAfterExitFee = bsub(poolSupply, newPoolSupply);
                // charge exit fee on the pool token side
                // pAi = pAiAfterExitFee/(1-exitFee)
                poolAmountIn = bdiv(poolAmountInAfterExitFee, bsub(BONE, EXIT_FEE));
                return poolAmountIn;
              }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            import "./BConst.sol";
            /************************************************************************************************
            Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol
            This source code has been modified from the original, which was copied from the github repository
            at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
            Subject to the GPL-3.0 license
            *************************************************************************************************/
            contract BNum is BConst {
              function btoi(uint256 a) internal pure returns (uint256) {
                return a / BONE;
              }
              function bfloor(uint256 a) internal pure returns (uint256) {
                return btoi(a) * BONE;
              }
              function badd(uint256 a, uint256 b) internal pure returns (uint256) {
                uint256 c = a + b;
                require(c >= a, "ERR_ADD_OVERFLOW");
                return c;
              }
              function bsub(uint256 a, uint256 b) internal pure returns (uint256) {
                (uint256 c, bool flag) = bsubSign(a, b);
                require(!flag, "ERR_SUB_UNDERFLOW");
                return c;
              }
              function bsubSign(uint256 a, uint256 b)
                internal
                pure
                returns (uint256, bool)
              {
                if (a >= b) {
                  return (a - b, false);
                } else {
                  return (b - a, true);
                }
              }
              function bmul(uint256 a, uint256 b) internal pure returns (uint256) {
                uint256 c0 = a * b;
                require(a == 0 || c0 / a == b, "ERR_MUL_OVERFLOW");
                uint256 c1 = c0 + (BONE / 2);
                require(c1 >= c0, "ERR_MUL_OVERFLOW");
                uint256 c2 = c1 / BONE;
                return c2;
              }
              function bdiv(uint256 a, uint256 b) internal pure returns (uint256) {
                require(b != 0, "ERR_DIV_ZERO");
                uint256 c0 = a * BONE;
                require(a == 0 || c0 / a == BONE, "ERR_DIV_INTERNAL"); // bmul overflow
                uint256 c1 = c0 + (b / 2);
                require(c1 >= c0, "ERR_DIV_INTERNAL"); //  badd require
                uint256 c2 = c1 / b;
                return c2;
              }
              // DSMath.wpow
              function bpowi(uint256 a, uint256 n) internal pure returns (uint256) {
                uint256 z = n % 2 != 0 ? a : BONE;
                for (n /= 2; n != 0; n /= 2) {
                  a = bmul(a, a);
                  if (n % 2 != 0) {
                    z = bmul(z, a);
                  }
                }
                return z;
              }
              // Compute b^(e.w) by splitting it into (b^e)*(b^0.w).
              // Use `bpowi` for `b^e` and `bpowK` for k iterations
              // of approximation of b^0.w
              function bpow(uint256 base, uint256 exp) internal pure returns (uint256) {
                require(base >= MIN_BPOW_BASE, "ERR_BPOW_BASE_TOO_LOW");
                require(base <= MAX_BPOW_BASE, "ERR_BPOW_BASE_TOO_HIGH");
                uint256 whole = bfloor(exp);
                uint256 remain = bsub(exp, whole);
                uint256 wholePow = bpowi(base, btoi(whole));
                if (remain == 0) {
                  return wholePow;
                }
                uint256 partialResult = bpowApprox(base, remain, BPOW_PRECISION);
                return bmul(wholePow, partialResult);
              }
              function bpowApprox(
                uint256 base,
                uint256 exp,
                uint256 precision
              ) internal pure returns (uint256) {
                // term 0:
                uint256 a = exp;
                (uint256 x, bool xneg) = bsubSign(base, BONE);
                uint256 term = BONE;
                uint256 sum = term;
                bool negative = false;
                // term(k) = numer / denom
                //         = (product(a - i - 1, i=1-->k) * x^k) / (k!)
                // each iteration, multiply previous term by (a-(k-1)) * x / k
                // continue until term is less than precision
                for (uint256 i = 1; term >= precision; i++) {
                  uint256 bigK = i * BONE;
                  (uint256 c, bool cneg) = bsubSign(a, bsub(bigK, BONE));
                  term = bmul(term, bmul(c, x));
                  term = bdiv(term, bigK);
                  if (term == 0) break;
                  if (xneg) negative = !negative;
                  if (cneg) negative = !negative;
                  if (negative) {
                    sum = bsub(sum, term);
                  } else {
                    sum = badd(sum, term);
                  }
                }
                return sum;
              }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            import "./BNum.sol";
            /************************************************************************************************
            Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BToken.sol
            This source code has been modified from the original, which was copied from the github repository
            at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
            Subject to the GPL-3.0 license
            *************************************************************************************************/
            // Highly opinionated token implementation
            interface IERC20 {
              event Approval(address indexed src, address indexed dst, uint256 amt);
              event Transfer(address indexed src, address indexed dst, uint256 amt);
              function name() external view returns (string memory);
              function symbol() external view returns (string memory);
              function decimals() external view returns (uint8);
              function totalSupply() external view returns (uint256);
              function balanceOf(address whom) external view returns (uint256);
              function allowance(address src, address dst) external view returns (uint256);
              function approve(address dst, uint256 amt) external returns (bool);
              function transfer(address dst, uint256 amt) external returns (bool);
              function transferFrom(
                address src,
                address dst,
                uint256 amt
              ) external returns (bool);
            }
            contract BTokenBase is BNum {
              mapping(address => uint256) internal _balance;
              mapping(address => mapping(address => uint256)) internal _allowance;
              uint256 internal _totalSupply;
              event Approval(address indexed src, address indexed dst, uint256 amt);
              event Transfer(address indexed src, address indexed dst, uint256 amt);
              function _mint(uint256 amt) internal {
                _balance[address(this)] = badd(_balance[address(this)], amt);
                _totalSupply = badd(_totalSupply, amt);
                emit Transfer(address(0), address(this), amt);
              }
              function _burn(uint256 amt) internal {
                require(_balance[address(this)] >= amt, "ERR_INSUFFICIENT_BAL");
                _balance[address(this)] = bsub(_balance[address(this)], amt);
                _totalSupply = bsub(_totalSupply, amt);
                emit Transfer(address(this), address(0), amt);
              }
              function _move(
                address src,
                address dst,
                uint256 amt
              ) internal {
                require(_balance[src] >= amt, "ERR_INSUFFICIENT_BAL");
                _balance[src] = bsub(_balance[src], amt);
                _balance[dst] = badd(_balance[dst], amt);
                emit Transfer(src, dst, amt);
              }
              function _push(address to, uint256 amt) internal {
                _move(address(this), to, amt);
              }
              function _pull(address from, uint256 amt) internal {
                _move(from, address(this), amt);
              }
            }
            contract BToken is BTokenBase, IERC20 {
              uint8 private constant DECIMALS = 18;
              string private _name;
              string private _symbol;
              function _initializeToken(string memory name, string memory symbol) internal {
                require(
                  bytes(_name).length == 0 &&
                  bytes(name).length != 0 &&
                  bytes(symbol).length != 0,
                  "ERR_BTOKEN_INITIALIZED"
                );
                _name = name;
                _symbol = symbol;
              }
              function name()
                external
                override
                view
                returns (string memory)
              {
                return _name;
              }
              function symbol()
                external
                override
                view
                returns (string memory)
              {
                return _symbol;
              }
              function decimals()
                external
                override
                view
                returns (uint8)
              {
                return DECIMALS;
              }
              function allowance(address src, address dst)
                external
                override
                view
                returns (uint256)
              {
                return _allowance[src][dst];
              }
              function balanceOf(address whom) external override view returns (uint256) {
                return _balance[whom];
              }
              function totalSupply() public override view returns (uint256) {
                return _totalSupply;
              }
              function approve(address dst, uint256 amt) external override returns (bool) {
                _allowance[msg.sender][dst] = amt;
                emit Approval(msg.sender, dst, amt);
                return true;
              }
              function increaseApproval(address dst, uint256 amt) external returns (bool) {
                _allowance[msg.sender][dst] = badd(_allowance[msg.sender][dst], amt);
                emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
                return true;
              }
              function decreaseApproval(address dst, uint256 amt) external returns (bool) {
                uint256 oldValue = _allowance[msg.sender][dst];
                if (amt > oldValue) {
                  _allowance[msg.sender][dst] = 0;
                } else {
                  _allowance[msg.sender][dst] = bsub(oldValue, amt);
                }
                emit Approval(msg.sender, dst, _allowance[msg.sender][dst]);
                return true;
              }
              function transfer(address dst, uint256 amt) external override returns (bool) {
                _move(msg.sender, dst, amt);
                return true;
              }
              function transferFrom(
                address src,
                address dst,
                uint256 amt
              ) external override returns (bool) {
                require(
                  msg.sender == src || amt <= _allowance[src][msg.sender],
                  "ERR_BTOKEN_BAD_CALLER"
                );
                _move(src, dst, amt);
                if (msg.sender != src && _allowance[src][msg.sender] != uint256(-1)) {
                  _allowance[src][msg.sender] = bsub(_allowance[src][msg.sender], amt);
                  emit Approval(msg.sender, dst, _allowance[src][msg.sender]);
                }
                return true;
              }
            }
            // SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            pragma experimental ABIEncoderV2;
            /* ========== Internal Inheritance ========== */
            import "./BToken.sol";
            import "./BMath.sol";
            /* ========== Internal Interfaces ========== */
            import "../interfaces/IFlashLoanRecipient.sol";
            import "../interfaces/IIndexPool.sol";
            /************************************************************************************************
            Originally from https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol
            This source code has been modified from the original, which was copied from the github repository
            at commit hash f4ed5d65362a8d6cec21662fb6eae233b0babc1f.
            Subject to the GPL-3.0 license
            *************************************************************************************************/
            contract IndexPool is BToken, BMath, IIndexPool {
            /* ==========  EVENTS  ========== */
              /** @dev Emitted when tokens are swapped. */
              event LOG_SWAP(
                address indexed caller,
                address indexed tokenIn,
                address indexed tokenOut,
                uint256 tokenAmountIn,
                uint256 tokenAmountOut
              );
              /** @dev Emitted when underlying tokens are deposited for pool tokens. */
              event LOG_JOIN(
                address indexed caller,
                address indexed tokenIn,
                uint256 tokenAmountIn
              );
              /** @dev Emitted when pool tokens are burned for underlying. */
              event LOG_EXIT(
                address indexed caller,
                address indexed tokenOut,
                uint256 tokenAmountOut
              );
              /** @dev Emitted when a token's weight updates. */
              event LOG_DENORM_UPDATED(address indexed token, uint256 newDenorm);
              /** @dev Emitted when a token's desired weight is set. */
              event LOG_DESIRED_DENORM_SET(address indexed token, uint256 desiredDenorm);
              /** @dev Emitted when a token is unbound from the pool. */
              event LOG_TOKEN_REMOVED(address token);
              /** @dev Emitted when a token is unbound from the pool. */
              event LOG_TOKEN_ADDED(
                address indexed token,
                uint256 desiredDenorm,
                uint256 minimumBalance
              );
              /** @dev Emitted when a token's minimum balance is updated. */
              event LOG_MINIMUM_BALANCE_UPDATED(address token, uint256 minimumBalance);
              /** @dev Emitted when a token reaches its minimum balance. */
              event LOG_TOKEN_READY(address indexed token);
              /** @dev Emitted when public trades are enabled. */
              event LOG_PUBLIC_SWAP_ENABLED();
              /** @dev Emitted when the maximum tokens value is updated. */
              event LOG_MAX_TOKENS_UPDATED(uint256 maxPoolTokens);
              /** @dev Emitted when the swap fee is updated. */
              event LOG_SWAP_FEE_UPDATED(uint256 swapFee);
            /* ==========  Modifiers  ========== */
              modifier _lock_ {
                require(!_mutex, "ERR_REENTRY");
                _mutex = true;
                _;
                _mutex = false;
              }
              modifier _viewlock_() {
                require(!_mutex, "ERR_REENTRY");
                _;
              }
              modifier _control_ {
                require(msg.sender == _controller, "ERR_NOT_CONTROLLER");
                _;
              }
              modifier _public_ {
                require(_publicSwap, "ERR_NOT_PUBLIC");
                _;
              }
            /* ==========  Storage  ========== */
              bool internal _mutex;
              // Account with CONTROL role. Able to modify the swap fee,
              // adjust token weights, bind and unbind tokens and lock
              // public swaps & joins.
              address internal _controller;
              // Contract that handles unbound tokens.
              TokenUnbindHandler internal _unbindHandler;
              // True if PUBLIC can call SWAP & JOIN functions
              bool internal _publicSwap;
              // `setSwapFee` requires CONTROL
              uint256 internal _swapFee;
              // Array of underlying tokens in the pool.
              address[] internal _tokens;
              // Internal records of the pool's underlying tokens
              mapping(address => Record) internal _records;
              // Total denormalized weight of the pool.
              uint256 internal _totalWeight;
              // Minimum balances for tokens which have been added without the
              // requisite initial balance.
              mapping(address => uint256) internal _minimumBalances;
              // Maximum LP tokens that can be bound.
              // Used in alpha to restrict the economic impact of a catastrophic
              // failure. It can be gradually increased as the pool continues to
              // not be exploited.
              uint256 internal _maxPoolTokens;
            /* ==========  Controls  ========== */
              /**
               * @dev Sets the controller address and the token name & symbol.
               *
               * Note: This saves on storage costs for multi-step pool deployment.
               *
               * @param controller Controller of the pool
               * @param name Name of the pool token
               * @param symbol Symbol of the pool token
               */
              function configure(
                address controller,
                string calldata name,
                string calldata symbol
              ) external override {
                require(_controller == address(0), "ERR_CONFIGURED");
                require(controller != address(0), "ERR_NULL_ADDRESS");
                _controller = controller;
                // default fee is 2.5%
                _swapFee = BONE / 40;
                _initializeToken(name, symbol);
              }
              /**
               * @dev Sets up the initial assets for the pool.
               *
               * Note: `tokenProvider` must have approved the pool to transfer the
               * corresponding `balances` of `tokens`.
               *
               * @param tokens Underlying tokens to initialize the pool with
               * @param balances Initial balances to transfer
               * @param denorms Initial denormalized weights for the tokens
               * @param tokenProvider Address to transfer the balances from
               */
              function initialize(
                address[] calldata tokens,
                uint256[] calldata balances,
                uint96[] calldata denorms,
                address tokenProvider,
                address unbindHandler
              )
                external
                override
                _control_
              {
                require(_tokens.length == 0, "ERR_INITIALIZED");
                uint256 len = tokens.length;
                require(len >= MIN_BOUND_TOKENS, "ERR_MIN_TOKENS");
                require(len <= MAX_BOUND_TOKENS, "ERR_MAX_TOKENS");
                require(balances.length == len && denorms.length == len, "ERR_ARR_LEN");
                uint256 totalWeight = 0;
                for (uint256 i = 0; i < len; i++) {
                  address token = tokens[i];
                  uint96 denorm = denorms[i];
                  uint256 balance = balances[i];
                  require(denorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
                  require(denorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
                  require(balance >= MIN_BALANCE, "ERR_MIN_BALANCE");
                  _records[token] = Record({
                    bound: true,
                    ready: true,
                    lastDenormUpdate: uint40(now),
                    denorm: denorm,
                    desiredDenorm: denorm,
                    index: uint8(i),
                    balance: balance
                  });
                  _tokens.push(token);
                  totalWeight = badd(totalWeight, denorm);
                  _pullUnderlying(token, tokenProvider, balance);
                }
                require(totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
                _totalWeight = totalWeight;
                _publicSwap = true;
                emit LOG_PUBLIC_SWAP_ENABLED();
                _mintPoolShare(INIT_POOL_SUPPLY);
                _pushPoolShare(tokenProvider, INIT_POOL_SUPPLY);
                _unbindHandler = TokenUnbindHandler(unbindHandler);
              }
              /**
               * @dev Sets the maximum number of pool tokens that can be minted.
               *
               * This value will be used in the alpha to limit the maximum damage
               * that can be caused by a catastrophic error. It can be gradually
               * increased as the pool continues to not be exploited.
               *
               * If it is set to 0, the limit will be removed.
               */
              function setMaxPoolTokens(uint256 maxPoolTokens) external override _control_ {
                _maxPoolTokens = maxPoolTokens;
                emit LOG_MAX_TOKENS_UPDATED(maxPoolTokens);
              }
            /* ==========  Configuration Actions  ========== */
              /**
               * @dev Set the swap fee.
               * Note: Swap fee must be between 0.0001% and 10%
               */
              function setSwapFee(uint256 swapFee) external override _control_ {
                require(swapFee >= MIN_FEE, "ERR_MIN_FEE");
                require(swapFee <= MAX_FEE, "ERR_MAX_FEE");
                _swapFee = swapFee;
                emit LOG_SWAP_FEE_UPDATED(swapFee);
              }
            /* ==========  Token Management Actions  ========== */
              /**
               * @dev Sets the desired weights for the pool tokens, which
               * will be adjusted over time as they are swapped.
               *
               * Note: This does not check for duplicate tokens or that the total
               * of the desired weights is equal to the target total weight (25).
               * Those assumptions should be met in the controller. Further, the
               * provided tokens should only include the tokens which are not set
               * for removal.
               */
              function reweighTokens(
                address[] calldata tokens,
                uint96[] calldata desiredDenorms
              )
                external
                override
                _lock_
                _control_
              {
                uint256 len = tokens.length;
                require(desiredDenorms.length == len, "ERR_ARR_LEN");
                for (uint256 i = 0; i < len; i++)
                  _setDesiredDenorm(tokens[i], desiredDenorms[i]);
              }
              /**
               * @dev Update the underlying assets held by the pool and their associated
               * weights. Tokens which are not currently bound will be gradually added
               * as they are swapped in to reach the provided minimum balances, which must
               * be an amount of tokens worth the minimum weight of the total pool value.
               * If a currently bound token is not received in this call, the token's
               * desired weight will be set to 0.
               */
              function reindexTokens(
                address[] calldata tokens,
                uint96[] calldata desiredDenorms,
                uint256[] calldata minimumBalances
              )
                external
                override
                _lock_
                _control_
              {
                uint256 len = tokens.length;
                require(
                  desiredDenorms.length == len && minimumBalances.length == len,
                  "ERR_ARR_LEN"
                );
                // This size may not be the same as the input size, as it is possible
                // to temporarily exceed the index size while tokens are being phased in
                // or out.
                uint256 tLen = _tokens.length;
                bool[] memory receivedIndices = new bool[](tLen);
                // We need to read token records in two separate loops, so
                // write them to memory to avoid duplicate storage reads.
                Record[] memory records = new Record[](len);
                // Read all the records from storage and mark which of the existing tokens
                // were represented in the reindex call.
                for (uint256 i = 0; i < len; i++) {
                  records[i] = _records[tokens[i]];
                  if (records[i].bound) receivedIndices[records[i].index] = true;
                }
                // If any bound tokens were not sent in this call, set their desired weights to 0.
                for (uint256 i = 0; i < tLen; i++) {
                  if (!receivedIndices[i]) {
                    _setDesiredDenorm(_tokens[i], 0);
                  }
                }
                for (uint256 i = 0; i < len; i++) {
                  address token = tokens[i];
                  // If an input weight is less than the minimum weight, use that instead.
                  uint96 denorm = desiredDenorms[i];
                  if (denorm < MIN_WEIGHT) denorm = uint96(MIN_WEIGHT);
                  if (!records[i].bound) {
                    // If the token is not bound, bind it.
                    _bind(token, minimumBalances[i], denorm);
                  } else {
                    _setDesiredDenorm(token, denorm);
                  }
                }
              }
              /**
               * @dev Updates the minimum balance for an uninitialized token.
               * This becomes useful if a token's external price significantly
               * rises after being bound, since the pool can not send a token
               * out until it reaches the minimum balance.
               */
              function setMinimumBalance(
                address token,
                uint256 minimumBalance
              )
                external
                override
                _control_
              {
                Record storage record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
                require(!record.ready, "ERR_READY");
                _minimumBalances[token] = minimumBalance;
                emit LOG_MINIMUM_BALANCE_UPDATED(token, minimumBalance);
              }
            /* ==========  Liquidity Provider Actions  ========== */
              /**
               * @dev Mint new pool tokens by providing the proportional amount of each
               * underlying token's balance relative to the proportion of pool tokens minted.
               *
               * For any underlying tokens which are not initialized, the caller must provide
               * the proportional share of the minimum balance for the token rather than the
               * actual balance.
               *
               * @param poolAmountOut Amount of pool tokens to mint
               * @param maxAmountsIn Maximum amount of each token to pay in the same
               * order as the pool's _tokens list.
               */
              function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn)
                external
                override
                _lock_
                _public_
              {
                uint256 poolTotal = totalSupply();
                uint256 ratio = bdiv(poolAmountOut, poolTotal);
                require(ratio != 0, "ERR_MATH_APPROX");
                require(maxAmountsIn.length == _tokens.length, "ERR_ARR_LEN");
                uint256 maxPoolTokens = _maxPoolTokens;
                if (maxPoolTokens > 0) {
                  require(
                    badd(poolTotal, poolAmountOut) <= maxPoolTokens,
                    "ERR_MAX_POOL_TOKENS"
                  );
                }
                for (uint256 i = 0; i < maxAmountsIn.length; i++) {
                  address t = _tokens[i];
                  (Record memory record, uint256 realBalance) = _getInputToken(t);
                  uint256 tokenAmountIn = bmul(ratio, record.balance);
                  require(tokenAmountIn != 0, "ERR_MATH_APPROX");
                  require(tokenAmountIn <= maxAmountsIn[i], "ERR_LIMIT_IN");
                  _updateInputToken(t, record, badd(realBalance, tokenAmountIn));
                  emit LOG_JOIN(msg.sender, t, tokenAmountIn);
                  _pullUnderlying(t, msg.sender, tokenAmountIn);
                }
                _mintPoolShare(poolAmountOut);
                _pushPoolShare(msg.sender, poolAmountOut);
              }
              /**
               * @dev Pay `tokenAmountIn` of `tokenIn` to mint at least `minPoolAmountOut`
               * pool tokens.
               *
               * The pool implicitly swaps `(1- weightTokenIn) * tokenAmountIn` to the other
               * underlying tokens. Thus a swap fee is charged against the input tokens.
               *
               * @param tokenIn Token to send the pool
               * @param tokenAmountIn Exact amount of `tokenIn` to pay
               * @param minPoolAmountOut Minimum amount of pool tokens to mint
               * @return poolAmountOut - Amount of pool tokens minted
               */
              function joinswapExternAmountIn(
                address tokenIn,
                uint256 tokenAmountIn,
                uint256 minPoolAmountOut
              )
                external
                override
                _lock_
                _public_
                returns (uint256/* poolAmountOut */)
              {
                (Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
                require(tokenAmountIn != 0, "ERR_ZERO_IN");
                require(
                  tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
                  "ERR_MAX_IN_RATIO"
                );
                uint256 poolAmountOut = calcPoolOutGivenSingleIn(
                  inRecord.balance,
                  inRecord.denorm,
                  _totalSupply,
                  _totalWeight,
                  tokenAmountIn,
                  _swapFee
                );
                uint256 maxPoolTokens = _maxPoolTokens;
                if (maxPoolTokens > 0) {
                  require(
                    badd(_totalSupply, poolAmountOut) <= maxPoolTokens,
                    "ERR_MAX_POOL_TOKENS"
                  );
                }
                require(poolAmountOut >= minPoolAmountOut, "ERR_LIMIT_OUT");
                _updateInputToken(tokenIn, inRecord, badd(realInBalance, tokenAmountIn));
                emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
                _mintPoolShare(poolAmountOut);
                _pushPoolShare(msg.sender, poolAmountOut);
                _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
                return poolAmountOut;
              }
              /**
               * @dev Pay up to `maxAmountIn` of `tokenIn` to mint exactly `poolAmountOut`.
               *
               * The pool implicitly swaps `(1- weightTokenIn) * tokenAmountIn` to the other
               * underlying tokens. Thus a swap fee is charged against the input tokens.
               *
               * @param tokenIn Token to send the pool
               * @param poolAmountOut Exact amount of pool tokens to mint
               * @param maxAmountIn Maximum amount of `tokenIn` to pay
               * @return tokenAmountIn - Amount of `tokenIn` paid
               */
              function joinswapPoolAmountOut(
                address tokenIn,
                uint256 poolAmountOut,
                uint256 maxAmountIn
              )
                external
                override
                _lock_
                _public_
                returns (uint256/* tokenAmountIn */)
              {
                uint256 maxPoolTokens = _maxPoolTokens;
                if (maxPoolTokens > 0) {
                  require(
                    badd(_totalSupply, poolAmountOut) <= maxPoolTokens,
                    "ERR_MAX_POOL_TOKENS"
                  );
                }
                (Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
                uint256 tokenAmountIn = calcSingleInGivenPoolOut(
                  inRecord.balance,
                  inRecord.denorm,
                  _totalSupply,
                  _totalWeight,
                  poolAmountOut,
                  _swapFee
                );
                require(tokenAmountIn != 0, "ERR_MATH_APPROX");
                require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");
                require(
                  tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
                  "ERR_MAX_IN_RATIO"
                );
                _updateInputToken(tokenIn, inRecord, badd(realInBalance, tokenAmountIn));
                emit LOG_JOIN(msg.sender, tokenIn, tokenAmountIn);
                _mintPoolShare(poolAmountOut);
                _pushPoolShare(msg.sender, poolAmountOut);
                _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
                return tokenAmountIn;
              }
              /**
               * @dev Burns `poolAmountIn` pool tokens in exchange for the amounts of each
               * underlying token's balance proportional to the ratio of tokens burned to
               * total pool supply. The amount of each token transferred to the caller must
               * be greater than or equal to the associated minimum output amount from the
               * `minAmountsOut` array.
               *
               * @param poolAmountIn Exact amount of pool tokens to burn
               * @param minAmountsOut Minimum amount of each token to receive, in the same
               * order as the pool's _tokens list.
               */
              function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut)
                external
                override
                _lock_
              {
                require(minAmountsOut.length == _tokens.length, "ERR_ARR_LEN");
                uint256 poolTotal = totalSupply();
                uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);
                uint256 pAiAfterExitFee = bsub(poolAmountIn, exitFee);
                uint256 ratio = bdiv(pAiAfterExitFee, poolTotal);
                require(ratio != 0, "ERR_MATH_APPROX");
                _pullPoolShare(msg.sender, poolAmountIn);
                _pushPoolShare(_controller, exitFee);
                _burnPoolShare(pAiAfterExitFee);
                for (uint256 i = 0; i < minAmountsOut.length; i++) {
                  address t = _tokens[i];
                  Record memory record = _records[t];
                  if (record.ready) {
                    uint256 tokenAmountOut = bmul(ratio, record.balance);
                    require(tokenAmountOut != 0, "ERR_MATH_APPROX");
                    require(tokenAmountOut >= minAmountsOut[i], "ERR_LIMIT_OUT");
                    _records[t].balance = bsub(record.balance, tokenAmountOut);
                    emit LOG_EXIT(msg.sender, t, tokenAmountOut);
                    _pushUnderlying(t, msg.sender, tokenAmountOut);
                  } else {
                    // If the token is not initialized, it can not exit the pool.
                    require(minAmountsOut[i] == 0, "ERR_OUT_NOT_READY");
                  }
                }
              }
              /**
               * @dev Burns `poolAmountIn` pool tokens in exchange for at least `minAmountOut`
               * of `tokenOut`. Returns the number of tokens sent to the caller.
               *
               * The pool implicitly burns the tokens for all underlying tokens and swaps them
               * to the desired output token. A swap fee is charged against the output tokens.
               *
               * @param tokenOut Token to receive
               * @param poolAmountIn Exact amount of pool tokens to burn
               * @param minAmountOut Minimum amount of `tokenOut` to receive
               * @return tokenAmountOut - Amount of `tokenOut` received
               */
              function exitswapPoolAmountIn(
                address tokenOut,
                uint256 poolAmountIn,
                uint256 minAmountOut
              )
                external
                override
                _lock_
                returns (uint256/* tokenAmountOut */)
              {
                Record memory outRecord = _getOutputToken(tokenOut);
                uint256 tokenAmountOut = calcSingleOutGivenPoolIn(
                  outRecord.balance,
                  outRecord.denorm,
                  _totalSupply,
                  _totalWeight,
                  poolAmountIn,
                  _swapFee
                );
                require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");
                require(
                  tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
                  "ERR_MAX_OUT_RATIO"
                );
                _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
                _records[tokenOut].balance = bsub(outRecord.balance, tokenAmountOut);
                _decreaseDenorm(outRecord, tokenOut);
                uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);
                emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
                _pullPoolShare(msg.sender, poolAmountIn);
                _burnPoolShare(bsub(poolAmountIn, exitFee));
                _pushPoolShare(_controller, exitFee);
                return tokenAmountOut;
              }
              /**
               * @dev Burn up to `maxPoolAmountIn` for exactly `tokenAmountOut` of `tokenOut`.
               * Returns the number of pool tokens burned.
               *
               * The pool implicitly burns the tokens for all underlying tokens and swaps them
               * to the desired output token. A swap fee is charged against the output tokens.
               *
               * @param tokenOut Token to receive
               * @param tokenAmountOut Exact amount of `tokenOut` to receive
               * @param maxPoolAmountIn Maximum amount of pool tokens to burn
               * @return poolAmountIn - Amount of pool tokens burned
               */
              function exitswapExternAmountOut(
                address tokenOut,
                uint256 tokenAmountOut,
                uint256 maxPoolAmountIn
              )
                external
                override
                _lock_
                returns (uint256/* poolAmountIn */)
              {
                Record memory outRecord = _getOutputToken(tokenOut);
                require(
                  tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
                  "ERR_MAX_OUT_RATIO"
                );
                uint256 poolAmountIn = calcPoolInGivenSingleOut(
                  outRecord.balance,
                  outRecord.denorm,
                  _totalSupply,
                  _totalWeight,
                  tokenAmountOut,
                  _swapFee
                );
                require(poolAmountIn != 0, "ERR_MATH_APPROX");
                require(poolAmountIn <= maxPoolAmountIn, "ERR_LIMIT_IN");
                _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
                _records[tokenOut].balance = bsub(outRecord.balance, tokenAmountOut);
                _decreaseDenorm(outRecord, tokenOut);
                uint256 exitFee = bmul(poolAmountIn, EXIT_FEE);
                emit LOG_EXIT(msg.sender, tokenOut, tokenAmountOut);
                _pullPoolShare(msg.sender, poolAmountIn);
                _burnPoolShare(bsub(poolAmountIn, exitFee));
                _pushPoolShare(_controller, exitFee);
                return poolAmountIn;
              }
            /* ==========  Other  ========== */
              /**
               * @dev Absorb any tokens that have been sent to the pool.
               * If the token is not bound, it will be sent to the unbound
               * token handler.
               */
              function gulp(address token) external override _lock_ {
                Record storage record = _records[token];
                uint256 balance = IERC20(token).balanceOf(address(this));
                if (record.bound) {
                  if (!record.ready) {
                    uint256 minimumBalance = _minimumBalances[token];
                    if (balance >= minimumBalance) {
                      _minimumBalances[token] = 0;
                      record.ready = true;
                      emit LOG_TOKEN_READY(token);
                      uint256 additionalBalance = bsub(balance, minimumBalance);
                      uint256 balRatio = bdiv(additionalBalance, minimumBalance);
                      uint96 newDenorm = uint96(badd(MIN_WEIGHT, bmul(MIN_WEIGHT, balRatio)));
                      record.denorm = newDenorm;
                      record.lastDenormUpdate = uint40(now);
                      _totalWeight = badd(_totalWeight, newDenorm);
                      emit LOG_DENORM_UPDATED(token, record.denorm);
                    }
                  }
                  _records[token].balance = balance;
                } else {
                  _pushUnderlying(token, address(_unbindHandler), balance);
                  _unbindHandler.handleUnbindToken(token, balance);
                }
              }
            /* ==========  Flash Loan  ========== */
              /**
               * @dev Execute a flash loan, transferring `amount` of `token` to `recipient`.
               * `amount` must be repaid with `swapFee` interest by the end of the transaction.
               *
               * @param recipient Must implement the IFlashLoanRecipient interface
               * @param token Token to borrow
               * @param amount Amount to borrow
               * @param data Data to send to the recipient in `receiveFlashLoan` call
               */
              function flashBorrow(
                address recipient,
                address token,
                uint256 amount,
                bytes calldata data
              )
                external
                override
                _lock_
              {
                Record storage record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
                uint256 balStart = IERC20(token).balanceOf(address(this));
                require(balStart >= amount, "ERR_INSUFFICIENT_BAL");
                _pushUnderlying(token, address(recipient), amount);
                uint256 fee = bmul(balStart, _swapFee);
                uint256 amountDue = badd(amount, fee);
                IFlashLoanRecipient(recipient).receiveFlashLoan(token, amount, amountDue, data);
                uint256 balEnd = IERC20(token).balanceOf(address(this));
                require(
                  balEnd > balStart && balEnd >= amountDue,
                  "ERR_INSUFFICIENT_PAYMENT"
                );
                record.balance = balEnd;
                // If the payment brings the token above its minimum balance,
                // clear the minimum and mark the token as ready.
                if (!record.ready) {
                  uint256 minimumBalance = _minimumBalances[token];
                  if (balEnd >= minimumBalance) {
                    _minimumBalances[token] = 0;
                    record.ready = true;
                    emit LOG_TOKEN_READY(token);
                    uint256 additionalBalance = bsub(balEnd, minimumBalance);
                    uint256 balRatio = bdiv(additionalBalance, minimumBalance);
                    uint96 newDenorm = uint96(badd(MIN_WEIGHT, bmul(MIN_WEIGHT, balRatio)));
                    record.denorm = newDenorm;
                    record.lastDenormUpdate = uint40(now);
                    _totalWeight = badd(_totalWeight, newDenorm);
                    emit LOG_DENORM_UPDATED(token, record.denorm);
                  }
                }
              }
            /* ==========  Token Swaps  ========== */
              /**
               * @dev Execute a token swap with a specified amount of input
               * tokens and a minimum amount of output tokens.
               *
               * Note: Will revert if `tokenOut` is uninitialized.
               *
               * @param tokenIn Token to swap in
               * @param tokenAmountIn Exact amount of `tokenIn` to swap in
               * @param tokenOut Token to swap out
               * @param minAmountOut Minimum amount of `tokenOut` to receive
               * @param maxPrice Maximum ratio of input to output tokens
               * @return (tokenAmountOut, spotPriceAfter)
               */
              function swapExactAmountIn(
                address tokenIn,
                uint256 tokenAmountIn,
                address tokenOut,
                uint256 minAmountOut,
                uint256 maxPrice
              )
                external
                override
                _lock_
                _public_
                returns (uint256/* tokenAmountOut */, uint256/* spotPriceAfter */)
              {
                (Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
                Record memory outRecord = _getOutputToken(tokenOut);
                require(
                  tokenAmountIn <= bmul(inRecord.balance, MAX_IN_RATIO),
                  "ERR_MAX_IN_RATIO"
                );
                uint256 spotPriceBefore = calcSpotPrice(
                  inRecord.balance,
                  inRecord.denorm,
                  outRecord.balance,
                  outRecord.denorm,
                  _swapFee
                );
                require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");
                uint256 tokenAmountOut = calcOutGivenIn(
                  inRecord.balance,
                  inRecord.denorm,
                  outRecord.balance,
                  outRecord.denorm,
                  tokenAmountIn,
                  _swapFee
                );
                require(tokenAmountOut >= minAmountOut, "ERR_LIMIT_OUT");
                _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
                _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
                realInBalance = badd(realInBalance, tokenAmountIn);
                _updateInputToken(tokenIn, inRecord, realInBalance);
                if (inRecord.ready) {
                  inRecord.balance = realInBalance;
                }
                // Update the in-memory record for the spotPriceAfter calculation,
                // then update the storage record with the local balance.
                outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
                _records[tokenOut].balance = outRecord.balance;
                // If needed, update the output token's weight.
                _decreaseDenorm(outRecord, tokenOut);
                uint256 spotPriceAfter = calcSpotPrice(
                  inRecord.balance,
                  inRecord.denorm,
                  outRecord.balance,
                  outRecord.denorm,
                  _swapFee
                );
                require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX_2");
                require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
                require(
                  spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut),
                  "ERR_MATH_APPROX"
                );
                emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);
                return (tokenAmountOut, spotPriceAfter);
              }
              /**
               * @dev Trades at most `maxAmountIn` of `tokenIn` for exactly `tokenAmountOut`
               * of `tokenOut`.
               *
               * Returns the actual input amount and the new spot price after the swap,
               * which can not exceed `maxPrice`.
               *
               * @param tokenIn Token to swap in
               * @param maxAmountIn Maximum amount of `tokenIn` to pay
               * @param tokenOut Token to swap out
               * @param tokenAmountOut Exact amount of `tokenOut` to receive
               * @param maxPrice Maximum ratio of input to output tokens
               * @return (tokenAmountIn, spotPriceAfter)
               */
              function swapExactAmountOut(
                address tokenIn,
                uint256 maxAmountIn,
                address tokenOut,
                uint256 tokenAmountOut,
                uint256 maxPrice
              )
                external
                override
                _lock_
                _public_
                returns (uint256 /* tokenAmountIn */, uint256 /* spotPriceAfter */)
              {
                (Record memory inRecord, uint256 realInBalance) = _getInputToken(tokenIn);
                Record memory outRecord = _getOutputToken(tokenOut);
                require(
                  tokenAmountOut <= bmul(outRecord.balance, MAX_OUT_RATIO),
                  "ERR_MAX_OUT_RATIO"
                );
                uint256 spotPriceBefore = calcSpotPrice(
                  inRecord.balance,
                  inRecord.denorm,
                  outRecord.balance,
                  outRecord.denorm,
                  _swapFee
                );
                require(spotPriceBefore <= maxPrice, "ERR_BAD_LIMIT_PRICE");
                uint256 tokenAmountIn = calcInGivenOut(
                  inRecord.balance,
                  inRecord.denorm,
                  outRecord.balance,
                  outRecord.denorm,
                  tokenAmountOut,
                  _swapFee
                );
                require(tokenAmountIn <= maxAmountIn, "ERR_LIMIT_IN");
                _pullUnderlying(tokenIn, msg.sender, tokenAmountIn);
                _pushUnderlying(tokenOut, msg.sender, tokenAmountOut);
                // Update the balance and (if necessary) weight of the input token.
                realInBalance = badd(realInBalance, tokenAmountIn);
                _updateInputToken(tokenIn, inRecord, realInBalance);
                if (inRecord.ready) {
                  inRecord.balance = realInBalance;
                }
                // Update the in-memory record for the spotPriceAfter calculation,
                // then update the storage record with the local balance.
                outRecord.balance = bsub(outRecord.balance, tokenAmountOut);
                _records[tokenOut].balance = outRecord.balance;
                // If needed, update the output token's weight.
                _decreaseDenorm(outRecord, tokenOut);
                uint256 spotPriceAfter = calcSpotPrice(
                  inRecord.balance,
                  inRecord.denorm,
                  outRecord.balance,
                  outRecord.denorm,
                  _swapFee
                );
                require(spotPriceAfter >= spotPriceBefore, "ERR_MATH_APPROX");
                require(spotPriceAfter <= maxPrice, "ERR_LIMIT_PRICE");
                require(
                  spotPriceBefore <= bdiv(tokenAmountIn, tokenAmountOut),
                  "ERR_MATH_APPROX"
                );
                emit LOG_SWAP(msg.sender, tokenIn, tokenOut, tokenAmountIn, tokenAmountOut);
                return (tokenAmountIn, spotPriceAfter);
              }
            /* ==========  Config Queries  ========== */
              /**
               * @dev Check if swapping tokens and joining the pool is allowed.
               */
              function isPublicSwap() external view override returns (bool) {
                return _publicSwap;
              }
              function getSwapFee() external view override _viewlock_ returns (uint256/* swapFee */) {
                return _swapFee;
              }
              /**
               * @dev Returns the controller address.
               */
              function getController() external view override returns (address)
              {
                return _controller;
              }
            /* ==========  Token Queries  ========== */
              function getMaxPoolTokens() external view override returns (uint256) {
                return _maxPoolTokens;
              }
              /**
               * @dev Check if a token is bound to the pool.
               */
              function isBound(address t) external view override returns (bool) {
                return _records[t].bound;
              }
              /**
               * @dev Get the number of tokens bound to the pool.
               */
              function getNumTokens() external view override returns (uint256) {
                return _tokens.length;
              }
              /**
               * @dev Get all bound tokens.
               */
              function getCurrentTokens()
                external
                view
                override
                _viewlock_
                returns (address[] memory tokens)
              {
                tokens = _tokens;
              }
              /**
               * @dev Returns the list of tokens which have a desired weight above 0.
               * Tokens with a desired weight of 0 are set to be phased out of the pool.
               */
              function getCurrentDesiredTokens()
                external
                view
                override
                _viewlock_
                returns (address[] memory tokens)
              {
                address[] memory tempTokens = _tokens;
                tokens = new address[](tempTokens.length);
                uint256 usedIndex = 0;
                for (uint256 i = 0; i < tokens.length; i++) {
                  address token = tempTokens[i];
                  if (_records[token].desiredDenorm > 0) {
                    tokens[usedIndex++] = token;
                  }
                }
                assembly { mstore(tokens, usedIndex) }
              }
              /**
               * @dev Returns the denormalized weight of a bound token.
               */
              function getDenormalizedWeight(address token)
                external
                view
                override
                _viewlock_
                returns (uint256/* denorm */)
              {
                require(_records[token].bound, "ERR_NOT_BOUND");
                return _records[token].denorm;
              }
              /**
               * @dev Returns the record for a token bound to the pool.
               */
              function getTokenRecord(address token)
                external
                view
                override
                _viewlock_
                returns (Record memory record)
              {
                record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
              }
              /**
               * @dev Finds the first token which is both initialized and has a
               * desired weight above 0, then returns the address of that token
               * and the extrapolated value of the pool in terms of that token.
               *
               * The value is extrapolated by multiplying the token's
               * balance by the reciprocal of its normalized weight.
               * @return (token, extrapolatedValue)
               */
              function extrapolatePoolValueFromToken()
                external
                view
                override
                _viewlock_
                returns (address/* token */, uint256/* extrapolatedValue */)
              {
                address token;
                uint256 extrapolatedValue;
                uint256 len = _tokens.length;
                for (uint256 i = 0; i < len; i++) {
                  token = _tokens[i];
                  Record storage record = _records[token];
                  if (record.ready && record.desiredDenorm > 0) {
                    extrapolatedValue = bmul(
                      record.balance,
                      bdiv(_totalWeight, record.denorm)
                    );
                    break;
                  }
                }
                require(extrapolatedValue > 0, "ERR_NONE_READY");
                return (token, extrapolatedValue);
              }
              /**
               * @dev Get the total denormalized weight of the pool.
               */
              function getTotalDenormalizedWeight()
                external
                view
                override
                _viewlock_
                returns (uint256)
              {
                return _totalWeight;
              }
              /**
               * @dev Returns the stored balance of a bound token.
               */
              function getBalance(address token) external view override _viewlock_ returns (uint256) {
                Record storage record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
                return record.balance;
              }
              /**
               * @dev Get the minimum balance of an uninitialized token.
               * Note: Throws if the token is initialized.
               */
              function getMinimumBalance(address token) external view override _viewlock_ returns (uint256) {
                Record memory record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
                require(!record.ready, "ERR_READY");
                return _minimumBalances[token];
              }
              /**
               * @dev Returns the balance of a token which is used in price
               * calculations. If the token is initialized, this is the
               * stored balance; if not, this is the minimum balance.
               */
              function getUsedBalance(address token) external view override _viewlock_ returns (uint256) {
                Record memory record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
                if (!record.ready) {
                  return _minimumBalances[token];
                }
                return record.balance;
              }
            /* ==========  Price Queries  ========== */
              /**
               * @dev Returns the spot price for `tokenOut` in terms of `tokenIn`.
               */
              function getSpotPrice(address tokenIn, address tokenOut)
                external
                view
                override
                _viewlock_
                returns (uint256)
              {
                (Record memory inRecord,) = _getInputToken(tokenIn);
                Record memory outRecord = _getOutputToken(tokenOut);
                return
                  calcSpotPrice(
                    inRecord.balance,
                    inRecord.denorm,
                    outRecord.balance,
                    outRecord.denorm,
                    _swapFee
                  );
              }
            /* ==========  Pool Share Internal Functions  ========== */
              function _pullPoolShare(address from, uint256 amount) internal {
                _pull(from, amount);
              }
              function _pushPoolShare(address to, uint256 amount) internal {
                _push(to, amount);
              }
              function _mintPoolShare(uint256 amount) internal {
                _mint(amount);
              }
              function _burnPoolShare(uint256 amount) internal {
                _burn(amount);
              }
            /* ==========  Underlying Token Internal Functions  ========== */
              // 'Underlying' token-manipulation functions make external calls but are NOT locked
              // You must `_lock_` or otherwise ensure reentry-safety
              function _pullUnderlying(
                address erc20,
                address from,
                uint256 amount
              ) internal {
                (bool success, bytes memory data) = erc20.call(
                  abi.encodeWithSelector(
                    IERC20.transferFrom.selector,
                    from,
                    address(this),
                    amount
                  )
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  "ERR_ERC20_FALSE"
                );
              }
              function _pushUnderlying(
                address erc20,
                address to,
                uint256 amount
              ) internal {
                (bool success, bytes memory data) = erc20.call(
                  abi.encodeWithSelector(
                    IERC20.transfer.selector,
                    to,
                    amount
                  )
                );
                require(
                  success && (data.length == 0 || abi.decode(data, (bool))),
                  "ERR_ERC20_FALSE"
                );
              }
            /* ==========  Token Management Internal Functions  ========== */
              /**
               * @dev Bind a token by address without actually depositing a balance.
               * The token will be unable to be swapped out until it reaches the minimum balance.
               * Note: Token must not already be bound.
               * Note: `minimumBalance` should represent an amount of the token which is worth
               * the portion of the current pool value represented by the minimum weight.
               * @param token Address of the token to bind
               * @param minimumBalance minimum balance to reach before the token can be swapped out
               * @param desiredDenorm Desired weight for the token.
               */
              function _bind(
                address token,
                uint256 minimumBalance,
                uint96 desiredDenorm
              ) internal {
                require(!_records[token].bound, "ERR_IS_BOUND");
                require(desiredDenorm >= MIN_WEIGHT, "ERR_MIN_WEIGHT");
                require(desiredDenorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
                require(minimumBalance >= MIN_BALANCE, "ERR_MIN_BALANCE");
                _records[token] = Record({
                  bound: true,
                  ready: false,
                  lastDenormUpdate: 0,
                  denorm: 0,
                  desiredDenorm: desiredDenorm,
                  index: uint8(_tokens.length),
                  balance: 0
                });
                _tokens.push(token);
                _minimumBalances[token] = minimumBalance;
                emit LOG_TOKEN_ADDED(token, desiredDenorm, minimumBalance);
              }
              /**
               * @dev Remove a token from the pool.
               * Replaces the address in the tokens array with the last address,
               * then removes it from the array.
               * Note: This should only be called after the total weight has been adjusted.
               * Note: Must be called in a function with:
               * - _lock_ modifier to prevent reentrance
               * - requirement that the token is bound
               */
              function _unbind(address token) internal {
                Record memory record = _records[token];
                uint256 tokenBalance = record.balance;
                // Swap the token-to-unbind with the last token,
                // then delete the last token
                uint256 index = record.index;
                uint256 last = _tokens.length - 1;
                // Only swap the token with the last token if it is not
                // already at the end of the array.
                if (index != last) {
                  _tokens[index] = _tokens[last];
                  _records[_tokens[index]].index = uint8(index);
                }
                _tokens.pop();
                _records[token] = Record({
                  bound: false,
                  ready: false,
                  lastDenormUpdate: 0,
                  denorm: 0,
                  desiredDenorm: 0,
                  index: 0,
                  balance: 0
                });
                // transfer any remaining tokens out
                _pushUnderlying(token, address(_unbindHandler), tokenBalance);
                _unbindHandler.handleUnbindToken(token, tokenBalance);
                emit LOG_TOKEN_REMOVED(token);
              }
              function _setDesiredDenorm(address token, uint96 desiredDenorm) internal {
                Record storage record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
                // If the desired weight is 0, this will trigger a gradual unbinding of the token.
                // Therefore the weight only needs to be above the minimum weight if it isn't 0.
                require(
                  desiredDenorm >= MIN_WEIGHT || desiredDenorm == 0,
                  "ERR_MIN_WEIGHT"
                );
                require(desiredDenorm <= MAX_WEIGHT, "ERR_MAX_WEIGHT");
                record.desiredDenorm = desiredDenorm;
                emit LOG_DESIRED_DENORM_SET(token, desiredDenorm);
              }
              function _increaseDenorm(Record memory record, address token) internal {
                // If the weight does not need to increase or the token is not
                // initialized, don't do anything.
                if (
                  record.denorm >= record.desiredDenorm ||
                  !record.ready ||
                  now - record.lastDenormUpdate < WEIGHT_UPDATE_DELAY
                ) return;
                uint96 oldWeight = record.denorm;
                uint96 denorm = record.desiredDenorm;
                uint256 maxDiff = bmul(oldWeight, WEIGHT_CHANGE_PCT);
                uint256 diff = bsub(denorm, oldWeight);
                if (diff > maxDiff) {
                  denorm = uint96(badd(oldWeight, maxDiff));
                  diff = maxDiff;
                }
                _totalWeight = badd(_totalWeight, diff);
                require(_totalWeight <= MAX_TOTAL_WEIGHT, "ERR_MAX_TOTAL_WEIGHT");
                // Update the in-memory denorm value for spot-price computations.
                record.denorm = denorm;
                // Update the storage record
                _records[token].denorm = denorm;
                _records[token].lastDenormUpdate = uint40(now);
                emit LOG_DENORM_UPDATED(token, denorm);
              }
              function _decreaseDenorm(Record memory record, address token) internal {
                // If the weight does not need to decrease, don't do anything.
                if (
                  record.denorm <= record.desiredDenorm ||
                  !record.ready ||
                  now - record.lastDenormUpdate < WEIGHT_UPDATE_DELAY
                ) return;
                uint96 oldWeight = record.denorm;
                uint96 denorm = record.desiredDenorm;
                uint256 maxDiff = bmul(oldWeight, WEIGHT_CHANGE_PCT);
                uint256 diff = bsub(oldWeight, denorm);
                if (diff > maxDiff) {
                  denorm = uint96(bsub(oldWeight, maxDiff));
                  diff = maxDiff;
                }
                if (denorm <= MIN_WEIGHT) {
                  denorm = 0;
                  _totalWeight = bsub(_totalWeight, denorm);
                  // Because this is removing the token from the pool, the
                  // in-memory denorm value is irrelevant, as it is only used
                  // to calculate the new spot price, but the spot price calc
                  // will throw if it is passed 0 for the denorm.
                  _unbind(token);
                } else {
                  _totalWeight = bsub(_totalWeight, diff);
                  // Update the in-memory denorm value for spot-price computations.
                  record.denorm = denorm;
                  // Update the stored denorm value
                  _records[token].denorm = denorm;
                  _records[token].lastDenormUpdate = uint40(now);
                  emit LOG_DENORM_UPDATED(token, denorm);
                }
              }
              /**
               * @dev Handles weight changes and initialization of an
               * input token.
               *
               * If the token is not initialized and the new balance is
               * still below the minimum, this will not do anything.
               *
               * If the token is not initialized but the new balance will
               * bring the token above the minimum balance, this will
               * mark the token as initialized, remove the minimum
               * balance and set the weight to the minimum weight plus
               * 1%.
               *
               *
               * @param token Address of the input token
               * @param record Token record with minimums applied to the balance
               * and weight if the token was uninitialized.
               */
              function _updateInputToken(
                address token,
                Record memory record,
                uint256 realBalance
              )
                internal
              {
                if (!record.ready) {
                  // Check if the minimum balance has been reached
                  if (realBalance >= record.balance) {
                    // Remove the minimum balance record
                    _minimumBalances[token] = 0;
                    // Mark the token as initialized
                    _records[token].ready = true;
                    record.ready = true;
                    emit LOG_TOKEN_READY(token);
                    // Set the initial denorm value to the minimum weight times one plus
                    // the ratio of the increase in balance over the minimum to the minimum
                    // balance.
                    // weight = (1 + ((bal - min_bal) / min_bal)) * min_weight
                    uint256 additionalBalance = bsub(realBalance, record.balance);
                    uint256 balRatio = bdiv(additionalBalance, record.balance);
                    record.denorm = uint96(badd(MIN_WEIGHT, bmul(MIN_WEIGHT, balRatio)));
                    _records[token].denorm = record.denorm;
                    _records[token].lastDenormUpdate = uint40(now);
                    _totalWeight = badd(_totalWeight, record.denorm);
                    emit LOG_DENORM_UPDATED(token, record.denorm);
                  } else {
                    uint256 realToMinRatio = bdiv(
                      bsub(record.balance, realBalance),
                      record.balance
                    );
                    uint256 weightPremium = bmul(MIN_WEIGHT / 10, realToMinRatio);
                    record.denorm = uint96(badd(MIN_WEIGHT, weightPremium));
                  }
                  // If the token is still not ready, do not adjust the weight.
                } else {
                  // If the token is already initialized, update the weight (if any adjustment
                  // is needed).
                  _increaseDenorm(record, token);
                }
                // Regardless of whether the token is initialized, store the actual new balance.
                _records[token].balance = realBalance;
              }
            /* ==========  Token Query Internal Functions  ========== */
              /**
               * @dev Get the record for a token which is being swapped in.
               * The token must be bound to the pool. If the token is not
               * initialized (meaning it does not have the minimum balance)
               * this function will return the actual balance of the token
               * which the pool holds, but set the record's balance and weight
               * to the token's minimum balance and the pool's minimum weight.
               * This allows the token swap to be priced correctly even if the
               * pool does not own any of the tokens.
               */
              function _getInputToken(address token)
                internal
                view
                returns (Record memory record, uint256 realBalance)
              {
                record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
                realBalance = record.balance;
                // If the input token is not initialized, we use the minimum
                // initial weight and minimum initial balance instead of the
                // real values for price and output calculations.
                if (!record.ready) {
                  record.balance = _minimumBalances[token];
                  uint256 realToMinRatio = bdiv(
                    bsub(record.balance, realBalance),
                    record.balance
                  );
                  uint256 weightPremium = bmul(MIN_WEIGHT / 10, realToMinRatio);
                  record.denorm = uint96(badd(MIN_WEIGHT, weightPremium));
                }
              }
              function _getOutputToken(address token)
                internal
                view
                returns (Record memory record)
              {
                record = _records[token];
                require(record.bound, "ERR_NOT_BOUND");
                // Tokens which have not reached their minimum balance can not be
                // swapped out.
                require(record.ready, "ERR_OUT_NOT_READY");
              }
            }
            interface TokenUnbindHandler {
              /**
               * @dev Receive `amount` of `token` from the pool.
               */
              function handleUnbindToken(address token, uint256 amount) external;
            }// SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            interface IFlashLoanRecipient {
              function receiveFlashLoan(
                address tokenBorrowed,
                uint256 amountBorrowed,
                uint256 amountDue,
                bytes calldata data
              ) external;
            }// SPDX-License-Identifier: GPL-3.0
            pragma solidity ^0.6.0;
            pragma experimental ABIEncoderV2;
            interface IIndexPool {
              /**
               * @dev Token record data structure
               * @param bound is token bound to pool
               * @param ready has token been initialized
               * @param lastDenormUpdate timestamp of last denorm change
               * @param denorm denormalized weight
               * @param desiredDenorm desired denormalized weight (used for incremental changes)
               * @param index index of address in tokens array
               * @param balance token balance
               */
              struct Record {
                bool bound;
                bool ready;
                uint40 lastDenormUpdate;
                uint96 denorm;
                uint96 desiredDenorm;
                uint8 index;
                uint256 balance;
              }
              event LOG_SWAP(
                address indexed caller,
                address indexed tokenIn,
                address indexed tokenOut,
                uint256 tokenAmountIn,
                uint256 tokenAmountOut
              );
              event LOG_JOIN(
                address indexed caller,
                address indexed tokenIn,
                uint256 tokenAmountIn
              );
              event LOG_EXIT(
                address indexed caller,
                address indexed tokenOut,
                uint256 tokenAmountOut
              );
              event LOG_DENORM_UPDATED(address indexed token, uint256 newDenorm);
              event LOG_DESIRED_DENORM_SET(address indexed token, uint256 desiredDenorm);
              event LOG_TOKEN_REMOVED(address token);
              event LOG_TOKEN_ADDED(
                address indexed token,
                uint256 desiredDenorm,
                uint256 minimumBalance
              );
              event LOG_MINIMUM_BALANCE_UPDATED(address token, uint256 minimumBalance);
              event LOG_TOKEN_READY(address indexed token);
              event LOG_PUBLIC_SWAP_ENABLED();
              event LOG_MAX_TOKENS_UPDATED(uint256 maxPoolTokens);
              event LOG_SWAP_FEE_UPDATED(uint256 swapFee);
              function configure(
                address controller,
                string calldata name,
                string calldata symbol
              ) external;
              function initialize(
                address[] calldata tokens,
                uint256[] calldata balances,
                uint96[] calldata denorms,
                address tokenProvider,
                address unbindHandler
              ) external;
              function setMaxPoolTokens(uint256 maxPoolTokens) external;
              function setSwapFee(uint256 swapFee) external;
              function reweighTokens(
                address[] calldata tokens,
                uint96[] calldata desiredDenorms
              ) external;
              function reindexTokens(
                address[] calldata tokens,
                uint96[] calldata desiredDenorms,
                uint256[] calldata minimumBalances
              ) external;
              function setMinimumBalance(address token, uint256 minimumBalance) external;
              function joinPool(uint256 poolAmountOut, uint256[] calldata maxAmountsIn) external;
              function joinswapExternAmountIn(
                address tokenIn,
                uint256 tokenAmountIn,
                uint256 minPoolAmountOut
              ) external returns (uint256/* poolAmountOut */);
              function joinswapPoolAmountOut(
                address tokenIn,
                uint256 poolAmountOut,
                uint256 maxAmountIn
              ) external returns (uint256/* tokenAmountIn */);
              function exitPool(uint256 poolAmountIn, uint256[] calldata minAmountsOut) external;
              function exitswapPoolAmountIn(
                address tokenOut,
                uint256 poolAmountIn,
                uint256 minAmountOut
              )
                external returns (uint256/* tokenAmountOut */);
              function exitswapExternAmountOut(
                address tokenOut,
                uint256 tokenAmountOut,
                uint256 maxPoolAmountIn
              ) external returns (uint256/* poolAmountIn */);
              function gulp(address token) external;
              function flashBorrow(
                address recipient,
                address token,
                uint256 amount,
                bytes calldata data
              ) external;
              function swapExactAmountIn(
                address tokenIn,
                uint256 tokenAmountIn,
                address tokenOut,
                uint256 minAmountOut,
                uint256 maxPrice
              ) external returns (uint256/* tokenAmountOut */, uint256/* spotPriceAfter */);
              function swapExactAmountOut(
                address tokenIn,
                uint256 maxAmountIn,
                address tokenOut,
                uint256 tokenAmountOut,
                uint256 maxPrice
              ) external returns (uint256 /* tokenAmountIn */, uint256 /* spotPriceAfter */);
              function isPublicSwap() external view returns (bool);
              function getSwapFee() external view returns (uint256/* swapFee */);
              function getController() external view returns (address);
              function getMaxPoolTokens() external view returns (uint256);
              function isBound(address t) external view returns (bool);
              function getNumTokens() external view returns (uint256);
              function getCurrentTokens() external view returns (address[] memory tokens);
              function getCurrentDesiredTokens() external view returns (address[] memory tokens);
              function getDenormalizedWeight(address token) external view returns (uint256/* denorm */);
              function getTokenRecord(address token) external view returns (Record memory record);
              function extrapolatePoolValueFromToken() external view returns (address/* token */, uint256/* extrapolatedValue */);
              function getTotalDenormalizedWeight() external view returns (uint256);
              function getBalance(address token) external view returns (uint256);
              function getMinimumBalance(address token) external view returns (uint256);
              function getUsedBalance(address token) external view returns (uint256);
              function getSpotPrice(address tokenIn, address tokenOut) external view returns (uint256);
            }

            File 5 of 5: GasToken2
            pragma solidity ^0.4.10;
            
            contract GasToken2 {
                //////////////////////////////////////////////////////////////////////////
                // RLP.sol
                // Due to some unexplained bug, we get a slightly different bytecode if 
                // we use an import, and are then unable to verify the code in Etherscan
                //////////////////////////////////////////////////////////////////////////
                
                uint256 constant ADDRESS_BYTES = 20;
                uint256 constant MAX_SINGLE_BYTE = 128;
                uint256 constant MAX_NONCE = 256**9 - 1;
            
                // count number of bytes required to represent an unsigned integer
                function count_bytes(uint256 n) constant internal returns (uint256 c) {
                    uint i = 0;
                    uint mask = 1;
                    while (n >= mask) {
                        i += 1;
                        mask *= 256;
                    }
            
                    return i;
                }
            
                function mk_contract_address(address a, uint256 n) constant internal returns (address rlp) {
                    /*
                     * make sure the RLP encoding fits in one word:
                     * total_length      1 byte
                     * address_length    1 byte
                     * address          20 bytes
                     * nonce_length      1 byte (or 0)
                     * nonce           1-9 bytes
                     *                ==========
                     *                24-32 bytes
                     */
                    require(n <= MAX_NONCE);
            
                    // number of bytes required to write down the nonce
                    uint256 nonce_bytes;
                    // length in bytes of the RLP encoding of the nonce
                    uint256 nonce_rlp_len;
            
                    if (0 < n && n < MAX_SINGLE_BYTE) {
                        // nonce fits in a single byte
                        // RLP(nonce) = nonce
                        nonce_bytes = 1;
                        nonce_rlp_len = 1;
                    } else {
                        // RLP(nonce) = [num_bytes_in_nonce nonce]
                        nonce_bytes = count_bytes(n);
                        nonce_rlp_len = nonce_bytes + 1;
                    }
            
                    // [address_length(1) address(20) nonce_length(0 or 1) nonce(1-9)]
                    uint256 tot_bytes = 1 + ADDRESS_BYTES + nonce_rlp_len;
            
                    // concatenate all parts of the RLP encoding in the leading bytes of
                    // one 32-byte word
                    uint256 word = ((192 + tot_bytes) * 256**31) +
                                   ((128 + ADDRESS_BYTES) * 256**30) +
                                   (uint256(a) * 256**10);
            
                    if (0 < n && n < MAX_SINGLE_BYTE) {
                        word += n * 256**9;
                    } else {
                        word += (128 + nonce_bytes) * 256**9;
                        word += n * 256**(9 - nonce_bytes);
                    }
            
                    uint256 hash;
            
                    assembly {
                        let mem_start := mload(0x40)        // get a pointer to free memory
                        mstore(0x40, add(mem_start, 0x20))  // update the pointer
            
                        mstore(mem_start, word)             // store the rlp encoding
                        hash := sha3(mem_start,
                                     add(tot_bytes, 1))     // hash the rlp encoding
                    }
            
                    // interpret hash as address (20 least significant bytes)
                    return address(hash);
                }
                
                //////////////////////////////////////////////////////////////////////////
                // Generic ERC20
                //////////////////////////////////////////////////////////////////////////
            
                // owner -> amount
                mapping(address => uint256) s_balances;
                // owner -> spender -> max amount
                mapping(address => mapping(address => uint256)) s_allowances;
            
                event Transfer(address indexed from, address indexed to, uint256 value);
            
                event Approval(address indexed owner, address indexed spender, uint256 value);
            
                // Spec: Get the account balance of another account with address `owner`
                function balanceOf(address owner) public constant returns (uint256 balance) {
                    return s_balances[owner];
                }
            
                function internalTransfer(address from, address to, uint256 value) internal returns (bool success) {
                    if (value <= s_balances[from]) {
                        s_balances[from] -= value;
                        s_balances[to] += value;
                        Transfer(from, to, value);
                        return true;
                    } else {
                        return false;
                    }
                }
            
                // Spec: Send `value` amount of tokens to address `to`
                function transfer(address to, uint256 value) public returns (bool success) {
                    address from = msg.sender;
                    return internalTransfer(from, to, value);
                }
            
                // Spec: Send `value` amount of tokens from address `from` to address `to`
                function transferFrom(address from, address to, uint256 value) public returns (bool success) {
                    address spender = msg.sender;
                    if(value <= s_allowances[from][spender] && internalTransfer(from, to, value)) {
                        s_allowances[from][spender] -= value;
                        return true;
                    } else {
                        return false;
                    }
                }
            
                // Spec: Allow `spender` to withdraw from your account, multiple times, up
                // to the `value` amount. If this function is called again it overwrites the
                // current allowance with `value`.
                function approve(address spender, uint256 value) public returns (bool success) {
                    address owner = msg.sender;
                    if (value != 0 && s_allowances[owner][spender] != 0) {
                        return false;
                    }
                    s_allowances[owner][spender] = value;
                    Approval(owner, spender, value);
                    return true;
                }
            
                // Spec: Returns the `amount` which `spender` is still allowed to withdraw
                // from `owner`.
                // What if the allowance is higher than the balance of the `owner`?
                // Callers should be careful to use min(allowance, balanceOf) to make sure
                // that the allowance is actually present in the account!
                function allowance(address owner, address spender) public constant returns (uint256 remaining) {
                    return s_allowances[owner][spender];
                }
            
                //////////////////////////////////////////////////////////////////////////
                // GasToken specifics
                //////////////////////////////////////////////////////////////////////////
            
                uint8 constant public decimals = 2;
                string constant public name = "Gastoken.io";
                string constant public symbol = "GST2";
            
                // We build a queue of nonces at which child contracts are stored. s_head is
                // the nonce at the head of the queue, s_tail is the nonce behind the tail
                // of the queue. The queue grows at the head and shrinks from the tail.
                // Note that when and only when a contract CREATEs another contract, the
                // creating contract's nonce is incremented.
                // The first child contract is created with nonce == 1, the second child
                // contract is created with nonce == 2, and so on...
                // For example, if there are child contracts at nonces [2,3,4],
                // then s_head == 4 and s_tail == 1. If there are no child contracts,
                // s_head == s_tail.
                uint256 s_head;
                uint256 s_tail;
            
                // totalSupply gives  the number of tokens currently in existence
                // Each token corresponds to one child contract that can be SELFDESTRUCTed
                // for a gas refund.
                function totalSupply() public constant returns (uint256 supply) {
                    return s_head - s_tail;
                }
            
                // Creates a child contract that can only be destroyed by this contract.
                function makeChild() internal returns (address addr) {
                    assembly {
                        // EVM assembler of runtime portion of child contract:
                        //     ;; Pseudocode: if (msg.sender != 0x0000000000b3f879cb30fe243b4dfee438691c04) { throw; }
                        //     ;;             suicide(msg.sender)
                        //     PUSH15 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract
                        //     CALLER
                        //     XOR
                        //     PC
                        //     JUMPI
                        //     CALLER
                        //     SELFDESTRUCT
                        // Or in binary: 6eb3f879cb30fe243b4dfee438691c043318585733ff
                        // Since the binary is so short (22 bytes), we can get away
                        // with a very simple initcode:
                        //     PUSH22 0x6eb3f879cb30fe243b4dfee438691c043318585733ff
                        //     PUSH1 0
                        //     MSTORE ;; at this point, memory locations mem[10] through
                        //            ;; mem[31] contain the runtime portion of the child
                        //            ;; contract. all that's left to do is to RETURN this
                        //            ;; chunk of memory.
                        //     PUSH1 22 ;; length
                        //     PUSH1 10 ;; offset
                        //     RETURN
                        // Or in binary: 756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3
                        // Almost done! All we have to do is put this short (31 bytes) blob into
                        // memory and call CREATE with the appropriate offsets.
                        let solidity_free_mem_ptr := mload(0x40)
                        mstore(solidity_free_mem_ptr, 0x00756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3)
                        addr := create(0, add(solidity_free_mem_ptr, 1), 31)
                    }
                }
            
                // Mints `value` new sub-tokens (e.g. cents, pennies, ...) by creating `value`
                // new child contracts. The minted tokens are owned by the caller of this
                // function.
                function mint(uint256 value) public {
                    for (uint256 i = 0; i < value; i++) {
                        makeChild();
                    }
                    s_head += value;
                    s_balances[msg.sender] += value;
                }
            
                // Destroys `value` child contracts and updates s_tail.
                //
                // This function is affected by an issue in solc: https://github.com/ethereum/solidity/issues/2999
                // The `mk_contract_address(this, i).call();` doesn't forward all available gas, but only GAS - 25710.
                // As a result, when this line is executed with e.g. 30000 gas, the callee will have less than 5000 gas
                // available and its SELFDESTRUCT operation will fail leading to no gas refund occurring.
                // The remaining ~29000 gas left after the call is enough to update s_tail and the caller's balance.
                // Hence tokens will have been destroyed without a commensurate gas refund.
                // Fortunately, there is a simple workaround:
                // Whenever you call free, freeUpTo, freeFrom, or freeUpToFrom, ensure that you pass at least
                // 25710 + `value` * (1148 + 5722 + 150) gas. (It won't all be used)
                function destroyChildren(uint256 value) internal {
                    uint256 tail = s_tail;
                    // tail points to slot behind the last contract in the queue
                    for (uint256 i = tail + 1; i <= tail + value; i++) {
                        mk_contract_address(this, i).call();
                    }
            
                    s_tail = tail + value;
                }
            
                // Frees `value` sub-tokens (e.g. cents, pennies, ...) belonging to the
                // caller of this function by destroying `value` child contracts, which
                // will trigger a partial gas refund.
                // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                // when calling this function. For details, see the comment above `destroyChilden`.
                function free(uint256 value) public returns (bool success) {
                    uint256 from_balance = s_balances[msg.sender];
                    if (value > from_balance) {
                        return false;
                    }
            
                    destroyChildren(value);
            
                    s_balances[msg.sender] = from_balance - value;
            
                    return true;
                }
            
                // Frees up to `value` sub-tokens. Returns how many tokens were freed.
                // Otherwise, identical to free.
                // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                // when calling this function. For details, see the comment above `destroyChilden`.
                function freeUpTo(uint256 value) public returns (uint256 freed) {
                    uint256 from_balance = s_balances[msg.sender];
                    if (value > from_balance) {
                        value = from_balance;
                    }
            
                    destroyChildren(value);
            
                    s_balances[msg.sender] = from_balance - value;
            
                    return value;
                }
            
                // Frees `value` sub-tokens owned by address `from`. Requires that `msg.sender`
                // has been approved by `from`.
                // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                // when calling this function. For details, see the comment above `destroyChilden`.
                function freeFrom(address from, uint256 value) public returns (bool success) {
                    address spender = msg.sender;
                    uint256 from_balance = s_balances[from];
                    if (value > from_balance) {
                        return false;
                    }
            
                    mapping(address => uint256) from_allowances = s_allowances[from];
                    uint256 spender_allowance = from_allowances[spender];
                    if (value > spender_allowance) {
                        return false;
                    }
            
                    destroyChildren(value);
            
                    s_balances[from] = from_balance - value;
                    from_allowances[spender] = spender_allowance - value;
            
                    return true;
                }
            
                // Frees up to `value` sub-tokens owned by address `from`. Returns how many tokens were freed.
                // Otherwise, identical to `freeFrom`.
                // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                // when calling this function. For details, see the comment above `destroyChilden`.
                function freeFromUpTo(address from, uint256 value) public returns (uint256 freed) {
                    address spender = msg.sender;
                    uint256 from_balance = s_balances[from];
                    if (value > from_balance) {
                        value = from_balance;
                    }
            
                    mapping(address => uint256) from_allowances = s_allowances[from];
                    uint256 spender_allowance = from_allowances[spender];
                    if (value > spender_allowance) {
                        value = spender_allowance;
                    }
            
                    destroyChildren(value);
            
                    s_balances[from] = from_balance - value;
                    from_allowances[spender] = spender_allowance - value;
            
                    return value;
                }
            }