TW
Tripwire
Find contracts. Test them. Review real vulns.

Analysis Run

019d5666-71df-70bc-b254-bdcd67639ea9

failed
Chain ID
1
Address
Block Number
Created
Sat, Apr 4, 2026 2:50 AM
Review Summary

What survived the funnel

Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?

Confirmed
0
Validated
0
Source
Verified
Review Backing
Present
Best current signal
Untrusted DELEGATECALL target reachable
crit 0 high 0 proxy context implementation known
Review Checklist
1. Read the findings table first. Ignore raw jobs until the result looks real.
2. Confirm attacker control, not just the existence of a sink.
3. Confirm money or privilege impact.
4. Keep it if the behavior is real. Kill it if this is normal proxy, admin, or upgrade plumbing.

Processing Jobs

Status & attempts
ID Type Status Attempts Heartbeat
42799 artifact.fetch complete 1 / 3 1 week ago
42800 proxy.resolve complete 1 / 3 1 week ago
42801 tool.cast_disasm complete 1 / 3 1 week ago
42802 analysis.bundle complete 1 / 3 1 week ago
42803 capability.graph complete 1 / 3 1 week ago
42804 detector.run complete 1 / 3 1 week ago
42805 validation.fork complete 1 / 3 1 week ago
42939 report.consolidate complete 1 / 6 1 week ago
42940 tool.slither failed 2 / 2 1 week ago

Artifact

Runtime bytecode: available
Creation TX:
Deployer:
Proxy: yes confirmed
Verified Source: yes
Proxy evidence
{
    "method": "etherscan+onchain",
    "status": "confirmed",
    "etherscan_hint": {
        "source": "etherscan",
        "etherscan": {
            "proxy_flag": true,
            "implementation": "0x04393e5c6701237d7ee836d644f8dbdd122afebc"
        }
    },
    "implementation": "0x04393e5c6701237d7ee836d644f8dbdd122afebc",
    "onchain_method": "beacon"
}

Slither

tool.slither
Status
failed slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-zdp771mw) 'forge config --json' running 'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-zdp771mw) 'forge' returned non-zero exit code 1 2026-04-04T04:13:51.549217Z ERROR foundry_compilers_artifacts_solc::sources: error="/tmp/slither-zdp771mw/lib/openzeppelin-contracts/contracts/proxy/UpgradeableBeacon.sol": No such file or directory (os error 2) stdout: 2026-04-04T04:13:51.549303Z ERROR foundry_compilers_artifacts_solc::sources: error="/tmp/slither-zdp771mw/lib/openzeppelin-contracts/contracts/proxy/BeaconProxy.sol": No such file or directory (os error 2) stdout: Unable to resolve imports: stdout: "@openzeppelin/contracts/proxy/UpgradeableBeacon.sol" in "/tmp/slither-zdp771mw/contracts/proxy/SafeBeaconProxy.sol" stdout: "@openzeppelin/contracts/proxy/BeaconProxy.sol" in "/tmp/slither-zdp771mw/contracts/proxy/SafeBeaconProxy.sol" stdout: with remappings: stdout: openzeppelin-contracts/=/tmp/slither-zdp771mw/lib/openzeppelin-contracts/ stdout: @openzeppelin/contracts/=/tmp/slither-zdp771mw/lib/openzeppelin-contracts/contracts/ stdout: Compiling 1 files with Solc 0.7.6 stdout: Solc 0.7.6 finished in 2.34ms Error: Compiler run failed: stderr: Error (6275): Source "lib/openzeppelin-contracts/contracts/proxy/UpgradeableBeacon.sol" not found: File not found. stderr: contracts/proxy/SafeBeaconProxy.sol:20:1: ParserError: Source "lib/openzeppelin-contracts/contracts/proxy/UpgradeableBeacon.sol" not found: File not found. stderr: import {UpgradeableBeacon} from '@openzeppelin/contracts/proxy/UpgradeableBeacon.sol'; stderr: ^------------------------------------------------------------------------------------^ stderr: Error (6275): Source "lib/openzeppelin-contracts/contracts/proxy/BeaconProxy.sol" not found: File not found. stderr: contracts/proxy/SafeBeaconProxy.sol:21:1: ParserError: Source "lib/openzeppelin-contracts/contracts/proxy/BeaconProxy.sol" not found: File not found. stderr: import {BeaconProxy} from '@openzeppelin/contracts/proxy/BeaconProxy.sol'; stderr: ^------------------------------------------------------------------------^ Traceback (most recent call last): File "/var/www/tripwire/.venv/bin/slither", line 10, in <module> sys.exit(main()) ~~~~^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main main_impl(all_detector_classes=detectors, all_printer_classes=printers) ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl ) = process_all(filename, args, detector_classes, printer_classes) ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all compilations = compile_all(target, **vars(args)) File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all compilations.append(CryticCompile(target, **kwargs)) ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__ self._compile(**kwargs) ~~~~~~~~~~~~~^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile self._platform.compile(self, **kwargs) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile hardhat_like_parsing( ~~~~~~~~~~~~~~~~~~~~^ crytic_compile, str(self._target), build_directory, str(self._project_root) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ) ^ File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing raise InvalidCompilation(txt) crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command? /tmp/slither-zdp771mw/out/build-info is not a directory.
Attempts
2 / 2
Findings
0
Solc
View stderr
Traceback (most recent call last):
  File "/var/www/tripwire/worker/worker.py", line 198, in main
    payload = dispatch_job(job_type, job_id, job_meta, run_data, config, laravel_client)
  File "/var/www/tripwire/worker/worker.py", line 114, in dispatch_job
    return slither_run.run(job_id, job_meta, run_data, config, log, laravel_client)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/worker/jobs/slither_run.py", line 1084, in run
    stdout_text, stderr_text, result_json = _run_slither(
                                            ~~~~~~~~~~~~^
        workspace,
        ^^^^^^^^^^
    ...<4 lines>...
        target_path=target_path,
        ^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/var/www/tripwire/worker/jobs/slither_run.py", line 958, in _run_slither
    raise RuntimeError(f"slither failed (exit {result.returncode}): {stderr_text or stdout_text}")
RuntimeError: slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-zdp771mw)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-zdp771mw)
'forge' returned non-zero exit code 1
2026-04-04T04:13:51.549217Z ERROR foundry_compilers_artifacts_solc::sources: error="/tmp/slither-zdp771mw/lib/openzeppelin-contracts/contracts/proxy/UpgradeableBeacon.sol": No such file or directory (os error 2)
stdout: 2026-04-04T04:13:51.549303Z ERROR foundry_compilers_artifacts_solc::sources: error="/tmp/slither-zdp771mw/lib/openzeppelin-contracts/contracts/proxy/BeaconProxy.sol": No such file or directory (os error 2)
stdout: Unable to resolve imports:
stdout:       "@openzeppelin/contracts/proxy/UpgradeableBeacon.sol" in "/tmp/slither-zdp771mw/contracts/proxy/SafeBeaconProxy.sol"
stdout:       "@openzeppelin/contracts/proxy/BeaconProxy.sol" in "/tmp/slither-zdp771mw/contracts/proxy/SafeBeaconProxy.sol"
stdout: with remappings:
stdout:       openzeppelin-contracts/=/tmp/slither-zdp771mw/lib/openzeppelin-contracts/
stdout:       @openzeppelin/contracts/=/tmp/slither-zdp771mw/lib/openzeppelin-contracts/contracts/
stdout: Compiling 1 files with Solc 0.7.6
stdout: Solc 0.7.6 finished in 2.34ms
Error: Compiler run failed:
stderr: Error (6275): Source "lib/openzeppelin-contracts/contracts/proxy/UpgradeableBeacon.sol" not found: File not found.
stderr: contracts/proxy/SafeBeaconProxy.sol:20:1: ParserError: Source "lib/openzeppelin-contracts/contracts/proxy/UpgradeableBeacon.sol" not found: File not found.
stderr: import {UpgradeableBeacon} from '@openzeppelin/contracts/proxy/UpgradeableBeacon.sol';
stderr: ^------------------------------------------------------------------------------------^
stderr: Error (6275): Source "lib/openzeppelin-contracts/contracts/proxy/BeaconProxy.sol" not found: File not found.
stderr: contracts/proxy/SafeBeaconProxy.sol:21:1: ParserError: Source "lib/openzeppelin-contracts/contracts/proxy/BeaconProxy.sol" not found: File not found.
stderr: import {BeaconProxy} from '@openzeppelin/contracts/proxy/BeaconProxy.sol';
stderr: ^------------------------------------------------------------------------^
Traceback (most recent call last):
  File "/var/www/tripwire/.venv/bin/slither", line 10, in <module>
    sys.exit(main())
             ~~~~^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main
    main_impl(all_detector_classes=detectors, all_printer_classes=printers)
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl
    ) = process_all(filename, args, detector_classes, printer_classes)
        ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all
    compilations = compile_all(target, **vars(args))
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all
    compilations.append(CryticCompile(target, **kwargs))
                        ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__
    self._compile(**kwargs)
    ~~~~~~~~~~~~~^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile
    self._platform.compile(self, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile
    hardhat_like_parsing(
    ~~~~~~~~~~~~~~~~~~~~^
        crytic_compile, str(self._target), build_directory, str(self._project_root)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing
    raise InvalidCompilation(txt)
crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command?
/tmp/slither-zdp771mw/out/build-info is not a directory.

Codex

report.consolidate
Job Status
complete
Attempts
1 / 6
Findings
2
No findings?
no
Summary
Read the full AUDIT_BUNDLE.txt. The Garden implementation exposes two critical by-signature theft vectors: arbitrary self-reported reward claims and keeper-driven price-per-share manipulation.
View stdout
{"no_findings":false,"summary":"Read the full AUDIT_BUNDLE.txt. The Garden implementation exposes two critical by-signature theft vectors: arbitrary self-reported reward claims and keeper-driven price-per-share manipulation.","findings":[{"severity":"critical","category":"auth","title":"`claimRewardsBySig` trusts attacker-chosen reward amounts and can drain pooled rewards","exploit_scenario":"A contributor signs a `claimRewardsBySig` or `claimAndStakeRewardsBySig` payload with `_profits` set to the entire `reserveAssetRewardsSetAside` and `_babl` set to the per-tx cap. A whitelisted keeper submits it, and the garden transfers the reserve asset directly from the shared reward pool to the attacker without checking actual entitlement. The attacker can repeat with new nonces until the garden's reserved rewards are exhausted, and may also over-pull BABL through the distributor path.","recommendation":"Do not accept beneficiary-supplied reward amounts. Compute claimable rewards on-chain from canonical accounting, or verify a protocol-authenticated proof that binds garden, contributor, strategy set, and exact amounts. Remove `_profits`/`_babl` as free user-controlled inputs in by-signature claim flows.","evidence":"Both by-signature claim entrypoints only verify the user's signature over arbitrary `_babl`/`_profits` values, then call `_sendRewardsInternal`. `_sendRewardsInternal` only checks that `_profits` is nonzero, `reserveAssetRewardsSetAside >= _profits`, and then subtracts `_profits` from the shared reserve and transfers it out; it never validates that the caller actually earned that amount.","locations":["contracts/gardens/Garden.sol:525","contracts/gardens/Garden.sol:576","contracts/gardens/Garden.sol:1011"]},{"severity":"critical","category":"oracle","title":"Keeper-supplied `pricePerShare` can be ratcheted away from NAV to steal value from other LPs","exploit_scenario":"An attacker collaborates with any whitelisted keeper and submits a series of tiny `depositBySig`/`withdrawBySig` calls with manipulated `_pricePerShare` values. Because each call updates `lastPricePerShare` and future checks only compare against that last user-supplied value, the attacker can staircase the accepted price far below or above real NAV. Once the anchor is moved, a large deposit mints excess shares at an artificially low price, or a large withdrawal redeems too much reserve asset at an artificially high price, diluting or draining other contributors.","recommendation":"Do not let relayers provide an untrusted PPS that becomes the next anchor. Bind pricing to an on-chain valuation or a protocol-signed snapshot, include the exact PPS in the signed payload, and validate it against current NAV/oracle bounds instead of the previous user-supplied price alone.","evidence":"`depositBySig` and `withdrawBySig` take `_pricePerShare` from the keeper instead of deriving it on-chain. `_internalDeposit` and `_withdrawInternal` use that value directly to mint/burn shares. `_checkLastPricePerShare` does not compare against actual NAV; it only compares against `lastPricePerShare`, then stores the new attacker-chosen value as the next reference point. On the first by-signature use, `lastPricePerShare == 0`, so any PPS is accepted.","locations":["contracts/gardens/Garden.sol:315","contracts/gardens/Garden.sol:427","contracts/gardens/Garden.sol:863","contracts/gardens/Garden.sol:947","contracts/gardens/Garden.sol:1232"]}]}
View stderr
e with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

    SPDX-License-Identifier: Apache License, Version 2.0
*/

pragma solidity 0.7.6;

import {SignedSafeMath} from '@openzeppelin/contracts/math/SignedSafeMath.sol';

import {LowGasSafeMath} from './LowGasSafeMath.sol';

/**
 * @title PreciseUnitMath
 * @author Set Protocol
 *
 * Arithmetic for fixed-point numbers with 18 decimals of precision. Some functions taken from
 * dYdX's BaseMath library.
 *
 * CHANGELOG:
 * - 9/21/20: Added safePower function
 */
library PreciseUnitMath {
    using LowGasSafeMath for uint256;
    using SignedSafeMath for int256;

    // The number One in precise units.
    uint256 internal constant PRECISE_UNIT = 10**18;
    int256 internal constant PRECISE_UNIT_INT = 10**18;

    // Max unsigned integer value
    uint256 internal constant MAX_UINT_256 = type(uint256).max;
    // Max and min signed integer value
    int256 internal constant MAX_INT_256 = type(int256).max;
    int256 internal constant MIN_INT_256 = type(int256).min;

    /**
     * @dev Getter function since constants can't be read directly from libraries.
     */
    function decimals() internal pure returns (uint256) {
        return 18;
    }

    /**
     * @dev Getter function since constants can't be read directly from libraries.
     */
    function preciseUnit() internal pure returns (uint256) {
        return PRECISE_UNIT;
    }

    /**
     * @dev Getter function since constants can't be read directly from libraries.
     */
    function preciseUnitInt() internal pure returns (int256) {
        return PRECISE_UNIT_INT;
    }

    /**
     * @dev Getter function since constants can't be read directly from libraries.
     */
    function maxUint256() internal pure returns (uint256) {
        return MAX_UINT_256;
    }

    /**
     * @dev Getter function since constants can't be read directly from libraries.
     */
    function maxInt256() internal pure returns (int256) {
        return MAX_INT_256;
    }

    /**
     * @dev Getter function since constants can't be read directly from libraries.
     */
    function minInt256() internal pure returns (int256) {
        return MIN_INT_256;
    }

    /**
     * @dev Multiplies value a by value b (result is rounded down). It's assumed that the value b is the significand
     * of a number with 18 decimals precision.
     */
    function preciseMul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mul(b).div(PRECISE_UNIT);
    }

    /**
     * @dev Multiplies value a by value b (result is rounded towards zero). It's assumed that the value b is the
     * significand of a number with 18 decimals precision.
     */
    function preciseMul(int256 a, int256 b) internal pure returns (int256) {
        return a.mul(b).div(PRECISE_UNIT_INT);
    }

    /**
     * @dev Multiplies value a by value b (result is rounded up). It's assumed that the value b is the significand
     * of a number with 18 decimals precision.
     */
    function preciseMulCeil(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a == 0 || b == 0) {
            return 0;
        }
        return a.mul(b).sub(1).div(PRECISE_UNIT).add(1);
    }

    /**
     * @dev Divides value a by value b (result is rounded down).
     */
    function preciseDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        return a.mul(PRECISE_UNIT).div(b);
    }

    /**
     * @dev Divides value a by value b (result is rounded towards 0).
     */
    function preciseDiv(int256 a, int256 b) internal pure returns (int256) {
        return a.mul(PRECISE_UNIT_INT).div(b);
    }

    /**
     * @dev Divides value a by value b (result is rounded up or away from 0).
     */
    function preciseDivCeil(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0, 'Cant divide by 0');

        return a > 0 ? a.mul(PRECISE_UNIT).sub(1).div(b).add(1) : 0;
    }

    /**
     * @dev Divides value a by value b (result is rounded down - positive numbers toward 0 and negative away from 0).
     */
    function divDown(int256 a, int256 b) internal pure returns (int256) {
        require(b != 0, 'Cant divide by 0');
        require(a != MIN_INT_256 || b != -1, 'Invalid input');

        int256 result = a.div(b);
        if (a ^ b < 0 && a % b != 0) {
            result -= 1;
        }

        return result;
    }

    /**
     * @dev Multiplies value a by value b where rounding is towards the lesser number.
     * (positive values are rounded towards zero and negative values are rounded away from 0).
     */
    function conservativePreciseMul(int256 a, int256 b) internal pure returns (int256) {
        return divDown(a.mul(b), PRECISE_UNIT_INT);
    }

    /**
     * @dev Divides value a by value b where rounding is towards the lesser number.
     * (positive values are rounded towards zero and negative values are rounded away from 0).
     */
    function conservativePreciseDiv(int256 a, int256 b) internal pure returns (int256) {
        return divDown(a.mul(PRECISE_UNIT_INT), b);
    }

    /**
     * @dev Performs the power on a specified value, reverts on overflow.
     */
    function safePower(uint256 a, uint256 pow) internal pure returns (uint256) {
        require(a > 0, 'Value must be positive');

        uint256 result = 1;
        for (uint256 i = 0; i < pow; i++) {
            uint256 previousResult = result;

            // Using safemath multiplication prevents overflows
            result = previousResult.mul(a);
        }

        return result;
    }
}

================================================================================
FILE: contracts/lib/SafeDecimalMath.sol
================================================================================
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.7.6;

import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import {LowGasSafeMath} from '../lib/LowGasSafeMath.sol';
import {UniversalERC20} from '../lib/UniversalERC20.sol';

library SafeDecimalMath {
    using LowGasSafeMath for uint256;
    using UniversalERC20 for IERC20;

    /* Number of decimal places in the representations. */
    uint8 internal constant decimals = 18;

    /* The number representing 1.0. */
    uint256 internal constant UNIT = 10**uint256(decimals);

    /**
     * @return Provides an interface to UNIT.
     */
    function unit() internal pure returns (uint256) {
        return UNIT;
    }

    /**
     * @return The result of multiplying x and y, interpreting the operands as fixed-point
     * decimals.
     *
     * @dev A unit factor is divided out after the product of x and y is evaluated,
     * so that product must be less than 2**256. As this is an integer division,
     * the internal division always rounds down. This helps save on gas. Rounding
     * is more expensive on gas.
     */
    function multiplyDecimal(uint256 x, uint256 y) internal pure returns (uint256) {
        /* Divide by UNIT to remove the extra factor introduced by the product. */
        return x.mul(y) / UNIT;
    }

    /**
     * @return The result of safely multiplying x and y, interpreting the operands
     * as fixed-point decimals of the specified precision unit.
     *
     * @dev The operands should be in the form of a the specified unit factor which will be
     * divided out after the product of x and y is evaluated, so that product must be
     * less than 2**256.
     *
     * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
     * Rounding is useful when you need to retain fidelity for small decimal numbers
     * (eg. small fractions or percentages).
     */
    function _multiplyDecimalRound(
        uint256 x,
        uint256 y,
        uint256 precisionUnit
    ) private pure returns (uint256) {
        /* Divide by UNIT to remove the extra factor introduced by the product. */
        uint256 quotientTimesTen = x.mul(y) / (precisionUnit / 10);

        if (quotientTimesTen % 10 >= 5) {
            quotientTimesTen += 10;
        }

        return quotientTimesTen / 10;
    }

    /**
     * @return The result of safely multiplying x and y, interpreting the operands
     * as fixed-point decimals of a standard unit.
     *
     * @dev The operands should be in the standard unit factor which will be
     * divided out after the product of x and y is evaluated, so that product must be
     * less than 2**256.
     *
     * Unlike multiplyDecimal, this function rounds the result to the nearest increment.
     * Rounding is useful when you need to retain fidelity for small decimal numbers
     * (eg. small fractions or percentages).
     */
    function multiplyDecimalRound(uint256 x, uint256 y) internal pure returns (uint256) {
        return _multiplyDecimalRound(x, y, UNIT);
    }

    /**
     * @return The result of safely dividing x and y. The return value is a high
     * precision decimal.
     *
     * @dev y is divided after the product of x and the standard precision unit
     * is evaluated, so the product of x and UNIT must be less than 2**256. As
     * this is an integer division, the result is always rounded down.
     * This helps save on gas. Rounding is more expensive on gas.
     */
    function divideDecimal(uint256 x, uint256 y) internal pure returns (uint256) {
        /* Reintroduce the UNIT factor that will be divided out by y. */
        return x.mul(UNIT).div(y);
    }

    /**
     * @return The result of safely dividing x and y. The return value is as a rounded
     * decimal in the precision unit specified in the parameter.
     *
     * @dev y is divided after the product of x and the specified precision unit
     * is evaluated, so the product of x and the specified precision unit must
     * be less than 2**256. The result is rounded to the nearest increment.
     */
    function _divideDecimalRound(
        uint256 x,
        uint256 y,
        uint256 precisionUnit
    ) private pure returns (uint256) {
        uint256 resultTimesTen = x.mul(precisionUnit * 10).div(y);

        if (resultTimesTen % 10 >= 5) {
            resultTimesTen += 10;
        }

        return resultTimesTen / 10;
    }

    /**
     * @return The result of safely dividing x and y. The return value is as a rounded
     * standard precision decimal.
     *
     * @dev y is divided after the product of x and the standard precision unit
     * is evaluated, so the product of x and the standard precision unit must
     * be less than 2**256. The result is rounded to the nearest increment.
     */
    function divideDecimalRound(uint256 x, uint256 y) internal pure returns (uint256) {
        return _divideDecimalRound(x, y, UNIT);
    }

    /**
     * Normalizing amount decimals between tokens
     * @param _from       ERC20 asset address
     * @param _to     ERC20 asset address
     * @param _amount Value _to normalize (e.g. capital)
     */
    function normalizeAmountTokens(
        address _from,
        address _to,
        uint256 _amount
    ) internal view returns (uint256) {
        uint256 fromDecimals = IERC20(_from).universalDecimals();
        uint256 toDecimals = IERC20(_to).universalDecimals();

        if (fromDecimals == toDecimals) {
            return _amount;
        }
        if (toDecimals > fromDecimals) {
            return _amount.mul(10**(toDecimals - (fromDecimals)));
        }
        return _amount.div(10**(fromDecimals - (toDecimals)));
    }
}

================================================================================
FILE: contracts/lib/SignatureChecker.sol
================================================================================
// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;

import {ECDSA} from '@openzeppelin/contracts/cryptography/ECDSA.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';

import {IERC1271} from '../interfaces/IERC1271.sol';

/**
 * @dev Signature verification helper: Provide a single mechanism to verify both private-key (EOA) ECDSA signature and
 * ERC1271 contract sigantures. Using this instead of ECDSA.recover in your contract will make them compatible with
 * smart contract wallets such as Argent and Gnosis.
 *
 * Note: unlike ECDSA signatures, contract signature's are revocable, and the outcome of this function can thus change
 * through time. It could return true at block N and false at block N+1 (or the opposite).
 *
 */

library SignatureChecker {
    function isValidSignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        if (Address.isContract(signer)) {
            try IERC1271(signer).isValidSignature(hash, signature) returns (bytes4 magicValue) {
                return magicValue == IERC1271(signer).isValidSignature.selector;
            } catch {
                return false;
            }
        } else {
            return ECDSA.recover(hash, signature) == signer;
        }
    }
}

================================================================================
FILE: contracts/lib/UniversalERC20.sol
================================================================================
// SPDX-License-Identifier: MIT

pragma solidity 0.7.6;

import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeMath} from '@openzeppelin/contracts/math/SafeMath.sol';

library UniversalERC20 {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    IERC20 private constant ZERO_ADDRESS = IERC20(0x0000000000000000000000000000000000000000);
    IERC20 private constant ETH_ADDRESS = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);

    function universalTransfer(
        IERC20 token,
        address to,
        uint256 amount
    ) internal returns (bool) {
        if (amount == 0) {
            return true;
        }

        if (isETH(token)) {
            address(uint160(to)).transfer(amount);
            return true;
        } else {
            token.safeTransfer(to, amount);
            return true;
        }
    }

    function universalTransferFrom(
        IERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        if (amount == 0) {
            return;
        }

        if (isETH(token)) {
            require(from == msg.sender && msg.value >= amount, 'msg.value is zero');
            if (to != address(this)) {
                address(uint160(to)).transfer(amount);
            }
            if (msg.value > amount) {
                msg.sender.transfer(msg.value.sub(amount));
            }
        } else {
            token.safeTransferFrom(from, to, amount);
        }
    }

    function universalApprove(
        IERC20 token,
        address to,
        uint256 amount
    ) internal {
        if (!isETH(token)) {
            if (amount > 0 && token.allowance(address(this), to) > 0) {
                token.safeApprove(to, 0);
            }
            token.safeApprove(to, amount);
        }
    }

    function universalBalanceOf(IERC20 token, address who) internal view returns (uint256) {
        if (isETH(token)) {
            return who.balance;
        } else {
            return token.balanceOf(who);
        }
    }

    function universalDecimals(IERC20 token) internal view returns (uint256) {
        if (isETH(token)) {
            return 18;
        }

        (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSignature('decimals()'));

        return success ? abi.decode(data, (uint256)) : 18;
    }

    function isETH(IERC20 token) internal pure returns (bool) {
        return address(token) == address(ZERO_ADDRESS) || address(token) == address(ETH_ADDRESS);
    }
}

================================================================================
FILE: contracts/proxy/VTableBeacon.sol
================================================================================
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.7.6;
pragma abicoder v2;

import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';

/**
 * @title VTableBeacon
 * @notice Redirects calls to an implementation based on the method signature
 */
contract VTableBeacon is Ownable {
    struct ModuleDefinition {
        address implementation;
        bytes4[] selectors;
    }

    bytes4 private constant _FALLBACK_SIGN = 0xffffffff;

    // Mapping of methods signatures to their implementations
    mapping(bytes4 => address) public delegates;

    event VTableUpdate(bytes4 indexed selector, address oldImplementation, address newImplementation);

    function implementation(bytes4 _selector) external view virtual returns (address module) {
        module = delegates[_selector];
        if (module != address(0)) return module;

        module = delegates[_FALLBACK_SIGN];
        if (module != address(0)) return module;

        revert('VTableBeacon: No implementation found');
    }

    /**
     * @dev Updates the vtable
     */
    function updateVTable(ModuleDefinition[] calldata modules) external onlyOwner {
        for (uint256 i = 0; i < modules.length; ++i) {

exec
/bin/bash -lc "sed -n '3901,4680p' AUDIT_BUNDLE.txt" in /tmp/codex-bjkv0enx
 succeeded in 0ms:
            ModuleDefinition memory module = modules[i];
            for (uint256 j = 0; j < module.selectors.length; ++j) {
                bytes4 selector = module.selectors[j];
                emit VTableUpdate(selector, delegates[selector], module.implementation);
                delegates[selector] = module.implementation;
            }
        }
    }
}

================================================================================
FILE: contracts/proxy/VTableBeaconProxy.sol
================================================================================
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.7.6;
pragma abicoder v2;

import '@openzeppelin/contracts/proxy/Proxy.sol';

import './VTableBeacon.sol';

/**
 * @title VTableBeaconProxy
 */
contract VTableBeaconProxy is Proxy {
    VTableBeacon public immutable beacon;

    constructor(VTableBeacon _beacon) {
        beacon = _beacon;
    }

    function _implementation() internal view virtual override returns (address module) {
        return beacon.implementation(msg.sig);
    }
}

================================================================================
FILE: contracts/token/TimeLockRegistry.sol
================================================================================
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.7.6;
pragma experimental ABIEncoderV2;
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';

import {TimeLockedToken} from './TimeLockedToken.sol';
import {AddressArrayUtils} from '../lib/AddressArrayUtils.sol';

import {LowGasSafeMath} from '../lib/LowGasSafeMath.sol';

/**
 * @title TimeLockRegistry
 * @notice Register Lockups for TimeLocked ERC20 Token BABL (e.g. vesting)
 * @author Babylon Finance
 * @dev This contract allows owner to register distributions for a TimeLockedToken
 *
 * To register a distribution, register method should be called by the owner.
 * claim() should be called only by the BABL Token smartcontract (modifier onlyBABLToken)
 *  when any account registered to receive tokens make its own claim
 * If case of a mistake, owner can cancel registration before the claim is done by the account
 *
 * Note this contract address must be setup in the TimeLockedToken's contract pointing
 * to interact with (e.g. setTimeLockRegistry() function)
 */

contract TimeLockRegistry is Ownable {
    using LowGasSafeMath for uint256;
    using Address for address;
    using AddressArrayUtils for address[];

    /* ============ Events ============ */

    event Register(address receiver, uint256 distribution);
    event Cancel(address receiver, uint256 distribution);
    event Claim(address account, uint256 distribution);

    /* ============ Modifiers ============ */

    modifier onlyBABLToken() {
        require(msg.sender == address(token), 'only BABL Token');
        _;
    }

    /* ============ State Variables ============ */

    // time locked token
    TimeLockedToken public token;

    /**
     * @notice The profile of each token owner under vesting conditions and its special conditions
     * @param receiver Account being registered
     * @param investorType Indicates whether or not is a Team member (true = team member / advisor, false = private investor)
     * @param vestingStarting Date When the vesting begins for such token owner
     * @param distribution Tokens amount that receiver is due to get
     */
    struct Registration {
        address receiver;
        uint256 distribution;
        bool investorType;
        uint256 vestingStartingDate;
    }

    /**
     * @notice The profile of each token owner under vesting conditions and its special conditions
     * @param team Indicates whether or not is a Team member (true = team member / advisor, false = private investor)
     * @param vestingBegin When the vesting begins for such token owner
     * @param vestingEnd When the vesting ends for such token owner
     * @param lastClaim When the last claim was done
     */
    struct TokenVested {
        bool team;
        bool cliff;
        uint256 vestingBegin;
        uint256 vestingEnd;
        uint256 lastClaim;
    }

    /// @notice A record of token owners under vesting conditions for each account, by index
    mapping(address => TokenVested) public tokenVested;

    // mapping from token owners under vesting conditions to BABL due amount (e.g. SAFT addresses, team members, advisors)
    mapping(address => uint256) public registeredDistributions;

    // array of all registrations
    address[] public registrations;

    // total amount of tokens registered
    uint256 public totalTokens;

    // vesting for Team Members
    uint256 private constant teamVesting = 365 days * 4;

    // vesting for Investors and Advisors
    uint256 private constant investorVesting = 365 days * 3;

    /* ============ Functions ============ */

    /* ============ Constructor ============ */

    /**
     * @notice Construct a new Time Lock Registry and gives ownership to sender
     * @param _token TimeLockedToken contract to use in this registry
     */
    constructor(TimeLockedToken _token) {
        token = _token;
    }

    /* ============ External Functions ============ */

    /* ============ External Getter Functions ============ */

    /**
     * Gets registrations
     *
     * @return  address[]        Returns list of registrations
     */

    function getRegistrations() external view returns (address[] memory) {
        return registrations;
    }

    /* ===========  Token related Gov Functions ====== */

    /**
     * PRIVILEGED GOVERNANCE FUNCTION
     *
     * @notice Register multiple investors/team in a batch
     * @param _registrations Registrations to process
     */
    function registerBatch(Registration[] memory _registrations) external onlyOwner {
        for (uint256 i = 0; i < _registrations.length; i++) {
            register(
                _registrations[i].receiver,
                _registrations[i].distribution,
                _registrations[i].investorType,
                _registrations[i].vestingStartingDate
            );
        }
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION
     *
     * @notice Register new account under vesting conditions (Team, Advisors, Investors e.g. SAFT purchaser)
     * @param receiver Address belonging vesting conditions
     * @param distribution Tokens amount that receiver is due to get
     */
    function register(
        address receiver,
        uint256 distribution,
        bool investorType,
        uint256 vestingStartingDate
    ) public onlyOwner {
        require(receiver != address(0), 'TimeLockRegistry::register: cannot register the zero address');
        require(
            receiver != address(this),
            'TimeLockRegistry::register: Time Lock Registry contract cannot be an investor'
        );
        require(distribution != 0, 'TimeLockRegistry::register: Distribution = 0');
        require(
            registeredDistributions[receiver] == 0,
            'TimeLockRegistry::register:Distribution for this address is already registered'
        );
        require(vestingStartingDate >= 1614553200, 'Cannot register earlier than March 2021'); // 1614553200 is UNIX TIME of 2021 March the 1st
        require(
            vestingStartingDate <= block.timestamp.add(30 days),
            'Cannot register more than 30 days ahead in the future'
        );
        require(totalTokens.add(distribution) <= IERC20(token).balanceOf(address(this)), 'Not enough tokens');

        totalTokens = totalTokens.add(distribution);
        // register distribution
        registeredDistributions[receiver] = distribution;
        registrations.push(receiver);

        // register token vested conditions
        TokenVested storage newTokenVested = tokenVested[receiver];
        newTokenVested.team = investorType;
        newTokenVested.vestingBegin = vestingStartingDate;

        if (newTokenVested.team == true) {
            newTokenVested.vestingEnd = vestingStartingDate.add(teamVesting);
        } else {
            newTokenVested.vestingEnd = vestingStartingDate.add(investorVesting);
        }
        newTokenVested.lastClaim = vestingStartingDate;

        tokenVested[receiver] = newTokenVested;

        // emit register event
        emit Register(receiver, distribution);
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Cancel distribution registration in case of mistake and before a claim is done
     *
     * @notice Cancel distribution registration
     * @dev A claim has not to be done earlier
     * @param receiver Address that should have it's distribution removed
     * @return Whether or not it succeeded
     */
    function cancelRegistration(address receiver) external onlyOwner returns (bool) {
        require(registeredDistributions[receiver] != 0, 'Not registered');

        // get amount from distributions
        uint256 amount = registeredDistributions[receiver];

        // set distribution mapping to 0
        delete registeredDistributions[receiver];

        // set tokenVested mapping to 0
        delete tokenVested[receiver];

        // remove from the list of all registrations
        registrations.remove(receiver);

        // decrease total tokens
        totalTokens = totalTokens.sub(amount);

        // emit cancel event
        emit Cancel(receiver, amount);

        return true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Cancel distribution registration in case of mistake and before a claim is done
     *
     * @notice Cancel already delivered tokens. It might only apply when non-completion of vesting period of Team members or Advisors
     * @dev An automatic override allowance is granted during the claim process
     * @param account Address that should have it's distribution removed
     * @return Whether or not it succeeded
     */
    function cancelDeliveredTokens(address account) external onlyOwner returns (bool) {
        uint256 loosingAmount = token.cancelVestedTokens(account);

        // emit cancel event
        emit Cancel(account, loosingAmount);
        return true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Recover tokens in Time Lock Registry smartcontract address by the owner
     *
     * @notice Send tokens from smartcontract address to the owner.
     * It might only apply after a cancellation of vested tokens
     * @param amount Amount to be recovered by the owner of the Time Lock Registry smartcontract from its balance
     * @return Whether or not it succeeded
     */
    function transferToOwner(uint256 amount) external onlyOwner returns (bool) {
        SafeERC20.safeTransfer(token, msg.sender, amount);
        return true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Claim locked tokens by the registered account
     *
     * @notice Claim tokens due amount.
     * @dev Claim is done by the user in the TimeLocked contract and the contract is the only allowed to call
     * this function on behalf of the user to make the claim
     * @return The amount of tokens registered and delivered after the claim
     */
    function claim(address _receiver) external onlyBABLToken returns (uint256) {
        require(registeredDistributions[_receiver] != 0, 'Not registered');

        // get amount from distributions
        uint256 amount = registeredDistributions[_receiver];
        TokenVested storage claimTokenVested = tokenVested[_receiver];

        claimTokenVested.lastClaim = block.timestamp;

        // set distribution mapping to 0
        delete registeredDistributions[_receiver];

        // decrease total tokens
        totalTokens = totalTokens.sub(amount);

        // register lockup in TimeLockedToken
        // this will transfer funds from this contract and lock them for sender
        token.registerLockup(
            _receiver,
            amount,
            claimTokenVested.team,
            claimTokenVested.vestingBegin,
            claimTokenVested.vestingEnd,
            claimTokenVested.lastClaim
        );

        // set tokenVested mapping to 0
        delete tokenVested[_receiver];

        // emit claim event
        emit Claim(_receiver, amount);

        return amount;
    }

    /* ============ Getter Functions ============ */

    function checkVesting(address address_)
        external
        view
        returns (
            bool team,
            uint256 start,
            uint256 end,
            uint256 last
        )
    {
        TokenVested storage checkTokenVested = tokenVested[address_];

        return (
            checkTokenVested.team,
            checkTokenVested.vestingBegin,
            checkTokenVested.vestingEnd,
            checkTokenVested.lastClaim
        );
    }

    function checkRegisteredDistribution(address address_) external view returns (uint256 amount) {
        return registeredDistributions[address_];
    }
}

================================================================================
FILE: contracts/token/TimeLockedToken.sol
================================================================================
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.7.6;

import {IBabController} from '../interfaces/IBabController.sol';
import {TimeLockRegistry} from './TimeLockRegistry.sol';
import {IRewardsDistributor} from '../interfaces/IRewardsDistributor.sol';
import {VoteToken} from './VoteToken.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {Errors, _require} from '../lib/BabylonErrors.sol';
import {LowGasSafeMath} from '../lib/LowGasSafeMath.sol';
import {IBabController} from '../interfaces/IBabController.sol';

/**
 * @title TimeLockedToken
 * @notice Time Locked ERC20 Token
 * @author Babylon Finance
 * @dev Contract which gives the ability to time-lock tokens specially for vesting purposes usage
 *
 * By overriding the balanceOf() and transfer() functions in ERC20,
 * an account can show its full, post-distribution balance and use it for voting power
 * but only transfer or spend up to an allowed amount
 *
 * A portion of previously non-spendable tokens are allowed to be transferred
 * along the time depending on each vesting conditions, and after all epochs have passed, the full
 * account balance is unlocked. In case on non-completion vesting period, only the Time Lock Registry can cancel
 * the delivery of the pending tokens and only can cancel the remaining locked ones.
 */

abstract contract TimeLockedToken is VoteToken {
    using LowGasSafeMath for uint256;

    /* ============ Events ============ */

    /// @notice An event that emitted when a new lockout ocurr
    event NewLockout(
        address account,
        uint256 tokenslocked,
        bool isTeamOrAdvisor,
        uint256 startingVesting,
        uint256 endingVesting
    );

    /// @notice An event that emitted when a new Time Lock is registered
    event NewTimeLockRegistration(address previousAddress, address newAddress);

    /// @notice An event that emitted when a new Rewards Distributor is registered
    event NewRewardsDistributorRegistration(address previousAddress, address newAddress);

    /// @notice An event that emitted when a cancellation of Lock tokens is registered
    event Cancel(address account, uint256 amount);

    /// @notice An event that emitted when a claim of tokens are registered
    event Claim(address _receiver, uint256 amount);

    /// @notice An event that emitted when a lockedBalance query is done
    event LockedBalance(address _account, uint256 amount);

    /* ============ Modifiers ============ */

    modifier onlyTimeLockRegistry() {
        require(
            msg.sender == address(timeLockRegistry),
            'TimeLockedToken:: onlyTimeLockRegistry: can only be executed by TimeLockRegistry'
        );
        _;
    }

    modifier onlyTimeLockOwner() {
        if (address(timeLockRegistry) != address(0)) {
            require(
                msg.sender == Ownable(timeLockRegistry).owner(),
                'TimeLockedToken:: onlyTimeLockOwner: can only be executed by the owner of TimeLockRegistry'
            );
        }
        _;
    }
    modifier onlyUnpaused() {
        // Do not execute if Globally or individually paused
        _require(!IBabController(controller).isPaused(address(this)), Errors.ONLY_UNPAUSED);
        _;
    }

    /* ============ State Variables ============ */

    // represents total distribution for locked balances
    mapping(address => uint256) distribution;

    /// @notice The profile of each token owner under its particular vesting conditions
    /**
     * @param team Indicates whether or not is a Team member or Advisor (true = team member/advisor, false = private investor)
     * @param vestingBegin When the vesting begins for such token owner
     * @param vestingEnd When the vesting ends for such token owner
     * @param lastClaim When the last claim was done
     */
    struct VestedToken {
        bool teamOrAdvisor;
        uint256 vestingBegin;
        uint256 vestingEnd;
        uint256 lastClaim;
    }

    /// @notice A record of token owners under vesting conditions for each account, by index
    mapping(address => VestedToken) public vestedToken;

    // address of Time Lock Registry contract
    IBabController public controller;

    // address of Time Lock Registry contract
    TimeLockRegistry public timeLockRegistry;

    // address of Rewards Distriburor contract
    IRewardsDistributor public rewardsDistributor;

    // Enable Transfer of ERC20 BABL Tokens
    // Only Minting or transfers from/to TimeLockRegistry and Rewards Distributor can transfer tokens until the protocol is fully decentralized
    bool private tokenTransfersEnabled;
    bool private tokenTransfersWereDisabled;

    /* ============ Functions ============ */

    /* ============ Constructor ============ */

    constructor(string memory _name, string memory _symbol) VoteToken(_name, _symbol) {
        tokenTransfersEnabled = true;
    }

    /* ============ External Functions ============ */

    /* ===========  Token related Gov Functions ====== */

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Disables transfers of ERC20 BABL Tokens
     */
    function disableTokensTransfers() external onlyOwner {
        require(!tokenTransfersWereDisabled, 'BABL must flow');
        tokenTransfersEnabled = false;
        tokenTransfersWereDisabled = true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Allows transfers of ERC20 BABL Tokens
     * Can only happen after the protocol is fully decentralized.
     */
    function enableTokensTransfers() external onlyOwner {
        tokenTransfersEnabled = true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Set the Time Lock Registry contract to control token vesting conditions
     *
     * @notice Set the Time Lock Registry contract to control token vesting conditions
     * @param newTimeLockRegistry Address of TimeLockRegistry contract
     */
    function setTimeLockRegistry(TimeLockRegistry newTimeLockRegistry) external onlyTimeLockOwner returns (bool) {
        require(address(newTimeLockRegistry) != address(0), 'cannot be zero address');
        require(address(newTimeLockRegistry) != address(this), 'cannot be this contract');
        require(address(newTimeLockRegistry) != address(timeLockRegistry), 'must be new TimeLockRegistry');
        emit NewTimeLockRegistration(address(timeLockRegistry), address(newTimeLockRegistry));

        timeLockRegistry = newTimeLockRegistry;

        return true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Set the Rewards Distributor contract to control either BABL Mining or profit rewards
     *
     * @notice Set the Rewards Distriburor contract to control both types of rewards (profit and BABL Mining program)
     * @param newRewardsDistributor Address of Rewards Distributor contract
     */
    function setRewardsDistributor(IRewardsDistributor newRewardsDistributor) external onlyOwner returns (bool) {
        require(address(newRewardsDistributor) != address(0), 'cannot be zero address');
        require(address(newRewardsDistributor) != address(this), 'cannot be this contract');
        require(address(newRewardsDistributor) != address(rewardsDistributor), 'must be new Rewards Distributor');
        emit NewRewardsDistributorRegistration(address(rewardsDistributor), address(newRewardsDistributor));

        rewardsDistributor = newRewardsDistributor;

        return true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Register new token lockup conditions for vested tokens defined only by Time Lock Registry
     *
     * @notice Tokens are completely delivered during the registration however lockup conditions apply for vested tokens
     * locking them according to the distribution epoch periods and the type of recipient (Team, Advisor, Investor)
     * Emits a transfer event showing a transfer to the recipient
     * Only the registry can call this function
     * @param _receiver Address to receive the tokens
     * @param _amount Tokens to be transferred
     * @param _profile True if is a Team Member or Advisor
     * @param _vestingBegin Unix Time when the vesting for that particular address
     * @param _vestingEnd Unix Time when the vesting for that particular address
     * @param _lastClaim Unix Time when the claim was done from that particular address
     *
     */
    function registerLockup(
        address _receiver,
        uint256 _amount,
        bool _profile,
        uint256 _vestingBegin,
        uint256 _vestingEnd,
        uint256 _lastClaim
    ) external onlyTimeLockRegistry returns (bool) {
        require(balanceOf(msg.sender) >= _amount, 'insufficient balance');
        require(_receiver != address(0), 'cannot be zero address');
        require(_receiver != address(this), 'cannot be this contract');
        require(_receiver != address(timeLockRegistry), 'cannot be the TimeLockRegistry contract itself');
        require(_receiver != msg.sender, 'the owner cannot lockup itself');

        // update amount of locked distribution
        distribution[_receiver] = distribution[_receiver].add(_amount);

        VestedToken storage newVestedToken = vestedToken[_receiver];

        newVestedToken.teamOrAdvisor = _profile;
        newVestedToken.vestingBegin = _vestingBegin;
        newVestedToken.vestingEnd = _vestingEnd;
        newVestedToken.lastClaim = _lastClaim;

        // transfer tokens to the recipient
        _transfer(msg.sender, _receiver, _amount);
        emit NewLockout(_receiver, _amount, _profile, _vestingBegin, _vestingEnd);

        return true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Cancel and remove locked tokens due to non-completion of vesting period
     * applied only by Time Lock Registry and specifically to Team or Advisors as it does not apply to investors.
     *
     * @dev Cancel distribution registration
     * @param lockedAccount that should have its still locked distribution removed due to non-completion of its vesting period
     */
    function cancelVestedTokens(address lockedAccount) external onlyTimeLockRegistry returns (uint256) {
        return _cancelVestedTokensFromTimeLock(lockedAccount);
    }

    /**
     * GOVERNANCE FUNCTION. Each token owner can claim its own specific tokens with its own specific vesting conditions from the Time Lock Registry
     *
     * @dev Claim msg.sender tokens (if any available in the registry)
     */
    function claimMyTokens() external {
        // claim msg.sender tokens from timeLockRegistry
        uint256 amount = timeLockRegistry.claim(msg.sender);
        // After a proper claim, locked tokens of Team and Advisors profiles are under restricted special vesting conditions so they automatic grant
        // rights to the Time Lock Registry to only retire locked tokens if non-compliance vesting conditions take places along the vesting periods.
        // It does not apply to Investors under vesting (their locked tokens cannot be removed).
        if (vestedToken[msg.sender].teamOrAdvisor == true) {
            approve(address(timeLockRegistry), amount);
        }
        // emit claim event
        emit Claim(msg.sender, amount);
    }

    /**
     * GOVERNANCE FUNCTION. Get unlocked balance for an account
     *
     * @notice Get unlocked balance for an account
     * @param account Account to check
     * @return Amount that is unlocked and available eg. to transfer
     */
    function unlockedBalance(address account) public returns (uint256) {
        // totalBalance - lockedBalance
        return balanceOf(account).sub(lockedBalance(account));
    }

    /**
     * GOVERNANCE FUNCTION. View the locked balance for an account
     *
     * @notice View locked balance for an account
     * @param account Account to check
     * @return Amount locked in the time of checking
     */

    function viewLockedBalance(address account) public view returns (uint256) {
        // distribution of locked tokens
        // get amount from distributions

        uint256 amount = distribution[account];
        uint256 lockedAmount = amount;

        // Team and investors cannot transfer tokens in the first year
        if (vestedToken[account].vestingBegin.add(365 days) > block.timestamp && amount != 0) {
            return lockedAmount;
        }

        // in case of vesting has passed, all tokens are now available, if no vesting lock is 0 as well
        if (block.timestamp >= vestedToken[account].vestingEnd || amount == 0) {
            lockedAmount = 0;
        } else if (amount != 0) {
            // in case of still under vesting period, locked tokens are recalculated
            lockedAmount = amount.mul(vestedToken[account].vestingEnd.sub(block.timestamp)).div(
                vestedToken[account].vestingEnd.sub(vestedToken[account].vestingBegin)
            );
        }
        return lockedAmount;
    }

    /**
     * GOVERNANCE FUNCTION. Get locked balance for an account
     *
     * @notice Get locked balance for an account
     * @param account Account to check
     * @return Amount locked in the time of checking
     */
    function lockedBalance(address account) public returns (uint256) {
        // get amount from distributions locked tokens (if any)
        uint256 lockedAmount = viewLockedBalance(account);
        // in case of vesting has passed, all tokens are now available so we set mapping to 0 only for accounts under vesting
        if (
            block.timestamp >= vestedToken[account].vestingEnd &&
            msg.sender == account &&
            lockedAmount == 0 &&
            vestedToken[account].vestingEnd != 0
        ) {
            delete distribution[account];
        }
        emit LockedBalance(account, lockedAmount);
        return lockedAmount;
    }

    /**
     * PUBLIC FUNCTION. Get the address of Time Lock Registry
     *
     * @notice Get the address of Time Lock Registry
     * @return Address of the Time Lock Registry
     */
    function getTimeLockRegistry() external view returns (address) {
        return address(timeLockRegistry);
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Override the Approval of allowances of ERC20 with special conditions for vesting
     *
     * @notice Override of "Approve" function to allow the `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender` except in the case of spender is Time Lock Registry
     * and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
     * @return Whether or not the approval succeeded
     */
    function approve(address spender, uint256 rawAmount) public override nonReentrant returns (bool) {
        require(spender != address(0), 'TimeLockedToken::approve: spender cannot be zero address');
        require(spender != msg.sender, 'TimeLockedToken::approve: spender cannot be the msg.sender');

        uint96 amount;
        if (rawAmount == uint256(-1)) {
            amount = uint96(-1);
        } else {
            amount = safe96(rawAmount, 'TimeLockedToken::approve: amount exceeds 96 bits');
        }

        // There is no option to decreaseAllowance to timeLockRegistry in case of vested tokens
        if ((spender == address(timeLockRegistry)) && (amount < allowance(msg.sender, address(timeLockRegistry)))) {
            amount = safe96(
                allowance(msg.sender, address(timeLockRegistry)),
                'TimeLockedToken::approve: cannot decrease allowance to timelockregistry'
            );
        }
        _approve(msg.sender, spender, amount);
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Override the Increase of allowances of ERC20 with special conditions for vesting
     *
     * @notice Atomically increases the allowance granted to `spender` by the caller.
     *
     * @dev This is an override with respect to the fulfillment of vesting conditions along the way
     * However an user can increase allowance many times, it will never be able to transfer locked tokens during vesting period
     * @return Whether or not the increaseAllowance succeeded
     */
    function increaseAllowance(address spender, uint256 addedValue) public override nonReentrant returns (bool) {
        require(
            unlockedBalance(msg.sender) >= allowance(msg.sender, spender).add(addedValue) ||
                spender == address(timeLockRegistry),
            'TimeLockedToken::increaseAllowance:Not enough unlocked tokens'
        );
        require(spender != address(0), 'TimeLockedToken::increaseAllowance:Spender cannot be zero address');
        require(spender != msg.sender, 'TimeLockedToken::increaseAllowance:Spender cannot be the msg.sender');
        _approve(msg.sender, spender, allowance(msg.sender, spender).add(addedValue));
        return true;
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Override the decrease of allowances of ERC20 with special conditions for vesting
     *
     * @notice Atomically decrease the allowance granted to `spender` by the caller.
     *
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     * This is an override with respect to the fulfillment of vesting conditions along the way
     * An user cannot decrease the allowance to the Time Lock Registry who is in charge of vesting conditions
     * @return Whether or not the decreaseAllowance succeeded
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public override nonReentrant returns (bool) {
        require(spender != address(0), 'TimeLockedToken::decreaseAllowance:Spender cannot be zero address');
        require(spender != msg.sender, 'TimeLockedToken::decreaseAllowance:Spender cannot be the msg.sender');
        require(
            allowance(msg.sender, spender) >= subtractedValue,
            'TimeLockedToken::decreaseAllowance:Underflow condition'
        );

        // There is no option to decreaseAllowance to timeLockRegistry in case of vested tokens
        require(
            address(spender) != address(timeLockRegistry),

exec
/bin/bash -lc "sed -n '4681,5460p' AUDIT_BUNDLE.txt" in /tmp/codex-bjkv0enx
 succeeded in 0ms:
            'TimeLockedToken::decreaseAllowance:cannot decrease allowance to timeLockRegistry'
        );

        _approve(msg.sender, spender, allowance(msg.sender, spender).sub(subtractedValue));
        return true;
    }

    /* ============ Internal Only Function ============ */

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Override the _transfer of ERC20 BABL tokens only allowing the transfer of unlocked tokens
     *
     * @dev Transfer function which includes only unlocked tokens
     * Locked tokens can always be transfered back to the returns address
     * Transferring to owner allows re-issuance of funds through registry
     *
     * @param _from The address to send tokens from
     * @param _to The address that will receive the tokens
     * @param _value The amount of tokens to be transferred
     */
    function _transfer(
        address _from,
        address _to,
        uint256 _value
    ) internal override onlyUnpaused {
        require(_from != address(0), 'TimeLockedToken:: _transfer: cannot transfer from the zero address');
        require(_to != address(0), 'TimeLockedToken:: _transfer: cannot transfer to the zero address');
        require(
            _to != address(this),
            'TimeLockedToken:: _transfer: do not transfer tokens to the token contract itself'
        );

        require(balanceOf(_from) >= _value, 'TimeLockedToken:: _transfer: insufficient balance');

        // check if enough unlocked balance to transfer
        require(unlockedBalance(_from) >= _value, 'TimeLockedToken:: _transfer: attempting to transfer locked funds');
        super._transfer(_from, _to, _value);
        // voting power
        _moveDelegates(
            delegates[_from],
            delegates[_to],
            safe96(_value, 'TimeLockedToken:: _transfer: uint96 overflow')
        );
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Disable BABL token transfer until certain conditions are met
     *
     * @dev Override the _beforeTokenTransfer of ERC20 BABL tokens until certain conditions are met:
     * Only allowing minting or transfers from Time Lock Registry and Rewards Distributor until transfers are allowed in the controller
     * Transferring to owner allows re-issuance of funds through registry
     *
     * @param _from The address to send tokens from
     * @param _to The address that will receive the tokens
     * @param _value The amount of tokens to be transferred
     */

    // Disable garden token transfers. Allow minting and burning.
    function _beforeTokenTransfer(
        address _from,
        address _to,
        uint256 _value
    ) internal virtual override {
        super._beforeTokenTransfer(_from, _to, _value);
        _require(
            _from == address(0) ||
                _from == address(timeLockRegistry) ||
                _from == address(rewardsDistributor) ||
                _to == address(timeLockRegistry) ||
                tokenTransfersEnabled,
            Errors.BABL_TRANSFERS_DISABLED
        );
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Cancel and remove locked tokens due to non-completion of  vesting period
     * applied only by Time Lock Registry and specifically to Team or Advisors
     *
     * @dev Cancel distribution registration
     * @param lockedAccount that should have its still locked distribution removed due to non-completion of its vesting period
     */
    function _cancelVestedTokensFromTimeLock(address lockedAccount) internal onlyTimeLockRegistry returns (uint256) {
        require(distribution[lockedAccount] != 0, 'TimeLockedToken::cancelTokens:Not registered');

        // get an update on locked amount from distributions at this precise moment
        uint256 loosingAmount = lockedBalance(lockedAccount);

        require(loosingAmount > 0, 'TimeLockedToken::cancelTokens:There are no more locked tokens');
        require(
            vestedToken[lockedAccount].teamOrAdvisor == true,
            'TimeLockedToken::cancelTokens:cannot cancel locked tokens to Investors'
        );

        // set distribution mapping to 0
        delete distribution[lockedAccount];

        // set tokenVested mapping to 0
        delete vestedToken[lockedAccount];

        // transfer only locked tokens back to TimeLockRegistry Owner (msg.sender)
        require(
            transferFrom(lockedAccount, address(timeLockRegistry), loosingAmount),
            'TimeLockedToken::cancelTokens:Transfer failed'
        );

        // emit cancel event
        emit Cancel(lockedAccount, loosingAmount);

        return loosingAmount;
    }
}

================================================================================
FILE: contracts/token/VoteToken.sol
================================================================================
// SPDX-License-Identifier: Apache-2.0

pragma solidity 0.7.6;

import {ERC20} from '@openzeppelin/contracts/token/ERC20/ERC20.sol';
import {IVoteToken} from '../interfaces/IVoteToken.sol';
import {ReentrancyGuard} from '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import {LowGasSafeMath} from '../lib/LowGasSafeMath.sol';
import {Context} from '@openzeppelin/contracts/utils/Context.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {Address} from '@openzeppelin/contracts/utils/Address.sol';

/**
 * @title VoteToken
 * @notice Custom token which tracks voting power for governance
 * @dev This is an abstraction of a fork of the Compound governance contract
 * VoteToken is used by BABL to allow tracking voting power
 * Checkpoints are created every time state is changed which record voting power
 * Inherits standard ERC20 behavior
 */

abstract contract VoteToken is Context, ERC20, Ownable, IVoteToken, ReentrancyGuard {
    using LowGasSafeMath for uint256;
    using Address for address;

    /* ============ Events ============ */

    event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
    event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);

    /* ============ Modifiers ============ */

    /* ============ State Variables ============ */

    /// @notice The EIP-712 typehash for the contract's domain
    bytes32 public constant DOMAIN_TYPEHASH =
        keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');

    /// @notice The EIP-712 typehash for the delegation struct used by the contract
    bytes32 public constant DELEGATION_TYPEHASH =
        keccak256('Delegation(address delegatee,uint256 nonce,uint256 expiry)');

    /// @dev A record of votes checkpoints for each account, by index
    mapping(address => address) public delegates;

    /// @notice A checkpoint for marking number of votes from a given block
    struct Checkpoint {
        uint32 fromBlock;
        uint96 votes;
    }

    /// @notice A record of votes checkpoints for each account, by index
    mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;

    /// @notice The number of checkpoints for each account
    mapping(address => uint32) public numCheckpoints;

    /// @notice A record of states for signing / validating signatures
    mapping(address => uint256) public nonces;

    /* ============ Functions ============ */

    /* ============ Constructor ============ */

    constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) {}

    /* ============ External Functions ============ */

    /* ===========  Token related Gov Functions ====== */

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Delegating votes from msg.sender to delegatee
     *
     * @notice Delegate votes from `msg.sender` to `delegatee`
     * @param delegatee The address to delegate votes to
     */

    function delegate(address delegatee) external override {
        return _delegate(msg.sender, delegatee);
    }

    /**
     * PRIVILEGED GOVERNANCE FUNCTION. Delegate votes using signature to 'delegatee'
     *
     * @notice Delegates votes from signatory to `delegatee`
     * @param delegatee The address to delegate votes to
     * @param nonce The contract state required to match the signature
     * @param expiry The time at which to expire the signature
     * @param v The recovery byte of the signature
     * @param r Half of the ECDSA signature pair
     * @param s Half of the ECDSA signature pair
     */

    function delegateBySig(
        address delegatee,
        uint256 nonce,
        uint256 expiry,
        uint8 v,
        bytes32 r,
        bytes32 s,
        bool prefix
    ) external override {
        address signatory;
        bytes32 domainSeparator =
            keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), getChainId(), address(this)));
        bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
        bytes32 digest = keccak256(abi.encodePacked('\x19\x01', domainSeparator, structHash));
        if (prefix) {
            bytes32 digestHash = keccak256(abi.encodePacked('\x19Ethereum Signed Message:\n32', digest));
            signatory = ecrecover(digestHash, v, r, s);
        } else {
            signatory = ecrecover(digest, v, r, s);
        }

        require(balanceOf(signatory) > 0, 'VoteToken::delegateBySig: invalid delegator');
        require(signatory != address(0), 'VoteToken::delegateBySig: invalid signature');
        require(nonce == nonces[signatory], 'VoteToken::delegateBySig: invalid nonce');
        nonces[signatory]++;
        require(block.timestamp <= expiry, 'VoteToken::delegateBySig: signature expired');
        return _delegate(signatory, delegatee);
    }

    /**
     * GOVERNANCE FUNCTION. Check Delegate votes using signature to 'delegatee'
     *
     * @notice Get current voting power for an account
     * @param account Account to get voting power for
     * @return Voting power for an account
     */
    function getCurrentVotes(address account) external view virtual override returns (uint96) {
        uint32 nCheckpoints = numCheckpoints[account];
        return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
    }

    /**
     * GOVERNANCE FUNCTION. Get voting power at a specific block for an account
     *
     * @param account Account to get voting power for
     * @param blockNumber Block to get voting power at
     * @return Voting power for an account at specific block
     */
    function getPriorVotes(address account, uint256 blockNumber) external view virtual override returns (uint96) {
        require(blockNumber < block.number, 'BABLToken::getPriorVotes: not yet determined');
        uint32 nCheckpoints = numCheckpoints[account];
        if (nCheckpoints == 0) {
            return 0;
        }

        // First check most recent balance
        if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
            return checkpoints[account][nCheckpoints - 1].votes;
        }

        // Next check implicit zero balance
        if (checkpoints[account][0].fromBlock > blockNumber) {
            return 0;
        }

        uint32 lower = 0;
        uint32 upper = nCheckpoints - 1;
        while (upper > lower) {
            uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
            Checkpoint memory cp = checkpoints[account][center];
            if (cp.fromBlock == blockNumber) {
                return cp.votes;
            } else if (cp.fromBlock < blockNumber) {
                lower = center;
            } else {
                upper = center - 1;
            }
        }
        return checkpoints[account][lower].votes;
    }

    function getMyDelegatee() external view override returns (address) {
        return delegates[msg.sender];
    }

    function getDelegatee(address account) external view override returns (address) {
        return delegates[account];
    }

    function getCheckpoints(address account, uint32 id)
        external
        view
        override
        returns (uint32 fromBlock, uint96 votes)
    {
        Checkpoint storage getCheckpoint = checkpoints[account][id];
        return (getCheckpoint.fromBlock, getCheckpoint.votes);
    }

    function getNumberOfCheckpoints(address account) external view override returns (uint32) {
        return numCheckpoints[account];
    }

    /* ============ Internal Only Function ============ */

    /**
     * GOVERNANCE FUNCTION. Make a delegation
     *
     * @dev Internal function to delegate voting power to an account
     * @param delegator The address of the account delegating votes from
     * @param delegatee The address to delegate votes to
     */

    function _delegate(address delegator, address delegatee) internal {
        address currentDelegate = delegates[delegator];
        uint96 delegatorBalance = safe96(_balanceOf(delegator), 'VoteToken::_delegate: uint96 overflow');
        delegates[delegator] = delegatee;

        emit DelegateChanged(delegator, currentDelegate, delegatee);

        _moveDelegates(currentDelegate, delegatee, delegatorBalance);
    }

    function _balanceOf(address account) internal view virtual returns (uint256) {
        return balanceOf(account);
    }

    /**
     * GOVERNANCE FUNCTION. Move the delegates
     *
     * @dev Internal function to move delegates between accounts
     * @param srcRep The address of the account delegating votes from
     * @param dstRep The address of the account delegating votes to
     * @param amount The voting power to move
     */
    function _moveDelegates(
        address srcRep,
        address dstRep,
        uint96 amount
    ) internal {
        if (srcRep != dstRep && amount > 0) {
            // It must not revert but do nothing in cases of address(0) being part of the move
            // Sub voting amount to source in case it is not the zero address (e.g. transfers)
            if (srcRep != address(0)) {
                uint32 srcRepNum = numCheckpoints[srcRep];
                uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
                uint96 srcRepNew = sub96(srcRepOld, amount, 'VoteToken::_moveDelegates: vote amount underflows');
                _writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
            }
            if (dstRep != address(0)) {
                // Add it to destination in case it is not the zero address (e.g. any transfer of tokens or delegations except a first mint to a specific address)
                uint32 dstRepNum = numCheckpoints[dstRep];
                uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
                uint96 dstRepNew = add96(dstRepOld, amount, 'VoteToken::_moveDelegates: vote amount overflows');
                _writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
            }
        }
    }

    /**
     * GOVERNANCE FUNCTION. Internal function to write a checkpoint for voting power
     *
     * @dev internal function to write a checkpoint for voting power
     * @param delegatee The address of the account delegating votes to
     * @param nCheckpoints The num checkpoint
     * @param oldVotes The previous voting power
     * @param newVotes The new voting power
     */
    function _writeCheckpoint(
        address delegatee,
        uint32 nCheckpoints,
        uint96 oldVotes,
        uint96 newVotes
    ) internal {
        uint32 blockNumber = safe32(block.number, 'VoteToken::_writeCheckpoint: block number exceeds 32 bits');

        if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
            checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
        } else {
            checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
            numCheckpoints[delegatee] = nCheckpoints + 1;
        }

        emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
    }

    /**
     * INTERNAL FUNCTION. Internal function to convert from uint256 to uint32
     *
     * @dev internal function to convert from uint256 to uint32
     */
    function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {
        require(n < 2**32, errorMessage);
        return uint32(n);
    }

    /**
     * INTERNAL FUNCTION. Internal function to convert from uint256 to uint96
     *
     * @dev internal function to convert from uint256 to uint96
     */
    function safe96(uint256 n, string memory errorMessage) internal pure returns (uint96) {
        require(n < 2**96, errorMessage);
        return uint96(n);
    }

    /**
     * INTERNAL FUNCTION. Internal function to add two uint96 numbers
     *
     * @dev internal safe math function to add two uint96 numbers
     */
    function add96(
        uint96 a,
        uint96 b,
        string memory errorMessage
    ) internal pure returns (uint96) {
        uint96 c = a + b;
        require(c >= a, errorMessage);
        return c;
    }

    /**
     * INTERNAL FUNCTION. Internal function to subtract two uint96 numbers
     *
     * @dev internal safe math function to subtract two uint96 numbers
     */
    function sub96(
        uint96 a,
        uint96 b,
        string memory errorMessage
    ) internal pure returns (uint96) {
        require(b <= a, errorMessage);
        return a - b;
    }

    /**
     * INTERNAL FUNCTION. Internal function to get chain ID
     *
     * @dev internal function to get chain ID
     */
    function getChainId() internal pure returns (uint256) {
        uint256 chainId;
        assembly {
            chainId := chainid()
        }
        return chainId;
    }
}

================================================================================
FILE: hardhat/console.sol
================================================================================
// SPDX-License-Identifier: MIT
pragma solidity >= 0.4.22 <0.9.0;

library console {
	address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);

	function _sendLogPayload(bytes memory payload) private view {
		uint256 payloadLength = payload.length;
		address consoleAddress = CONSOLE_ADDRESS;
		assembly {
			let payloadStart := add(payload, 32)
			let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
		}
	}

	function log() internal view {
		_sendLogPayload(abi.encodeWithSignature("log()"));
	}

	function logInt(int p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(int)", p0));
	}

	function logUint(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function logString(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function logBool(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function logAddress(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function logBytes(bytes memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
	}

	function logBytes1(bytes1 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
	}

	function logBytes2(bytes2 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
	}

	function logBytes3(bytes3 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
	}

	function logBytes4(bytes4 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
	}

	function logBytes5(bytes5 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
	}

	function logBytes6(bytes6 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
	}

	function logBytes7(bytes7 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
	}

	function logBytes8(bytes8 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
	}

	function logBytes9(bytes9 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
	}

	function logBytes10(bytes10 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
	}

	function logBytes11(bytes11 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
	}

	function logBytes12(bytes12 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
	}

	function logBytes13(bytes13 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
	}

	function logBytes14(bytes14 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
	}

	function logBytes15(bytes15 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
	}

	function logBytes16(bytes16 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
	}

	function logBytes17(bytes17 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
	}

	function logBytes18(bytes18 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
	}

	function logBytes19(bytes19 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
	}

	function logBytes20(bytes20 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
	}

	function logBytes21(bytes21 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
	}

	function logBytes22(bytes22 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
	}

	function logBytes23(bytes23 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
	}

	function logBytes24(bytes24 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
	}

	function logBytes25(bytes25 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
	}

	function logBytes26(bytes26 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
	}

	function logBytes27(bytes27 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
	}

	function logBytes28(bytes28 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
	}

	function logBytes29(bytes29 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
	}

	function logBytes30(bytes30 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
	}

	function logBytes31(bytes31 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
	}

	function logBytes32(bytes32 p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
	}

	function log(uint p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
	}

	function log(string memory p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
	}

	function log(bool p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
	}

	function log(address p0) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
	}

	function log(uint p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
	}

	function log(uint p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
	}

	function log(uint p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
	}

	function log(uint p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
	}

	function log(string memory p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
	}

	function log(string memory p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
	}

	function log(string memory p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
	}

	function log(string memory p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
	}

	function log(bool p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
	}

	function log(bool p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
	}

	function log(bool p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
	}

	function log(bool p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
	}

	function log(address p0, uint p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
	}

	function log(address p0, string memory p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
	}

	function log(address p0, bool p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
	}

	function log(address p0, address p1) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
	}

	function log(uint p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
	}

	function log(uint p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
	}

	function log(uint p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
	}

	function log(uint p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
	}

	function log(uint p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
	}

	function log(uint p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
	}

	function log(uint p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
	}

	function log(uint p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
	}

	function log(uint p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
	}

	function log(uint p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
	}

	function log(uint p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
	}

	function log(uint p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
	}

	function log(uint p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, string memory p2) internal view {

exec
/bin/bash -lc "sed -n '5461,6673p' AUDIT_BUNDLE.txt" in /tmp/codex-bjkv0enx
 succeeded in 0ms:
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
	}

	function log(string memory p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
	}

	function log(string memory p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
	}

	function log(string memory p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
	}

	function log(string memory p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
	}

	function log(string memory p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
	}

	function log(string memory p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
	}

	function log(string memory p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
	}

	function log(bool p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
	}

	function log(bool p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
	}

	function log(bool p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
	}

	function log(bool p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
	}

	function log(bool p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
	}

	function log(bool p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
	}

	function log(bool p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
	}

	function log(bool p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
	}

	function log(bool p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
	}

	function log(bool p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
	}

	function log(bool p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
	}

	function log(bool p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
	}

	function log(bool p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
	}

	function log(address p0, uint p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
	}

	function log(address p0, uint p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
	}

	function log(address p0, uint p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
	}

	function log(address p0, uint p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
	}

	function log(address p0, string memory p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
	}

	function log(address p0, string memory p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
	}

	function log(address p0, string memory p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
	}

	function log(address p0, string memory p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
	}

	function log(address p0, bool p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
	}

	function log(address p0, bool p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
	}

	function log(address p0, bool p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
	}

	function log(address p0, bool p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
	}

	function log(address p0, address p1, uint p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
	}

	function log(address p0, address p1, string memory p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
	}

	function log(address p0, address p1, bool p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
	}

	function log(address p0, address p1, address p2) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
	}

	function log(uint p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
	}

	function log(uint p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
	}

	function log(string memory p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
	}

	function log(bool p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, uint p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, string memory p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, bool p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, uint p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, string memory p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, bool p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, uint p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, string memory p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, bool p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
	}

	function log(address p0, address p1, address p2, address p3) internal view {
		_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
	}

}


exec
/bin/bash -lc "nl -ba contracts/gardens/Garden.sol | sed -n '1,420p'" in /tmp/codex-bjkv0enx
exec
/bin/bash -lc "rg -n \"function (initialize|makeGardenPublic|transferCreatorRights|addExtraCreators|setPublicRights|delegateVotes|updateCreators|updateGardenParams|verifyGarden|resetHardlock|finalizeStrategy|allocateCapitalToStrategy|expireCandidateStrategy|addStrategy|payKeeper|updateStrategyRewards|wrap)\" contracts/gardens/Garden.sol contracts -g '"'!lib/**'"' -g '"'!@openzeppelin/**'"'" in /tmp/codex-bjkv0enx
 succeeded in 0ms:
contracts/interfaces/IGarden.sol:16:    function wrap() external;
contracts/interfaces/IGarden.sol:25:    function finalizeStrategy(
contracts/interfaces/IGarden.sol:31:    function allocateCapitalToStrategy(uint256 _capital) external;
contracts/interfaces/IGarden.sol:33:    function expireCandidateStrategy() external;
contracts/interfaces/IGarden.sol:35:    function addStrategy(
contracts/interfaces/IGarden.sol:44:    function payKeeper(address payable _keeper, uint256 _fee) external;
contracts/interfaces/IGarden.sol:46:    function updateStrategyRewards(
contracts/interfaces/IGarden.sol:60:    function initialize(
contracts/interfaces/IGarden.sol:71:    function makeGardenPublic() external;
contracts/interfaces/IGarden.sol:73:    function transferCreatorRights(address _newCreator, uint8 _index) external;
contracts/interfaces/IGarden.sol:75:    function addExtraCreators(address[4] memory _newCreators) external;
contracts/interfaces/IGarden.sol:77:    function setPublicRights(bool _publicStrategist, bool _publicStewards) external;
contracts/interfaces/IGarden.sol:79:    function delegateVotes(address _token, address _address) external;
contracts/interfaces/IGarden.sol:81:    function updateCreators(address _newCreator, address[4] memory _newCreators) external;
contracts/interfaces/IGarden.sol:83:    function updateGardenParams(uint256[13] memory _newParams) external;
contracts/interfaces/IGarden.sol:85:    function verifyGarden(uint256 _verifiedCategory) external;
contracts/interfaces/IGarden.sol:87:    function resetHardlock(uint256 _hardlockStartsAt) external;
contracts/interfaces/IStrategy.sol:14:    function initialize(
contracts/interfaces/IStrategy.sol:64:    function finalizeStrategy(
contracts/interfaces/IStrategy.sol:101:    function updateStrategyRewards(uint256 _newTotalBABLRewards, uint256 _newCapitalReturned) external;

 succeeded in 0ms:
     1	// SPDX-License-Identifier: Apache-2.0
     2	
     3	pragma solidity 0.7.6;
     4	import {Address} from '@openzeppelin/contracts/utils/Address.sol';
     5	import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
     6	import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
     7	import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
     8	import {ReentrancyGuard} from '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
     9	import {ECDSA} from '@openzeppelin/contracts/cryptography/ECDSA.sol';
    10	import {ERC20Upgradeable} from '@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol';
    11	
    12	import {LowGasSafeMath} from '../lib/LowGasSafeMath.sol';
    13	import {SafeDecimalMath} from '../lib/SafeDecimalMath.sol';
    14	import {SafeCast} from '@openzeppelin/contracts/utils/SafeCast.sol';
    15	import {SignedSafeMath} from '@openzeppelin/contracts/math/SignedSafeMath.sol';
    16	import {Errors, _require, _revert} from '../lib/BabylonErrors.sol';
    17	import {AddressArrayUtils} from '../lib/AddressArrayUtils.sol';
    18	import {PreciseUnitMath} from '../lib/PreciseUnitMath.sol';
    19	import {Math} from '../lib/Math.sol';
    20	import {ControllerLib} from '../lib/ControllerLib.sol';
    21	import {SignatureChecker} from '../lib/SignatureChecker.sol';
    22	
    23	import {IPriceOracle} from '../interfaces/IPriceOracle.sol';
    24	import {IRewardsDistributor} from '../interfaces/IRewardsDistributor.sol';
    25	import {IBabController} from '../interfaces/IBabController.sol';
    26	import {IStrategyFactory} from '../interfaces/IStrategyFactory.sol';
    27	import {IGardenValuer} from '../interfaces/IGardenValuer.sol';
    28	import {IStrategy} from '../interfaces/IStrategy.sol';
    29	import {IGarden, ICoreGarden} from '../interfaces/IGarden.sol';
    30	import {IGardenNFT} from '../interfaces/IGardenNFT.sol';
    31	import {IMardukGate} from '../interfaces/IMardukGate.sol';
    32	import {IWETH} from '../interfaces/external/weth/IWETH.sol';
    33	import {IHeart} from '../interfaces/IHeart.sol';
    34	import {IERC1271} from '../interfaces/IERC1271.sol';
    35	
    36	import {VTableBeaconProxy} from '../proxy/VTableBeaconProxy.sol';
    37	import {VTableBeacon} from '../proxy/VTableBeacon.sol';
    38	
    39	import {TimeLockedToken} from '../token/TimeLockedToken.sol';
    40	
    41	/**
    42	 * @title Garden
    43	 *
    44	 * User facing features of Garden plus BeaconProxy
    45	 */
    46	contract Garden is ERC20Upgradeable, ReentrancyGuard, VTableBeaconProxy, ICoreGarden, IERC1271 {
    47	    using SafeCast for int256;
    48	    using SignedSafeMath for int256;
    49	    using PreciseUnitMath for int256;
    50	    using SafeDecimalMath for int256;
    51	
    52	    using SafeCast for uint256;
    53	    using LowGasSafeMath for uint256;
    54	    using PreciseUnitMath for uint256;
    55	    using SafeDecimalMath for uint256;
    56	
    57	    using Address for address;
    58	    using AddressArrayUtils for address[];
    59	
    60	    using SafeERC20 for IERC20;
    61	    using ECDSA for bytes32;
    62	    using ControllerLib for IBabController;
    63	
    64	    using SignatureChecker for address;
    65	
    66	    /* ============ Events ============ */
    67	
    68	    // DO NOT TOUCH for the love of GOD
    69	    event GardenDeposit(address indexed _to, uint256 reserveToken, uint256 reserveTokenQuantity, uint256 timestamp);
    70	    event GardenWithdrawal(
    71	        address indexed _from,
    72	        address indexed _to,
    73	        uint256 reserveToken,
    74	        uint256 reserveTokenQuantity,
    75	        uint256 timestamp
    76	    );
    77	
    78	    event RewardsForContributor(address indexed _contributor, uint256 indexed _amount);
    79	    event BABLRewardsForContributor(address indexed _contributor, uint256 _rewards);
    80	    event StakeBABLRewards(address indexed _contributor, uint256 _babl);
    81	
    82	    /* ============ Constants ============ */
    83	
    84	    // Wrapped ETH address
    85	    address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    86	    IERC20 private immutable BABL;
    87	
    88	    // Strategy cooldown period
    89	    uint256 private constant MIN_COOLDOWN_PERIOD = 60 seconds;
    90	    uint256 private constant MAX_COOLDOWN_PERIOD = 7 days;
    91	
    92	    uint8 private constant MAX_EXTRA_CREATORS = 4;
    93	    uint256 private constant EARLY_WITHDRAWAL_PENALTY = 25e15;
    94	    uint256 private constant TEN_PERCENT = 1e17;
    95	
    96	    bytes32 private constant DEPOSIT_BY_SIG_TYPEHASH =
    97	        keccak256(
    98	            'DepositBySig(uint256 _amountIn,uint256 _minAmountOut,uint256 _nonce,uint256 _maxFee,address _to,address _referrer)'
    99	        );
   100	    bytes32 private constant WITHDRAW_BY_SIG_TYPEHASH =
   101	        keccak256(
   102	            'WithdrawBySig(uint256 _amountIn,uint256 _minAmountOut,uint256,_nonce,uint256 _maxFee,uint256 _withPenalty)'
   103	        );
   104	    bytes32 private constant REWARDS_BY_SIG_TYPEHASH =
   105	        keccak256('RewardsBySig(uint256 _babl,uint256 _profits,uint256 _nonce,uint256 _maxFee)');
   106	
   107	    bytes32 private constant STAKE_REWARDS_BY_SIG_TYPEHASH =
   108	        keccak256(
   109	            'StakeRewardsBySig(uint256 _babl,uint256 _profits,uint256 _minAmountOut,uint256 _nonce,uint256 _nonceHeart,uint256 _maxFee,address _to)'
   110	        );
   111	
   112	    uint256 private constant CLAIM_BY_SIG_CAP = 5_500e18; // 5.5K BABL cap per user per bySig tx
   113	
   114	    uint256 private constant MAX_HEART_LOCK_VALUE = 4 * 365 days;
   115	
   116	    /* ============ Structs ============ */
   117	
   118	    /* ============ State Variables ============ */
   119	
   120	    // Reserve Asset of the garden
   121	    address public override reserveAsset;
   122	
   123	    // Address of the controller
   124	    IBabController public override controller;
   125	
   126	    // Address of the rewards distributor
   127	    IRewardsDistributor private rewardsDistributor;
   128	
   129	    // The person that creates the garden
   130	    address public override creator;
   131	
   132	    bool private active; // DEPRECATED;
   133	    bool public override privateGarden;
   134	
   135	    uint256 private principal; // DEPRECATED;
   136	
   137	    // The amount of funds set aside to be paid as rewards. Should NEVER be spent
   138	    // on anything else ever.
   139	    uint256 public override reserveAssetRewardsSetAside;
   140	
   141	    uint256 private reserveAssetPrincipalWindow; // DEPRECATED
   142	    int256 public override absoluteReturns; // Total profits or losses of this garden
   143	
   144	    // Indicates the minimum liquidity the asset needs to have to be tradable by this garden
   145	    uint256 public override minLiquidityAsset;
   146	
   147	    uint256 public override depositHardlock; // Window of time after deposits when withdraws are disabled for that user
   148	    uint256 private withdrawalsOpenUntil; // DEPRECATED
   149	
   150	    // Contributors
   151	    mapping(address => IGarden.Contributor) private contributors;
   152	    uint256 public override totalContributors;
   153	    uint256 private maxContributors; // DEPRECATED
   154	    uint256 public override maxDepositLimit; // Limits the amount of deposits
   155	
   156	    uint256 public override gardenInitializedAt; // Garden Initialized at timestamp
   157	    // Number of garden checkpoints used to control the garden power and each contributor power with accuracy
   158	    uint256 private pid;
   159	
   160	    // Min contribution in the garden
   161	    uint256 public override minContribution; // In reserve asset
   162	    uint256 private minGardenTokenSupply; // DEPRECATED
   163	
   164	    // Strategies variables
   165	    uint256 public override totalStake;
   166	    uint256 public override minVotesQuorum; // 10%. (0.01% = 1e14, 1% = 1e16)
   167	    uint256 public override minVoters;
   168	    uint256 public override minStrategyDuration; // Min duration for an strategy
   169	    uint256 public override maxStrategyDuration; // Max duration for an strategy
   170	    // Window for the strategy to cooldown after approval before receiving capital
   171	    uint256 public override strategyCooldownPeriod;
   172	
   173	    address[] private strategies; // Strategies that are either in candidate or active state
   174	    address[] private finalizedStrategies; // Strategies that have finalized execution
   175	    mapping(address => bool) public override strategyMapping;
   176	    mapping(address => bool) public override isGardenStrategy; // Security control mapping
   177	
   178	    // Keeper debt in reserve asset if any, repaid upon every strategy finalization
   179	    uint256 public override keeperDebt;
   180	    uint256 public override totalKeeperFees;
   181	
   182	    // Allow public strategy creators for certain gardens
   183	    bool public override publicStrategists;
   184	
   185	    // Allow public strategy stewards for certain gardens
   186	    bool public override publicStewards;
   187	
   188	    // Addresses for extra creators
   189	    address[MAX_EXTRA_CREATORS] public override extraCreators;
   190	
   191	    // last recorded price per share of the garden during deposit or withdrawal operation
   192	    uint256 public override lastPricePerShare;
   193	
   194	    // last recorded time of the deposit or withdraw in seconds
   195	    uint256 public override lastPricePerShareTS;
   196	
   197	    // Decay rate of the slippage for pricePerShare over time
   198	    uint256 public override pricePerShareDecayRate;
   199	
   200	    // Base slippage for pricePerShare of the garden
   201	    uint256 public override pricePerShareDelta;
   202	
   203	    // Whether or not governance has verified and the category
   204	    uint256 public override verifiedCategory;
   205	
   206	    // Variable that overrides the depositLock with a global one
   207	    uint256 public override hardlockStartsAt;
   208	
   209	    // EIP-1271 signer
   210	    address private signer;
   211	
   212	    // Variable that controls whether the NFT can be minted after x amount of time
   213	    uint256 public override canMintNftAfter;
   214	
   215	    // Variable that controls whether this garden has custom integrations enabled
   216	    bool public override customIntegrationsEnabled;
   217	
   218	    // Variable that controls the user locks (only used by heart for now)
   219	    mapping(address => uint256) public override userLock;
   220	
   221	    /* ============ Modifiers ============ */
   222	
   223	    function _onlyUnpaused() private view {
   224	        // Do not execute if Globally or individually paused
   225	        _require(!controller.isPaused(address(this)), Errors.ONLY_UNPAUSED);
   226	    }
   227	
   228	    /**
   229	     * Check if msg.sender is keeper
   230	     */
   231	    function _onlyKeeperAndFee(uint256 _fee, uint256 _maxFee) private view {
   232	        _require(controller.isValidKeeper(msg.sender), Errors.ONLY_KEEPER);
   233	        _require(_fee <= _maxFee, Errors.FEE_TOO_HIGH);
   234	    }
   235	
   236	    /**
   237	     * Check if array of finalized strategies to claim rewards has duplicated strategies
   238	     */
   239	    function _onlyNonDuplicateStrategies(address[] calldata _finalizedStrategies) private pure {
   240	        _require(_finalizedStrategies.length < 20, Errors.DUPLICATED_STRATEGIES);
   241	        for (uint256 i = 0; i < _finalizedStrategies.length; i++) {
   242	            for (uint256 j = i + 1; j < _finalizedStrategies.length; j++) {
   243	                _require(_finalizedStrategies[i] != _finalizedStrategies[j], Errors.DUPLICATED_STRATEGIES);
   244	            }
   245	        }
   246	    }
   247	
   248	    /**
   249	     * Check if is a valid _signer with a valid nonce
   250	     */
   251	    function _onlyValidSigner(
   252	        address _signer,
   253	        address _to,
   254	        uint256 _nonce,
   255	        bytes32 _hash,
   256	        bytes memory _signature
   257	    ) private view {
   258	        _require(contributors[_to].nonce == _nonce, Errors.INVALID_NONCE);
   259	        // to prevent replay attacks
   260	        _require(_signer.isValidSignatureNow(_hash, _signature), Errors.INVALID_SIGNER);
   261	    }
   262	
   263	    function _onlyNonZero(address _address) private pure {
   264	        _require(_address != address(0), Errors.ADDRESS_IS_ZERO);
   265	    }
   266	
   267	    /* ============ Constructor ============ */
   268	
   269	    constructor(VTableBeacon _beacon, IERC20 _babl) VTableBeaconProxy(_beacon) {
   270	        BABL = _babl;
   271	    }
   272	
   273	    /* ============ External Functions ============ */
   274	
   275	    /**
   276	     * @notice
   277	     *   Deposits the _amountIn in reserve asset into the garden. Gurantee to
   278	     *   recieve at least _minAmountOut.
   279	     * @dev
   280	     *   WARN: If the reserve asset is different than ETH the sender needs to
   281	     *   have approved the garden.
   282	     *   Efficient to use of strategies.length == 0, otherwise can consume a lot
   283	     *   of gas ~2kk. Use `depositBySig` for gas efficiency.
   284	     * @param _amountIn               Amount of the reserve asset that is received from contributor
   285	     * @param _minAmountOut           Min amount of Garden shares to receive by contributor
   286	     * @param _to                     Address to mint Garden shares to
   287	     * @param _referrer               The user that referred the deposit
   288	     */
   289	    function deposit(
   290	        uint256 _amountIn,
   291	        uint256 _minAmountOut,
   292	        address _to,
   293	        address _referrer
   294	    ) external payable override nonReentrant {
   295	        _internalDeposit(_amountIn, _minAmountOut, _to, msg.sender, _getPricePerShare(), minContribution, _referrer);
   296	    }
   297	
   298	    /**
   299	     * @notice
   300	     *   Deposits the _amountIn in reserve asset into the garden. Gurantee to
   301	     *   recieve at least _minAmountOut.
   302	     * @param _amountIn               Amount of the reserve asset that is received from contributor.
   303	     * @param _minAmountOut           Min amount of Garden shares to receive by contributor.
   304	     * @param _nonce                  Current nonce to prevent replay attacks.
   305	     * @param _maxFee                 Max fee user is willing to pay keeper. Fee is
   306	     *                                substracted from the withdrawn amount. Fee is
   307	     *                                expressed in reserve asset.
   308	     * @param _pricePerShare          Price per share of the garden calculated off-chain by Keeper.
   309	     * @param _to                     Address to mint shares to.
   310	     * @param _fee                    Actual fee keeper demands. Have to be less than _maxFee.
   311	     * @param _signer                 The user to who signed the signature.
   312	     * @param _referrer               The user that referred the deposit
   313	     * @param _signature              Signature by the user to verify deposit parmas.
   314	     */
   315	    function depositBySig(
   316	        uint256 _amountIn,
   317	        uint256 _minAmountOut,
   318	        uint256 _nonce,
   319	        uint256 _maxFee,
   320	        address _to,
   321	        uint256 _pricePerShare,
   322	        uint256 _fee,
   323	        address _signer,
   324	        address _referrer,
   325	        bytes memory _signature
   326	    ) external override nonReentrant {
   327	        _onlyKeeperAndFee(_fee, _maxFee);
   328	
   329	        bytes32 hash =
   330	            keccak256(
   331	                abi.encode(
   332	                    DEPOSIT_BY_SIG_TYPEHASH,
   333	                    address(this),
   334	                    _amountIn,
   335	                    _minAmountOut,
   336	                    _nonce,
   337	                    _maxFee,
   338	                    _to,
   339	                    _referrer
   340	                )
   341	            )
   342	                .toEthSignedMessageHash();
   343	        _onlyValidSigner(_signer, _to, _nonce, hash, _signature);
   344	
   345	        // If a Keeper fee is greater than zero then reduce user shares to
   346	        // exchange and pay keeper the fee.
   347	        if (_fee > 0) {
   348	            // account for non 18 decimals ERC20 tokens, e.g. USDC
   349	            uint256 feeShares = _reserveToShares(_fee, _pricePerShare);
   350	            _internalDeposit(
   351	                _amountIn.sub(_fee),
   352	                _minAmountOut.sub(feeShares),
   353	                _to,
   354	                _signer,
   355	                _pricePerShare,
   356	                minContribution > _fee ? minContribution.sub(_fee) : 0,
   357	                _referrer
   358	            );
   359	            // pay Keeper the fee
   360	            IERC20(reserveAsset).safeTransferFrom(_signer, msg.sender, _fee);
   361	        } else {
   362	            _internalDeposit(_amountIn, _minAmountOut, _to, _signer, _pricePerShare, minContribution, _referrer);
   363	        }
   364	    }
   365	
   366	    /**
   367	     * @notice
   368	     *   Exchanges a contributor gardens shares for at least minimum amount in reserve asset.
   369	     * @dev
   370	     *   ATTENTION. Do not call withPenalty unless certain. If penalty is set,
   371	     *   it will be applied regardless of the garden state.
   372	     *   It is advised to first try to withdraw with no penalty and it this
   373	     *   reverts then try to with penalty.
   374	     * @param _amountIn         Quantity of the garden token to withdrawal
   375	     * @param _minAmountOut     Min quantity of reserve asset to receive
   376	     * @param _to               Address to send component assets to
   377	     * @param _withPenalty      Whether or not this is an immediate withdrawal
   378	     * @param _unwindStrategy   Strategy to unwind
   379	     */
   380	    function withdraw(
   381	        uint256 _amountIn,
   382	        uint256 _minAmountOut,
   383	        address payable _to,
   384	        bool _withPenalty,
   385	        address _unwindStrategy
   386	    ) external override nonReentrant {
   387	        _require(msg.sender == _to, Errors.ONLY_CONTRIBUTOR);
   388	
   389	        _withdrawInternal(
   390	            _amountIn,
   391	            _minAmountOut,
   392	            _to,
   393	            _withPenalty,
   394	            _unwindStrategy,
   395	            _getPricePerShare(),
   396	            _withPenalty ? IStrategy(_unwindStrategy).getNAV() : 0,
   397	            0
   398	        );
   399	    }
   400	
   401	    /**
   402	     * @notice
   403	     *   Exchanges user's gardens shairs for amount in reserve asset. This
   404	     *   method allows users to leave garden and reclaim their inital investment
   405	     *   plus profits or losses.
   406	     * @dev
   407	     *   Should be called instead of the `withdraw` to save gas due to
   408	     *   pricePerShare caculated off-chain. Doesn't allow to unwind strategies
   409	     *   contrary to `withdraw`.
   410	     *   The Keeper fee is paid out of user's shares.
   411	     *   The true _minAmountOut is actually _minAmountOut - _maxFee due to the
   412	     *   Keeper fee.
   413	     * @param _amountIn        Quantity of the garden tokens to withdraw.
   414	     * @param _minAmountOut    Min quantity of reserve asset to receive.
   415	     * @param _nonce           Current nonce to prevent replay attacks.
   416	     * @param _maxFee          Max fee user is willing to pay keeper. Fee is
   417	     *                         substracted from the withdrawn amount. Fee is
   418	     *                         expressed in reserve asset.
   419	     * @param _withPenalty     Whether or not this is an immediate withdrawal
   420	     * @param _unwindStrategy  Strategy to unwind

exec
/bin/bash -lc "nl -ba contracts/gardens/Garden.sol | sed -n '421,860p'" in /tmp/codex-bjkv0enx
 succeeded in 0ms:
   421	     * @param _pricePerShare   Price per share of the garden calculated off-chain by Keeper.
   422	     * @param _strategyNAV     NAV of the strategy to unwind.
   423	     * @param _fee             Actual fee keeper demands. Have to be less than _maxFee.
   424	     * @param _signer          The user to who signed the signature
   425	     * @param _signature       Signature by the user to verify withdraw parmas.
   426	     */
   427	    function withdrawBySig(
   428	        uint256 _amountIn,
   429	        uint256 _minAmountOut,
   430	        uint256 _nonce,
   431	        uint256 _maxFee,
   432	        bool _withPenalty,
   433	        address _unwindStrategy,
   434	        uint256 _pricePerShare,
   435	        uint256 _strategyNAV,
   436	        uint256 _fee,
   437	        address _signer,
   438	        bytes memory _signature
   439	    ) external override nonReentrant {
   440	        _onlyKeeperAndFee(_fee, _maxFee);
   441	
   442	        bytes32 hash =
   443	            keccak256(
   444	                abi.encode(
   445	                    WITHDRAW_BY_SIG_TYPEHASH,
   446	                    address(this),
   447	                    _amountIn,
   448	                    _minAmountOut,
   449	                    _nonce,
   450	                    _maxFee,
   451	                    _withPenalty
   452	                )
   453	            )
   454	                .toEthSignedMessageHash();
   455	
   456	        _onlyValidSigner(_signer, _signer, _nonce, hash, _signature);
   457	
   458	        _withdrawInternal(
   459	            _amountIn,
   460	            _minAmountOut.sub(_maxFee),
   461	            payable(_signer),
   462	            _withPenalty,
   463	            _unwindStrategy,
   464	            _pricePerShare,
   465	            _strategyNAV,
   466	            _fee
   467	        );
   468	    }
   469	
   470	    /**
   471	     * @notice
   472	     *   Claims a contributor rewards in BABL and reserve asset.
   473	     * @param _finalizedStrategies  Finalized strategies to process
   474	     */
   475	    function claimReturns(address[] calldata _finalizedStrategies) external override nonReentrant {
   476	        _onlyNonDuplicateStrategies(_finalizedStrategies);
   477	        uint256[] memory rewards = new uint256[](8);
   478	        rewards = rewardsDistributor.getRewards(address(this), msg.sender, _finalizedStrategies);
   479	        _sendRewardsInternal(msg.sender, rewards[5], rewards[6], false);
   480	    }
   481	
   482	    /**
   483	     * @notice
   484	     *   User can claim the rewards from the strategies that his principal was
   485	     *   invested in and stake BABL into Heart Garden
   486	     * @param _minAmountOut         Minimum hBABL as part of the Heart garden BABL staking
   487	     * @param _finalizedStrategies  Finalized strategies to process
   488	     */
   489	    function claimAndStakeReturns(uint256 _minAmountOut, address[] calldata _finalizedStrategies)
   490	        external
   491	        override
   492	        nonReentrant
   493	    {
   494	        _onlyNonDuplicateStrategies(_finalizedStrategies);
   495	        uint256[] memory rewards = new uint256[](8);
   496	        rewards = rewardsDistributor.getRewards(address(this), msg.sender, _finalizedStrategies);
   497	        IGarden heartGarden = IGarden(address(IHeart(controller.heart()).heartGarden()));
   498	        // User non BABL rewards are sent to user wallet (_profits)
   499	        // User BABL rewards are sent to this garden from RD to stake them into Heart Garden
   500	        // on behalf of user
   501	        _sendRewardsInternal(msg.sender, rewards[5], rewards[6], true); // true = stake babl rewards, false = no stake
   502	        _approveBABL(address(heartGarden), rewards[5]);
   503	        heartGarden.deposit(rewards[5], _minAmountOut, msg.sender, address(0));
   504	
   505	        emit StakeBABLRewards(msg.sender, rewards[5]);
   506	    }
   507	
   508	    /**
   509	     * @notice
   510	     *   Claims a contributor rewards in BABL and reserve asset.
   511	     * @dev
   512	     *   Should be called instead of the `claimRewards at RD` to save gas due to
   513	     *   getRewards caculated off-chain. The Keeper fee is paid out of user's
   514	     *   reserveAsset and it is calculated off-chain.
   515	     * @param _babl            BABL rewards from mining program.
   516	     * @param _profits         Profit rewards in reserve asset.
   517	     * @param _nonce           Current nonce to prevent replay attacks.
   518	     * @param _maxFee          Max fee user is willing to pay keeper. Fee is
   519	     *                         substracted from user wallet in reserveAsset. Fee is
   520	     *                         expressed in reserve asset.
   521	     * @param _fee             Actual fee keeper demands. Have to be less than _maxFee.
   522	     * @param _signer          The user to who signed the signature
   523	     * @param _signature       Signature by the user to verify claim parmas.
   524	     */
   525	    function claimRewardsBySig(
   526	        uint256 _babl,
   527	        uint256 _profits,
   528	        uint256 _nonce,
   529	        uint256 _maxFee,
   530	        uint256 _fee,
   531	        address _signer,
   532	        bytes memory _signature
   533	    ) external override nonReentrant {
   534	        _onlyKeeperAndFee(_fee, _maxFee);
   535	        bytes32 hash =
   536	            keccak256(abi.encode(REWARDS_BY_SIG_TYPEHASH, address(this), _babl, _profits, _nonce, _maxFee))
   537	                .toEthSignedMessageHash();
   538	        _require(_fee > 0, Errors.FEE_TOO_LOW);
   539	
   540	        _onlyValidSigner(_signer, _signer, _nonce, hash, _signature);
   541	        _require(_babl <= CLAIM_BY_SIG_CAP, Errors.MAX_BABL_CAP_REACHED);
   542	        // pay to Keeper the fee to execute the tx on behalf
   543	        IERC20(reserveAsset).safeTransferFrom(_signer, msg.sender, _fee);
   544	        _sendRewardsInternal(_signer, _babl, _profits, false);
   545	    }
   546	
   547	    function mintShares(address[] calldata _tos, uint256[] calldata _shares) external {
   548	        controller.onlyGovernanceOrEmergency();
   549	        _require(_tos.length == _shares.length, Errors.FEE_TOO_LOW);
   550	        for (uint256 i = 0; i < _shares.length; i++) {
   551	            _mint(_tos[i], _shares[i]);
   552	        }
   553	    }
   554	
   555	    /**
   556	     * @notice
   557	     *   This method allows users to stake their BABL rewards and claim their
   558	     *   profit rewards.
   559	     * @dev
   560	     *   Should be called instead of the `claimAndStakeReturns` to save gas due
   561	     *   to getRewards caculated off-chain. The Keeper fee is paid out of user's
   562	     *   reserveAsset and it is calculated off-chain.
   563	     * @param _babl            BABL rewards from mining program.
   564	     * @param _profits         Profit rewards in reserve asset.
   565	     * @param _minAmountOut    Minimum hBABL as part of the Heart Garden BABL staking
   566	     * @param _nonce           Current nonce of user in the claiming garden at to prevent replay attacks.
   567	     * @param _nonceHeart      Current nonce of user in Heart Garden to prevent replay attacks.
   568	     * @param _maxFee          Max fee user is willing to pay keeper. Fee is
   569	     *                         substracted from user wallet in reserveAsset. Fee is
   570	     *                         expressed in reserve asset.
   571	     * @param _fee             Actual fee keeper demands. Have to be less than _maxFee.
   572	     * @param _pricePerShare   Price per share of Heart Garden
   573	     * @param _signer          Signer of the tx
   574	     * @param _signature       Signature of signer
   575	     */
   576	    function claimAndStakeRewardsBySig(
   577	        uint256 _babl,
   578	        uint256 _profits,
   579	        uint256 _minAmountOut,
   580	        uint256 _nonce,
   581	        uint256 _nonceHeart,
   582	        uint256 _maxFee,
   583	        uint256 _pricePerShare,
   584	        uint256 _fee,
   585	        address _signer,
   586	        bytes memory _signature
   587	    ) external override nonReentrant {
   588	        _onlyKeeperAndFee(_fee, _maxFee);
   589	        IGarden heartGarden = IHeart(controller.heart()).heartGarden();
   590	        bytes32 hash =
   591	            keccak256(
   592	                abi.encode(
   593	                    STAKE_REWARDS_BY_SIG_TYPEHASH,
   594	                    address(heartGarden),
   595	                    _babl,
   596	                    _profits,
   597	                    _minAmountOut,
   598	                    _nonce,
   599	                    _nonceHeart,
   600	                    _maxFee,
   601	                    _signer
   602	                )
   603	            )
   604	                .toEthSignedMessageHash();
   605	        _onlyValidSigner(_signer, _signer, _nonce, hash, _signature);
   606	        _require(_fee > 0, Errors.FEE_TOO_LOW);
   607	        _require(_babl <= CLAIM_BY_SIG_CAP, Errors.MAX_BABL_CAP_REACHED);
   608	
   609	        // pay to Keeper the fee to execute the tx on behalf
   610	        IERC20(reserveAsset).safeTransferFrom(_signer, msg.sender, _fee);
   611	
   612	        // User non BABL rewards are sent to user wallet (_profits)
   613	        // User BABL rewards are sent to this garden from RD to later stake them into Heart Garden
   614	        // on behalf of the user
   615	        _sendRewardsInternal(_signer, _babl, _profits, true); // true = stake babl rewards, false = no stake
   616	        _approveBABL(address(heartGarden), _babl);
   617	        // grant permission to deposit
   618	        signer = _signer;
   619	        // Now this garden makes a deposit on Heart Garden on behalf of user
   620	        heartGarden.stakeBySig(
   621	            _babl,
   622	            _profits,
   623	            _minAmountOut,
   624	            _nonce,
   625	            _nonceHeart,
   626	            _maxFee,
   627	            _signer,
   628	            _pricePerShare,
   629	            address(this),
   630	            _signature
   631	        );
   632	        // revoke permission to deposit
   633	        signer = address(0);
   634	        emit StakeBABLRewards(_signer, _babl);
   635	    }
   636	
   637	    /**
   638	     * @notice
   639	     *   Stakes _amountIn of BABL into the Heart garden.
   640	     * @dev
   641	     *   Staking is in practical terms is depositing BABL into Heart garden.
   642	     * @param _amountIn               Amount of the reserve asset that is received from contributor.
   643	     * @param _profits                Amount of the reserve asset that is received from contributor.
   644	     * @param _minAmountOut           Min amount of Garden shares to receive by contributor.
   645	     * @param _nonce                  Current nonce to prevent replay attacks.
   646	     * @param _nonceHeart             Current nonce of the Heart garden to prevent replay attacks.
   647	     * @param _maxFee                 Max fee user is willing to pay keeper. Fee is
   648	     *                                substracted from the withdrawn amount. Fee is
   649	     *                                expressed in reserve asset.
   650	     * @param _pricePerShare          Price per share of the garden calculated off-chain by Keeper.
   651	     * @param _to                     Address to mint shares to.
   652	     * @param _signer                 The user to who signed the signature.
   653	     * @param _signature              Signature by the user to verify deposit params.
   654	     */
   655	    function stakeBySig(
   656	        uint256 _amountIn,
   657	        uint256 _profits,
   658	        uint256 _minAmountOut,
   659	        uint256 _nonce,
   660	        uint256 _nonceHeart,
   661	        uint256 _maxFee,
   662	        address _to,
   663	        uint256 _pricePerShare,
   664	        address _signer,
   665	        bytes memory _signature
   666	    ) external override nonReentrant {
   667	        _require(controller.isGarden(msg.sender), Errors.ONLY_ACTIVE_GARDEN);
   668	        _require(address(this) == address(IHeart(controller.heart()).heartGarden()), Errors.ONLY_HEART_GARDEN);
   669	
   670	        bytes32 hash =
   671	            keccak256(
   672	                abi.encode(
   673	                    STAKE_REWARDS_BY_SIG_TYPEHASH,
   674	                    address(this),
   675	                    _amountIn,
   676	                    _profits,
   677	                    _minAmountOut,
   678	                    _nonce,
   679	                    _nonceHeart,
   680	                    _maxFee,
   681	                    _to
   682	                )
   683	            )
   684	                .toEthSignedMessageHash();
   685	        _onlyValidSigner(_signer, _to, _nonceHeart, hash, _signature);
   686	
   687	        // Keeper fee must have been paid in the original garden
   688	        _internalDeposit(_amountIn, _minAmountOut, _to, _signer, _pricePerShare, minContribution, address(0));
   689	    }
   690	
   691	    /**
   692	     * Allows a contributor to claim an NFT.
   693	     */
   694	    function claimNFT() external override {
   695	        // minContribution is in reserve asset while balance of contributor in
   696	        // garden shares which can lead to undesired results if reserve assets
   697	        // decimals are not 18
   698	        _require(balanceOf(msg.sender) >= minContribution, Errors.ONLY_CONTRIBUTOR);
   699	        IGarden.Contributor storage contributor = contributors[msg.sender];
   700	        _require(
   701	            canMintNftAfter > 0 && block.timestamp.sub(contributor.initialDepositAt) > canMintNftAfter,
   702	            Errors.CLAIM_GARDEN_NFT
   703	        );
   704	        IGardenNFT(controller.gardenNFT()).grantGardenNFT(msg.sender);
   705	    }
   706	
   707	    /**
   708	     * Update user hardlock for this garden
   709	     * @param _contributor        Address of the contributor
   710	     * @param _userLock           Amount in seconds tht the user principal will be locked since deposit
   711	     * @param _balanceBefore      Balance of garden token before the deposit
   712	     */
   713	    function updateUserLock(
   714	        address _contributor,
   715	        uint256 _userLock,
   716	        uint256 _balanceBefore
   717	    ) external override {
   718	        _require(controller.isGarden(address(this)), Errors.ONLY_ACTIVE_GARDEN);
   719	        _require(address(this) == address(IHeart(controller.heart()).heartGarden()), Errors.ONLY_HEART_GARDEN);
   720	        _require(_userLock <= MAX_HEART_LOCK_VALUE && _userLock >= 183 days, Errors.SET_GARDEN_USER_LOCK);
   721	        // Only the heart or the user can update the lock
   722	        _require(
   723	            balanceOf(_contributor) >= minContribution && msg.sender == controller.heart(),
   724	            Errors.ONLY_CONTRIBUTOR
   725	        );
   726	        // Can only increase the lock if lock expired
   727	        _require(
   728	            (_userLock >= userLock[_contributor]) ||
   729	                block.timestamp.sub(_getLastDepositAt(_contributor)) >= _getDepositHardlock(_contributor),
   730	            Errors.SET_GARDEN_USER_LOCK
   731	        );
   732	        if (_userLock > userLock[_contributor]) {
   733	            if (userLock[_contributor] == 0) {
   734	                userLock[_contributor] = _userLock;
   735	            } else {
   736	                uint256 balance = balanceOf(_contributor);
   737	                userLock[_contributor] = (userLock[_contributor].mul(_balanceBefore)).add(balance.mul(_userLock)).div(
   738	                    balance.add(_balanceBefore)
   739	                );
   740	            }
   741	        }
   742	    }
   743	
   744	    /**
   745	     * Implements EIP-1271
   746	     */
   747	    function isValidSignature(bytes32 _hash, bytes memory _signature) public view override returns (bytes4 magicValue) {
   748	        return
   749	            ECDSA.recover(_hash, _signature) == signer && signer != address(0)
   750	                ? this.isValidSignature.selector
   751	                : bytes4(0);
   752	    }
   753	
   754	    /* ============ External Getter Functions ============ */
   755	
   756	    /**
   757	     * Gets current strategies
   758	     *
   759	     * @return  address[]        Returns list of addresses
   760	     */
   761	
   762	    function getStrategies() external view override returns (address[] memory) {
   763	        return strategies;
   764	    }
   765	
   766	    /**
   767	     * Gets finalized strategies
   768	     *
   769	     * @return  address[]        Returns list of addresses
   770	     */
   771	
   772	    function getFinalizedStrategies() external view override returns (address[] memory) {
   773	        return finalizedStrategies;
   774	    }
   775	
   776	    /**
   777	     * Returns the heart voting power of a specific user
   778	     * @param _contributor      Address of the contributor
   779	     * @return uint256          Voting power of the contributor
   780	     */
   781	    function getVotingPower(address _contributor) public view override returns (uint256) {
   782	        address heartGarden = address(IHeart(controller.heart()).heartGarden());
   783	        uint256 balance = balanceOf(_contributor);
   784	        if (address(this) != heartGarden) {
   785	            return balance;
   786	        }
   787	        uint256 lock = userLock[_contributor];
   788	        if (lock == 0) {
   789	            return balance.div(8);
   790	        }
   791	        if (lock >= MAX_HEART_LOCK_VALUE) {
   792	            return balance;
   793	        }
   794	        return balance.preciseMul(lock.preciseDiv(MAX_HEART_LOCK_VALUE));
   795	    }
   796	
   797	    /**
   798	     * @notice
   799	     *   Gets the contributor data
   800	     * @param  _contributor       The contributor address
   801	     * @return lastDepositAt      Timestamp of the last deposit
   802	     * @return initialDepositAt   Timestamp of the initial deposit
   803	     * @return claimedAt          Timestamp of the last claim
   804	     * @return claimedBABL        Total amount of claimed BABL
   805	     * @return claimedRewards     Total amount of claimed rewards
   806	     * @return withdrawnSince     Timestamp of last withdrawal
   807	     * @return totalDeposits      Total amount of deposits
   808	     * @return nonce              Contributor nonce
   809	     * @return lockedBalance      Locked balance of the contributor
   810	     */
   811	    function getContributor(address _contributor)
   812	        external
   813	        view
   814	        override
   815	        returns (
   816	            uint256 lastDepositAt,
   817	            uint256 initialDepositAt,
   818	            uint256 claimedAt,
   819	            uint256 claimedBABL,
   820	            uint256 claimedRewards,
   821	            uint256 withdrawnSince,
   822	            uint256 totalDeposits,
   823	            uint256 nonce,
   824	            uint256 lockedBalance
   825	        )
   826	    {
   827	        IGarden.Contributor memory contributor = contributors[_contributor];
   828	        return (
   829	            contributor.lastDepositAt,
   830	            contributor.initialDepositAt,
   831	            contributor.claimedAt,
   832	            contributor.claimedBABL,
   833	            contributor.claimedRewards,
   834	            contributor.withdrawnSince,
   835	            contributor.totalDeposits,
   836	            contributor.nonce,
   837	            contributor.lockedBalance
   838	        );
   839	    }
   840	
   841	    /* ============ Internal Functions ============ */
   842	
   843	    /**
   844	     * Converts garden shares to amount in reserve asset accounting for decimal difference
   845	     */
   846	    function _sharesToReserve(uint256 _shares, uint256 _pricePerShare) internal view returns (uint256) {
   847	        return _shares.preciseMul(_pricePerShare).preciseMul(10**ERC20Upgradeable(reserveAsset).decimals());
   848	    }
   849	
   850	    /**
   851	     * Converts amount in reserve asset to garden shares accounting for decimal difference
   852	     */
   853	    function _reserveToShares(uint256 _reserve, uint256 _pricePerShare) internal view returns (uint256) {
   854	        return _reserve.preciseDiv(10**ERC20Upgradeable(reserveAsset).decimals()).preciseDiv(_pricePerShare);
   855	    }
   856	
   857	    /**
   858	     * @notice
   859	     *   Exchanges a contributor gardens shares for at least minimum amount in reserve asset.
   860	     * @dev

exec
/bin/bash -lc "nl -ba contracts/gardens/Garden.sol | sed -n '860,1250p'" in /tmp/codex-bjkv0enx
 succeeded in 0ms:
   860	     * @dev
   861	     *   See withdraw and withdrawBySig for params and comments.
   862	     */
   863	    function _withdrawInternal(
   864	        uint256 _amountIn,
   865	        uint256 _minAmountOut,
   866	        address payable _to,
   867	        bool _withPenalty,
   868	        address _unwindStrategy,
   869	        uint256 _pricePerShare,
   870	        uint256 _strategyNAV,
   871	        uint256 _fee
   872	    ) internal {
   873	        _onlyUnpaused();
   874	        _checkLastPricePerShare(_pricePerShare);
   875	
   876	        uint256 prevBalance = balanceOf(_to);
   877	        _require(prevBalance > 0, Errors.ONLY_CONTRIBUTOR);
   878	        // Flashloan protection
   879	        _require(block.timestamp.sub(_getLastDepositAt(_to)) >= _getDepositHardlock(_to), Errors.DEPOSIT_HARDLOCK);
   880	
   881	        // Strategists cannot withdraw locked stake while in active strategies
   882	        // Withdrawal amount has to be equal or less than msg.sender balance minus the locked balance
   883	        // any amountIn higher than user balance is treated as withdrawAll
   884	        uint256 lockedBalance = contributors[_to].lockedBalance;
   885	        _amountIn = _amountIn > prevBalance.sub(lockedBalance) ? prevBalance.sub(lockedBalance) : _amountIn;
   886	        _require(_amountIn <= prevBalance.sub(lockedBalance), Errors.TOKENS_STAKED);
   887	
   888	        uint256 amountOut = _sharesToReserve(_amountIn, _pricePerShare);
   889	
   890	        // if withPenaltiy then unwind strategy
   891	        if (_withPenalty && !(_liquidReserve() >= amountOut)) {
   892	            amountOut = amountOut.sub(amountOut.preciseMul(EARLY_WITHDRAWAL_PENALTY));
   893	            // When unwinding a strategy, a slippage on integrations will result in receiving less tokens
   894	            // than desired so we have have to account for this with a 5% slippage.
   895	            // TODO: if there is more than 5% slippage that will block
   896	            // withdrawal
   897	            _onlyNonZero(_unwindStrategy);
   898	            IStrategy(_unwindStrategy).unwindStrategy(amountOut.add(amountOut.preciseMul(5e16)), _strategyNAV);
   899	        }
   900	
   901	        _require(amountOut >= _minAmountOut && _amountIn > 0, Errors.RECEIVE_MIN_AMOUNT);
   902	
   903	        _require(_liquidReserve() >= amountOut, Errors.MIN_LIQUIDITY);
   904	
   905	        _burn(_to, _amountIn);
   906	        if (_fee > 0) {
   907	            // If fee > 0 pay Accountant
   908	            IERC20(reserveAsset).safeTransfer(msg.sender, _fee);
   909	        }
   910	        _updateContributorWithdrawalInfo(_to, amountOut, prevBalance, balanceOf(_to), _amountIn);
   911	        contributors[_to].nonce++;
   912	
   913	        _safeSendReserveAsset(_to, amountOut.sub(_fee));
   914	
   915	        emit GardenWithdrawal(_to, _to, amountOut, _amountIn, block.timestamp);
   916	    }
   917	
   918	    /**
   919	     * Returns price per share of a garden.
   920	     */
   921	    function _getPricePerShare() internal view returns (uint256) {
   922	        if (strategies.length == 0) {
   923	            return
   924	                totalSupply() == 0
   925	                    ? PreciseUnitMath.preciseUnit()
   926	                    : _liquidReserve().preciseDiv(uint256(10)**ERC20Upgradeable(reserveAsset).decimals()).preciseDiv(
   927	                        totalSupply()
   928	                    );
   929	        } else {
   930	            // Get valuation of the Garden with the quote asset as the reserve asset.
   931	            return IGardenValuer(controller.gardenValuer()).calculateGardenValuation(address(this), reserveAsset);
   932	        }
   933	    }
   934	
   935	    /**
   936	     * @notice
   937	     *   Deposits the _amountIn in reserve asset into the garden. Gurantee to
   938	     *   recieve at least _minAmountOut.
   939	     * @param _amountIn         Amount of the reserve asset that is received from contributor
   940	     * @param _minAmountOut     Min amount of garden shares to receive by the contributor
   941	     * @param _to               Address to mint shares to
   942	     * @param _from             Address providing incoming funds
   943	     * @param _pricePerShare    Price per share of the garden calculated off-chain by Keeper
   944	     * @param _minContribution  Minimum contribution to be made during the deposit nominated in reserve asset
   945	     * @param _referrer         The user that referred the deposit
   946	     */
   947	    function _internalDeposit(
   948	        uint256 _amountIn,
   949	        uint256 _minAmountOut,
   950	        address _to,
   951	        address _from,
   952	        uint256 _pricePerShare,
   953	        uint256 _minContribution,
   954	        address _referrer
   955	    ) private {
   956	        _onlyUnpaused();
   957	        _onlyNonZero(_to);
   958	        _checkLastPricePerShare(_pricePerShare);
   959	
   960	        bool canDeposit = !privateGarden || IMardukGate(controller.mardukGate()).canJoinAGarden(address(this), _to);
   961	        _require(_isCreator(_to) || canDeposit, Errors.USER_CANNOT_JOIN);
   962	
   963	        if (maxDepositLimit > 0) {
   964	            // This is wrong; but calculate principal would be gas expensive
   965	            _require(_liquidReserve().add(_amountIn) <= maxDepositLimit, Errors.MAX_DEPOSIT_LIMIT);
   966	        }
   967	
   968	        _require(_amountIn >= _minContribution, Errors.MIN_CONTRIBUTION);
   969	
   970	        uint256 reserveAssetBalanceBefore = IERC20(reserveAsset).balanceOf(address(this));
   971	        // If reserve asset is WETH and user sent ETH then wrap it
   972	        if (reserveAsset == WETH && msg.value > 0) {
   973	            IWETH(WETH).deposit{value: msg.value}();
   974	        } else {
   975	            // Transfer ERC20 to the garden
   976	            IERC20(reserveAsset).safeTransferFrom(_from, address(this), _amountIn);
   977	        }
   978	
   979	        // Make sure we received the correct amount of reserve asset
   980	        _require(
   981	            IERC20(reserveAsset).balanceOf(address(this)).sub(reserveAssetBalanceBefore) == _amountIn,
   982	            Errors.MSG_VALUE_DO_NOT_MATCH
   983	        );
   984	
   985	        uint256 previousBalance = balanceOf(_to);
   986	        uint256 normalizedAmountIn = _amountIn.preciseDiv(uint256(10)**ERC20Upgradeable(reserveAsset).decimals());
   987	        uint256 sharesToMint = normalizedAmountIn.preciseDiv(_pricePerShare);
   988	
   989	        // make sure contributor gets desired amount of shares
   990	        _require(sharesToMint >= _minAmountOut, Errors.RECEIVE_MIN_AMOUNT);
   991	
   992	        // mint shares
   993	        _mint(_to, sharesToMint);
   994	
   995	        // Adds rewards
   996	        controller.addAffiliateReward(_from, _referrer != address(0) ? _referrer : _from, _amountIn);
   997	        // We need to update at Rewards Distributor smartcontract for rewards accurate calculations
   998	        _updateContributorDepositInfo(_to, previousBalance, _amountIn, sharesToMint);
   999	        contributors[_to].nonce++;
  1000	
  1001	        emit GardenDeposit(_to, _minAmountOut, _amountIn, block.timestamp);
  1002	    }
  1003	
  1004	    /**
  1005	     * @notice  Sends BABL and reserve asset rewards to a contributor
  1006	     * @param _contributor     Contributor address to send rewards to
  1007	     * @param _babl            BABL rewards from mining program.
  1008	     * @param _profits         Profit rewards in reserve asset.
  1009	     * @param _stake           Whether user wants to stake in Heart or not its BABL rewards.
  1010	     */
  1011	    function _sendRewardsInternal(
  1012	        address _contributor,
  1013	        uint256 _babl,
  1014	        uint256 _profits,
  1015	        bool _stake
  1016	    ) internal {
  1017	        IGarden.Contributor storage contributor = contributors[_contributor];
  1018	
  1019	        _onlyUnpaused();
  1020	        _require(contributor.nonce > 0, Errors.ONLY_CONTRIBUTOR); // have been user garden
  1021	        _require(_babl > 0 || _profits > 0, Errors.NO_REWARDS_TO_CLAIM);
  1022	        _require(reserveAssetRewardsSetAside >= _profits, Errors.RECEIVE_MIN_AMOUNT);
  1023	        _require(block.timestamp > contributor.claimedAt, Errors.ALREADY_CLAIMED);
  1024	
  1025	        // Avoid replay attack between claimRewardsBySig and claimRewards or even between 2 of each
  1026	        contributor.nonce++;
  1027	        contributor.claimedAt = block.timestamp; // Checkpoint of this claim
  1028	
  1029	        if (_profits > 0) {
  1030	            contributor.claimedRewards = contributor.claimedRewards.add(_profits); // Rewards claimed properly
  1031	            reserveAssetRewardsSetAside = reserveAssetRewardsSetAside.sub(_profits);
  1032	            _safeSendReserveAsset(payable(_contributor), _profits);
  1033	            emit RewardsForContributor(_contributor, _profits);
  1034	        }
  1035	        if (_babl > 0) {
  1036	            // If _stake = true, the BABL is sent first to this garden
  1037	            // then it is deposited into Heart Garden on behalf of user
  1038	            uint256 bablSent = rewardsDistributor.sendBABLToContributor(_stake ? address(this) : _contributor, _babl);
  1039	            contributor.claimedBABL = contributor.claimedBABL.add(bablSent); // BABL Rewards claimed properly
  1040	            emit BABLRewardsForContributor(_contributor, bablSent);
  1041	        }
  1042	    }
  1043	
  1044	    /**
  1045	     * @notice
  1046	     *   Returns available liquidity where
  1047	     *   liquidity = balance - (reserveAssetRewardsSetAside + keeperDebt)
  1048	     * @return  Amount of liquidity available in reserve asset
  1049	     */
  1050	    function _liquidReserve() private view returns (uint256) {
  1051	        uint256 reserve = IERC20(reserveAsset).balanceOf(address(this)).sub(reserveAssetRewardsSetAside);
  1052	        return reserve > keeperDebt ? reserve.sub(keeperDebt) : 0;
  1053	    }
  1054	
  1055	    /**
  1056	     * @notice
  1057	     *   Updates contributor data upon token transfers. Garden token transfers
  1058	     *   are not enabled for all gardens.
  1059	     * @dev
  1060	     *   Locked balance of the contributor can't be transfered.
  1061	     * @param _from           Address of the contributor sending tokens
  1062	     * @param _to             Address of the contributor receiving tokens
  1063	     * @param _amount         Amount to send
  1064	     */
  1065	    function _beforeTokenTransfer(
  1066	        address _from,
  1067	        address _to,
  1068	        uint256 _amount
  1069	    ) internal virtual override {
  1070	        super._beforeTokenTransfer(_from, _to, _amount);
  1071	        _require(
  1072	            _from == address(0) || _to == address(0) || (controller.gardenTokensTransfersEnabled() && !privateGarden),
  1073	            Errors.GARDEN_TRANSFERS_DISABLED
  1074	        );
  1075	
  1076	        if (_from != address(0) && _to != address(0) && _from != _to) {
  1077	            uint256 fromBalance = balanceOf(_from);
  1078	
  1079	            uint256 lockedBalance = contributors[_from].lockedBalance;
  1080	            _require(fromBalance.sub(lockedBalance) >= _amount, Errors.TOKENS_STAKED);
  1081	
  1082	            // Prevent freezing receiver funds
  1083	            _require(userLock[_to] >= userLock[_from], Errors.SET_GARDEN_USER_LOCK);
  1084	            // Move the lock from the old address to the new address
  1085	            if (userLock[_from] < userLock[_to]) {
  1086	                uint256 toBalance = balanceOf(_to);
  1087	                userLock[_to] = ((userLock[_from].mul(fromBalance)).add(toBalance.mul(userLock[_to]))).div(
  1088	                    fromBalance.add(toBalance)
  1089	                );
  1090	            }
  1091	            _updateContributorWithdrawalInfo(_from, 0, fromBalance, fromBalance.sub(_amount), _amount);
  1092	            _updateContributorDepositInfo(_to, balanceOf(_to), 0, _amount);
  1093	        }
  1094	    }
  1095	
  1096	    /**
  1097	     * @notice
  1098	     *   Sends the amount of reserve asset to the addressee
  1099	     * @dev
  1100	     *   Watch out of reentrancy attacks on `sendValue`
  1101	     * @param _to             Address to send ERC20/ETH to
  1102	     * @param _amount         Amount to send
  1103	     */
  1104	    function _safeSendReserveAsset(address payable _to, uint256 _amount) private {
  1105	        if (reserveAsset == WETH) {
  1106	            // Check that the withdrawal is possible
  1107	            // Unwrap WETH if ETH balance lower than amount
  1108	            if (address(this).balance < _amount) {
  1109	                IWETH(WETH).withdraw(_amount.sub(address(this).balance));
  1110	            }
  1111	            // Send ETH
  1112	            Address.sendValue(_to, _amount);
  1113	        } else {
  1114	            // Send reserve asset
  1115	            IERC20(reserveAsset).safeTransfer(_to, _amount);
  1116	        }
  1117	    }
  1118	
  1119	    /**
  1120	     * @notice
  1121	     *   Approves spending of BABL token to an address
  1122	     * @dev
  1123	     *   Approves BABL staking amount for claim and stake rewards
  1124	     *   Only used to approve Heart Garden to stake
  1125	     * @param _to             Address to approve to
  1126	     * @param _amount         Amount of allowance
  1127	     */
  1128	    function _approveBABL(address _to, uint256 _amount) internal {
  1129	        IERC20(BABL).safeApprove(_to, _amount);
  1130	    }
  1131	
  1132	    /**
  1133	     * @notice                  Updates the contributor data upon deposit
  1134	     * @param _contributor      Contributor to update
  1135	     * @param _previousBalance  Previous balance of the contributor
  1136	     * @param _amountIn         Amount deposited in reserve asset
  1137	     * @param _sharesToMint     Amount of garden shares to mint
  1138	     */
  1139	    function _updateContributorDepositInfo(
  1140	        address _contributor,
  1141	        uint256 _previousBalance,
  1142	        uint256 _amountIn,
  1143	        uint256 _sharesToMint
  1144	    ) private {
  1145	        IGarden.Contributor storage contributor = contributors[_contributor];
  1146	        // If new contributor, create one, increment count, and set the current TS
  1147	        if (_previousBalance == 0 || contributor.initialDepositAt == 0) {
  1148	            totalContributors = totalContributors.add(1);
  1149	            contributor.initialDepositAt = block.timestamp;
  1150	        }
  1151	        // We make checkpoints around contributor deposits to give the right rewards afterwards
  1152	        contributor.totalDeposits = contributor.totalDeposits.add(_amountIn);
  1153	        contributor.lastDepositAt = block.timestamp;
  1154	        // RD checkpoint for accurate rewards
  1155	        rewardsDistributor.updateGardenPowerAndContributor(
  1156	            address(this),
  1157	            _contributor,
  1158	            _previousBalance,
  1159	            _sharesToMint,
  1160	            true // true = deposit , false = withdraw
  1161	        );
  1162	    }
  1163	
  1164	    /**
  1165	     * @notice                  Updates the contributor data upon withdrawal
  1166	     * @param _contributor      Contributor to update
  1167	     * @param _amountOut        Amount withdrawn in reserve asset
  1168	     * @param _previousBalance  Previous balance of the contributor
  1169	     * @param _balance          New balance
  1170	     * @param _amountToBurn     Amount of garden shares to burn
  1171	     */
  1172	    function _updateContributorWithdrawalInfo(
  1173	        address _contributor,
  1174	        uint256 _amountOut,
  1175	        uint256 _previousBalance,
  1176	        uint256 _balance,
  1177	        uint256 _amountToBurn
  1178	    ) private {
  1179	        IGarden.Contributor storage contributor = contributors[_contributor];
  1180	        // If withdrawn everything
  1181	        if (_balance == 0) {
  1182	            contributor.lastDepositAt = 0;
  1183	            contributor.initialDepositAt = 0;
  1184	            contributor.withdrawnSince = 0;
  1185	            contributor.totalDeposits = 0;
  1186	            userLock[_contributor] = 0;
  1187	            totalContributors = totalContributors.sub(1);
  1188	        } else {
  1189	            contributor.withdrawnSince = contributor.withdrawnSince.add(_amountOut);
  1190	        }
  1191	        // RD checkpoint for accurate rewards
  1192	        rewardsDistributor.updateGardenPowerAndContributor(
  1193	            address(this),
  1194	            _contributor,
  1195	            _previousBalance,
  1196	            _amountToBurn,
  1197	            false // true = deposit , false = withdraw
  1198	        );
  1199	    }
  1200	
  1201	    /**
  1202	     * @notice          Checks if an address is a creator
  1203	     * @param _creator  Creator address
  1204	     * @return          True if creator
  1205	     */
  1206	    function _isCreator(address _creator) private view returns (bool) {
  1207	        return
  1208	            _creator != address(0) &&
  1209	            (extraCreators[0] == _creator ||
  1210	                extraCreators[1] == _creator ||
  1211	                extraCreators[2] == _creator ||
  1212	                extraCreators[3] == _creator ||
  1213	                _creator == creator);
  1214	    }
  1215	
  1216	    /**
  1217	     * @notice
  1218	     *   Validates that pricePerShare is within acceptable range; if not reverts
  1219	     * @dev
  1220	     *   Allowed slippage between deposits and withdrawals in terms of the garden price per share is:
  1221	     *
  1222	     *     slippage = lastPricePerShare % (pricePerShareDelta + timePast * pricePerShareDecayRate);
  1223	     *
  1224	     *   For example, if lastPricePerShare is 1e18 and slippage is 10% then deposits with pricePerShare between
  1225	     *   9e17 and 11e17 allowed immediately. After one year (100% change in time) and with a decay rate 1x;
  1226	     *   deposits between 5e17 and 2e18 are possible. Different gardens should have different settings for
  1227	     *   slippage and decay rate due to various volatility of the strategies. For example, stable gardens
  1228	     *   would have low slippage and decay rate while some moonshot gardens may have both of them
  1229	     *   as high as 100% and 10x.
  1230	     * @param _pricePerShare  Price of the graden share to validate against historical data
  1231	     */
  1232	    function _checkLastPricePerShare(uint256 _pricePerShare) private {
  1233	        uint256 slippage = pricePerShareDelta > 0 ? pricePerShareDelta : 25e16;
  1234	        uint256 decay = pricePerShareDecayRate > 0 ? pricePerShareDecayRate : 1e18;
  1235	        // if no previous record then just pass the check
  1236	        if (lastPricePerShare != 0) {
  1237	            slippage = slippage.add(block.timestamp.sub(lastPricePerShareTS).preciseDiv(365 days).preciseMul(decay));
  1238	            if (_pricePerShare > lastPricePerShare) {
  1239	                _require(
  1240	                    _pricePerShare.sub(lastPricePerShare) <= lastPricePerShare.preciseMul(slippage),
  1241	                    Errors.PRICE_PER_SHARE_WRONG
  1242	                );
  1243	            } else {
  1244	                _require(
  1245	                    lastPricePerShare.sub(_pricePerShare) <=
  1246	                        lastPricePerShare.sub(lastPricePerShare.preciseDiv(slippage.add(1e18))),
  1247	                    Errors.PRICE_PER_SHARE_WRONG
  1248	                );
  1249	            }
  1250	        }

exec
/bin/bash -lc "nl -ba contracts/proxy/VTableBeacon.sol | sed -n '1,200p'" in /tmp/codex-bjkv0enx
 succeeded in 0ms:
     1	// SPDX-License-Identifier: Apache-2.0
     2	
     3	pragma solidity 0.7.6;
     4	pragma abicoder v2;
     5	
     6	import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
     7	
     8	/**
     9	 * @title VTableBeacon
    10	 * @notice Redirects calls to an implementation based on the method signature
    11	 */
    12	contract VTableBeacon is Ownable {
    13	    struct ModuleDefinition {
    14	        address implementation;
    15	        bytes4[] selectors;
    16	    }
    17	
    18	    bytes4 private constant _FALLBACK_SIGN = 0xffffffff;
    19	
    20	    // Mapping of methods signatures to their implementations
    21	    mapping(bytes4 => address) public delegates;
    22	
    23	    event VTableUpdate(bytes4 indexed selector, address oldImplementation, address newImplementation);
    24	
    25	    function implementation(bytes4 _selector) external view virtual returns (address module) {
    26	        module = delegates[_selector];
    27	        if (module != address(0)) return module;
    28	
    29	        module = delegates[_FALLBACK_SIGN];
    30	        if (module != address(0)) return module;
    31	
    32	        revert('VTableBeacon: No implementation found');
    33	    }
    34	
    35	    /**
    36	     * @dev Updates the vtable
    37	     */
    38	    function updateVTable(ModuleDefinition[] calldata modules) external onlyOwner {
    39	        for (uint256 i = 0; i < modules.length; ++i) {
    40	            ModuleDefinition memory module = modules[i];
    41	            for (uint256 j = 0; j < module.selectors.length; ++j) {
    42	                bytes4 selector = module.selectors[j];
    43	                emit VTableUpdate(selector, delegates[selector], module.implementation);
    44	                delegates[selector] = module.implementation;
    45	            }
    46	        }
    47	    }
    48	}

exec
/bin/bash -lc "nl -ba contracts/proxy/VTableBeaconProxy.sol | sed -n '1,120p'" in /tmp/codex-bjkv0enx
 succeeded in 0ms:
     1	// SPDX-License-Identifier: Apache-2.0
     2	
     3	pragma solidity 0.7.6;
     4	pragma abicoder v2;
     5	
     6	import '@openzeppelin/contracts/proxy/Proxy.sol';
     7	
     8	import './VTableBeacon.sol';
     9	
    10	/**
    11	 * @title VTableBeaconProxy
    12	 */
    13	contract VTableBeaconProxy is Proxy {
    14	    VTableBeacon public immutable beacon;
    15	
    16	    constructor(VTableBeacon _beacon) {
    17	        beacon = _beacon;
    18	    }
    19	
    20	    function _implementation() internal view virtual override returns (address module) {
    21	        return beacon.implementation(msg.sig);
    22	    }
    23	}

I’ve finished reading the full audit bundle. The exploitable surfaces concentrate in the Garden by-signature flows and the vtable proxy wrapper.
  ✓ Inspect workspace and identify first-party Solidity sources plus audit bundle contents
  ✓ Read AUDIT_BUNDLE.txt fully and map proxy/implementation relationships
  → Review implementation for exploitable issues and produce JSON findings
codex
{"no_findings":false,"summary":"Read the full AUDIT_BUNDLE.txt. The Garden implementation exposes two critical by-signature theft vectors: arbitrary self-reported reward claims and keeper-driven price-per-share manipulation.","findings":[{"severity":"critical","category":"auth","title":"`claimRewardsBySig` trusts attacker-chosen reward amounts and can drain pooled rewards","exploit_scenario":"A contributor signs a `claimRewardsBySig` or `claimAndStakeRewardsBySig` payload with `_profits` set to the entire `reserveAssetRewardsSetAside` and `_babl` set to the per-tx cap. A whitelisted keeper submits it, and the garden transfers the reserve asset directly from the shared reward pool to the attacker without checking actual entitlement. The attacker can repeat with new nonces until the garden's reserved rewards are exhausted, and may also over-pull BABL through the distributor path.","recommendation":"Do not accept beneficiary-supplied reward amounts. Compute claimable rewards on-chain from canonical accounting, or verify a protocol-authenticated proof that binds garden, contributor, strategy set, and exact amounts. Remove `_profits`/`_babl` as free user-controlled inputs in by-signature claim flows.","evidence":"Both by-signature claim entrypoints only verify the user's signature over arbitrary `_babl`/`_profits` values, then call `_sendRewardsInternal`. `_sendRewardsInternal` only checks that `_profits` is nonzero, `reserveAssetRewardsSetAside >= _profits`, and then subtracts `_profits` from the shared reserve and transfers it out; it never validates that the caller actually earned that amount.","locations":["contracts/gardens/Garden.sol:525","contracts/gardens/Garden.sol:576","contracts/gardens/Garden.sol:1011"]},{"severity":"critical","category":"oracle","title":"Keeper-supplied `pricePerShare` can be ratcheted away from NAV to steal value from other LPs","exploit_scenario":"An attacker collaborates with any whitelisted keeper and submits a series of tiny `depositBySig`/`withdrawBySig` calls with manipulated `_pricePerShare` values. Because each call updates `lastPricePerShare` and future checks only compare against that last user-supplied value, the attacker can staircase the accepted price far below or above real NAV. Once the anchor is moved, a large deposit mints excess shares at an artificially low price, or a large withdrawal redeems too much reserve asset at an artificially high price, diluting or draining other contributors.","recommendation":"Do not let relayers provide an untrusted PPS that becomes the next anchor. Bind pricing to an on-chain valuation or a protocol-signed snapshot, include the exact PPS in the signed payload, and validate it against current NAV/oracle bounds instead of the previous user-supplied price alone.","evidence":"`depositBySig` and `withdrawBySig` take `_pricePerShare` from the keeper instead of deriving it on-chain. `_internalDeposit` and `_withdrawInternal` use that value directly to mint/burn shares. `_checkLastPricePerShare` does not compare against actual NAV; it only compares against `lastPricePerShare`, then stores the new attacker-chosen value as the next reference point. On the first by-signature use, `lastPricePerShare == 0`, so any PPS is accepted.","locations":["contracts/gardens/Garden.sol:315","contracts/gardens/Garden.sol:427","contracts/gardens/Garden.sol:863","contracts/gardens/Garden.sol:947","contracts/gardens/Garden.sol:1232"]}]}
tokens used
113,156
Metadata
{
    "codex": {
        "ran_at": "2026-04-04T03:21:46.085360+00:00",
        "returncode": 0
    },
    "project": {
        "mode": "source",
        "file_count": 57,
        "import_count": 68,
        "first_party_contracts_count": 37
    },
    "summary": "Read the full AUDIT_BUNDLE.txt. The Garden implementation exposes two critical by-signature theft vectors: arbitrary self-reported reward claims and keeper-driven price-per-share manipulation.",
    "raw_final": {
        "summary": "Read the full AUDIT_BUNDLE.txt. The Garden implementation exposes two critical by-signature theft vectors: arbitrary self-reported reward claims and keeper-driven price-per-share manipulation.",
        "findings": [
            {
                "title": "`claimRewardsBySig` trusts attacker-chosen reward amounts and can drain pooled rewards",
                "category": "auth",
                "evidence": "Both by-signature claim entrypoints only verify the user's signature over arbitrary `_babl`/`_profits` values, then call `_sendRewardsInternal`. `_sendRewardsInternal` only checks that `_profits` is nonzero, `reserveAssetRewardsSetAside >= _profits`, and then subtracts `_profits` from the shared reserve and transfers it out; it never validates that the caller actually earned that amount.",
                "severity": "critical",
                "locations": [
                    "contracts/gardens/Garden.sol:525",
                    "contracts/gardens/Garden.sol:576",
                    "contracts/gardens/Garden.sol:1011"
                ],
                "recommendation": "Do not accept beneficiary-supplied reward amounts. Compute claimable rewards on-chain from canonical accounting, or verify a protocol-authenticated proof that binds garden, contributor, strategy set, and exact amounts. Remove `_profits`/`_babl` as free user-controlled inputs in by-signature claim flows.",
                "exploit_scenario": "A contributor signs a `claimRewardsBySig` or `claimAndStakeRewardsBySig` payload with `_profits` set to the entire `reserveAssetRewardsSetAside` and `_babl` set to the per-tx cap. A whitelisted keeper submits it, and the garden transfers the reserve asset directly from the shared reward pool to the attacker without checking actual entitlement. The attacker can repeat with new nonces until the garden's reserved rewards are exhausted, and may also over-pull BABL through the distributor path."
            },
            {
                "title": "Keeper-supplied `pricePerShare` can be ratcheted away from NAV to steal value from other LPs",
                "category": "oracle",
                "evidence": "`depositBySig` and `withdrawBySig` take `_pricePerShare` from the keeper instead of deriving it on-chain. `_internalDeposit` and `_withdrawInternal` use that value directly to mint/burn shares. `_checkLastPricePerShare` does not compare against actual NAV; it only compares against `lastPricePerShare`, then stores the new attacker-chosen value as the next reference point. On the first by-signature use, `lastPricePerShare == 0`, so any PPS is accepted.",
                "severity": "critical",
                "locations": [
                    "contracts/gardens/Garden.sol:315",
                    "contracts/gardens/Garden.sol:427",
                    "contracts/gardens/Garden.sol:863",
                    "contracts/gardens/Garden.sol:947",
                    "contracts/gardens/Garden.sol:1232"
                ],
                "recommendation": "Do not let relayers provide an untrusted PPS that becomes the next anchor. Bind pricing to an on-chain valuation or a protocol-signed snapshot, include the exact PPS in the signed payload, and validate it against current NAV/oracle bounds instead of the previous user-supplied price alone.",
                "exploit_scenario": "An attacker collaborates with any whitelisted keeper and submits a series of tiny `depositBySig`/`withdrawBySig` calls with manipulated `_pricePerShare` values. Because each call updates `lastPricePerShare` and future checks only compare against that last user-supplied value, the attacker can staircase the accepted price far below or above real NAV. Once the anchor is moved, a large deposit mints excess shares at an artificially low price, or a large withdrawal redeems too much reserve asset at an artificially high price, diluting or draining other contributors."
            }
        ],
        "no_findings": false
    },
    "no_findings": false,
    "proxy_context": {
        "proxy_run_id": "019d5666-71df-70bc-b254-bdcd67639ea9",
        "proxy_address": "0x22a591793a9dd506bb3009522dec919120dc3087",
        "implementation_run_id": "019d5666-b2b6-72b3-991d-f09cf3f2b2ec",
        "implementation_address": "0x04393e5c6701237d7ee836d644f8dbdd122afebc"
    },
    "schema_version": 1
}

Opcode Heuristics

tool.cast_disasm
Delegatecall
2
Selfdestruct
0
CREATE2
0
CALL-family (heavy)
1
EXT*/BALANCE
2
Total opcodes
501
Flags
delegatecall_present
View cast disassembly output
00000000: PUSH1 0x80
00000002: PUSH1 0x40
00000004: MSTORE
00000005: CALLDATASIZE
00000006: PUSH2 0x000b
00000009: JUMPI
0000000a: STOP
0000000b: JUMPDEST
0000000c: PUSH2 0x0013
0000000f: PUSH2 0x0015
00000012: JUMP
00000013: JUMPDEST
00000014: STOP
00000015: JUMPDEST
00000016: PUSH2 0x001d
00000019: PUSH2 0x002d
0000001c: JUMP
0000001d: JUMPDEST
0000001e: PUSH2 0x002d
00000021: PUSH2 0x0028
00000024: PUSH2 0x0146
00000027: JUMP
00000028: JUMPDEST
00000029: PUSH2 0x01c6
0000002c: JUMP
0000002d: JUMPDEST
0000002e: JUMP
0000002f: JUMPDEST
00000030: EXTCODESIZE
00000031: ISZERO
00000032: ISZERO
00000033: SWAP1
00000034: JUMP
00000035: JUMPDEST
00000036: PUSH1 0x60
00000038: PUSH2 0x0040
0000003b: DUP5
0000003c: PUSH2 0x002f
0000003f: JUMP
00000040: JUMPDEST
00000041: PUSH2 0x007b
00000044: JUMPI
00000045: PUSH1 0x40
00000047: MLOAD
00000048: PUSH3 0x461bcd
0000004c: PUSH1 0xe5
0000004e: SHL
0000004f: DUP2
00000050: MSTORE
00000051: PUSH1 0x04
00000053: ADD
00000054: DUP1
00000055: DUP1
00000056: PUSH1 0x20
00000058: ADD
00000059: DUP3
0000005a: DUP2
0000005b: SUB
0000005c: DUP3
0000005d: MSTORE
0000005e: PUSH1 0x26
00000060: DUP2
00000061: MSTORE
00000062: PUSH1 0x20
00000064: ADD
00000065: DUP1
00000066: PUSH2 0x02b4
00000069: PUSH1 0x26
0000006b: SWAP2
0000006c: CODECOPY
0000006d: PUSH1 0x40
0000006f: ADD
00000070: SWAP2
00000071: POP
00000072: POP
00000073: PUSH1 0x40
00000075: MLOAD
00000076: DUP1
00000077: SWAP2
00000078: SUB
00000079: SWAP1
0000007a: REVERT
0000007b: JUMPDEST
0000007c: PUSH1 0x00
0000007e: DUP1
0000007f: DUP6
00000080: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000095: AND
00000096: DUP6
00000097: PUSH1 0x40
00000099: MLOAD
0000009a: DUP1
0000009b: DUP3
0000009c: DUP1
0000009d: MLOAD
0000009e: SWAP1
0000009f: PUSH1 0x20
000000a1: ADD
000000a2: SWAP1
000000a3: DUP1
000000a4: DUP4
000000a5: DUP4
000000a6: JUMPDEST
000000a7: PUSH1 0x20
000000a9: DUP4
000000aa: LT
000000ab: PUSH2 0x00c5
000000ae: JUMPI
000000af: DUP1
000000b0: MLOAD
000000b1: DUP3
000000b2: MSTORE
000000b3: PUSH1 0x1f
000000b5: NOT
000000b6: SWAP1
000000b7: SWAP3
000000b8: ADD
000000b9: SWAP2
000000ba: PUSH1 0x20
000000bc: SWAP2
000000bd: DUP3
000000be: ADD
000000bf: SWAP2
000000c0: ADD
000000c1: PUSH2 0x00a6
000000c4: JUMP
000000c5: JUMPDEST
000000c6: PUSH1 0x01
000000c8: DUP4
000000c9: PUSH1 0x20
000000cb: SUB
000000cc: PUSH2 0x0100
000000cf: EXP
000000d0: SUB
000000d1: DUP1
000000d2: NOT
000000d3: DUP3
000000d4: MLOAD
000000d5: AND
000000d6: DUP2
000000d7: DUP5
000000d8: MLOAD
000000d9: AND
000000da: DUP1
000000db: DUP3
000000dc: OR
000000dd: DUP6
000000de: MSTORE
000000df: POP
000000e0: POP
000000e1: POP
000000e2: POP
000000e3: POP
000000e4: POP
000000e5: SWAP1
000000e6: POP
000000e7: ADD
000000e8: SWAP2
000000e9: POP
000000ea: POP
000000eb: PUSH1 0x00
000000ed: PUSH1 0x40
000000ef: MLOAD
000000f0: DUP1
000000f1: DUP4
000000f2: SUB
000000f3: DUP2
000000f4: DUP6
000000f5: GAS
000000f6: DELEGATECALL
000000f7: SWAP2
000000f8: POP
000000f9: POP
000000fa: RETURNDATASIZE
000000fb: DUP1
000000fc: PUSH1 0x00
000000fe: DUP2
000000ff: EQ
00000100: PUSH2 0x0125
00000103: JUMPI
00000104: PUSH1 0x40
00000106: MLOAD
00000107: SWAP2
00000108: POP
00000109: PUSH1 0x1f
0000010b: NOT
0000010c: PUSH1 0x3f
0000010e: RETURNDATASIZE
0000010f: ADD
00000110: AND
00000111: DUP3
00000112: ADD
00000113: PUSH1 0x40
00000115: MSTORE
00000116: RETURNDATASIZE
00000117: DUP3
00000118: MSTORE
00000119: RETURNDATASIZE
0000011a: PUSH1 0x00
0000011c: PUSH1 0x20
0000011e: DUP5
0000011f: ADD
00000120: RETURNDATACOPY
00000121: PUSH2 0x012a
00000124: JUMP
00000125: JUMPDEST
00000126: PUSH1 0x60
00000128: SWAP2
00000129: POP
0000012a: JUMPDEST
0000012b: POP
0000012c: SWAP2
0000012d: POP
0000012e: SWAP2
0000012f: POP
00000130: PUSH2 0x013a
00000133: DUP3
00000134: DUP3
00000135: DUP7
00000136: PUSH2 0x01ea
00000139: JUMP
0000013a: JUMPDEST
0000013b: SWAP3
0000013c: POP
0000013d: POP
0000013e: POP
0000013f: JUMPDEST
00000140: SWAP4
00000141: SWAP3
00000142: POP
00000143: POP
00000144: POP
00000145: JUMP
00000146: JUMPDEST
00000147: PUSH1 0x00
00000149: PUSH2 0x0150
0000014c: PUSH2 0x028e
0000014f: JUMP
00000150: JUMPDEST
00000151: PUSH20 0xffffffffffffffffffffffffffffffffffffffff
00000166: AND
00000167: PUSH4 0x5c60da1b
0000016c: PUSH1 0x40
0000016e: MLOAD
0000016f: DUP2
00000170: PUSH4 0xffffffff
00000175: AND
00000176: PUSH1 0xe0
00000178: SHL
00000179: DUP2
0000017a: MSTORE
0000017b: PUSH1 0x04
0000017d: ADD
0000017e: PUSH1 0x20
00000180: PUSH1 0x40
00000182: MLOAD
00000183: DUP1
00000184: DUP4
00000185: SUB
00000186: DUP2
00000187: DUP7
00000188: DUP1
00000189: EXTCODESIZE
0000018a: ISZERO
0000018b: DUP1
0000018c: ISZERO
0000018d: PUSH2 0x0195
00000190: JUMPI
00000191: PUSH1 0x00
00000193: DUP1
00000194: REVERT
00000195: JUMPDEST
00000196: POP
00000197: GAS
00000198: STATICCALL
00000199: ISZERO
0000019a: DUP1
0000019b: ISZERO
0000019c: PUSH2 0x01a9
0000019f: JUMPI
000001a0: RETURNDATASIZE
000001a1: PUSH1 0x00
000001a3: DUP1
000001a4: RETURNDATACOPY
000001a5: RETURNDATASIZE
000001a6: PUSH1 0x00
000001a8: REVERT
000001a9: JUMPDEST
000001aa: POP
000001ab: POP
000001ac: POP
000001ad: POP
000001ae: PUSH1 0x40
000001b0: MLOAD
000001b1: RETURNDATASIZE
000001b2: PUSH1 0x20
000001b4: DUP2
000001b5: LT
000001b6: ISZERO
000001b7: PUSH2 0x01bf
000001ba: JUMPI
000001bb: PUSH1 0x00
000001bd: DUP1
000001be: REVERT
000001bf: JUMPDEST
000001c0: POP
000001c1: MLOAD
000001c2: SWAP1
000001c3: POP
000001c4: SWAP1
000001c5: JUMP
000001c6: JUMPDEST
000001c7: CALLDATASIZE
000001c8: PUSH1 0x00
000001ca: DUP1
000001cb: CALLDATACOPY
000001cc: PUSH1 0x00
000001ce: DUP1
000001cf: CALLDATASIZE
000001d0: PUSH1 0x00
000001d2: DUP5
000001d3: GAS
000001d4: DELEGATECALL
000001d5: RETURNDATASIZE
000001d6: PUSH1 0x00
000001d8: DUP1
000001d9: RETURNDATACOPY
000001da: DUP1
000001db: DUP1
000001dc: ISZERO
000001dd: PUSH2 0x01e5
000001e0: JUMPI
000001e1: RETURNDATASIZE
000001e2: PUSH1 0x00
000001e4: RETURN
000001e5: JUMPDEST
000001e6: RETURNDATASIZE
000001e7: PUSH1 0x00
000001e9: REVERT
000001ea: JUMPDEST
000001eb: PUSH1 0x60
000001ed: DUP4
000001ee: ISZERO
000001ef: PUSH2 0x01f9
000001f2: JUMPI
000001f3: POP
000001f4: DUP2
000001f5: PUSH2 0x013f
000001f8: JUMP
000001f9: JUMPDEST
000001fa: DUP3
000001fb: MLOAD
000001fc: ISZERO
000001fd: PUSH2 0x0209
00000200: JUMPI
00000201: DUP3
00000202: MLOAD
00000203: DUP1
00000204: DUP5
00000205: PUSH1 0x20
00000207: ADD
00000208: REVERT
00000209: JUMPDEST
0000020a: DUP2
0000020b: PUSH1 0x40
0000020d: MLOAD
0000020e: PUSH3 0x461bcd
00000212: PUSH1 0xe5
00000214: SHL
00000215: DUP2
00000216: MSTORE
00000217: PUSH1 0x04
00000219: ADD
0000021a: DUP1
0000021b: DUP1
0000021c: PUSH1 0x20
0000021e: ADD
0000021f: DUP3
00000220: DUP2
00000221: SUB
00000222: DUP3
00000223: MSTORE
00000224: DUP4
00000225: DUP2
00000226: DUP2
00000227: MLOAD
00000228: DUP2
00000229: MSTORE
0000022a: PUSH1 0x20
0000022c: ADD
0000022d: SWAP2
0000022e: POP
0000022f: DUP1
00000230: MLOAD
00000231: SWAP1
00000232: PUSH1 0x20
00000234: ADD
00000235: SWAP1
00000236: DUP1
00000237: DUP4
00000238: DUP4
00000239: PUSH1 0x00
0000023b: JUMPDEST
0000023c: DUP4
0000023d: DUP2
0000023e: LT
0000023f: ISZERO
00000240: PUSH2 0x0253
00000243: JUMPI
00000244: DUP2
00000245: DUP2
00000246: ADD
00000247: MLOAD
00000248: DUP4
00000249: DUP3
0000024a: ADD
0000024b: MSTORE
0000024c: PUSH1 0x20
0000024e: ADD
0000024f: PUSH2 0x023b
00000252: JUMP
00000253: JUMPDEST
00000254: POP
00000255: POP
00000256: POP
00000257: POP
00000258: SWAP1
00000259: POP
0000025a: SWAP1
0000025b: DUP2
0000025c: ADD
0000025d: SWAP1
0000025e: PUSH1 0x1f
00000260: AND
00000261: DUP1
00000262: ISZERO
00000263: PUSH2 0x0280
00000266: JUMPI
00000267: DUP1
00000268: DUP3
00000269: SUB
0000026a: DUP1
0000026b: MLOAD
0000026c: PUSH1 0x01
0000026e: DUP4
0000026f: PUSH1 0x20
00000271: SUB
00000272: PUSH2 0x0100
00000275: EXP
00000276: SUB
00000277: NOT
00000278: AND
00000279: DUP2
0000027a: MSTORE
0000027b: PUSH1 0x20
0000027d: ADD
0000027e: SWAP2
0000027f: POP
00000280: JUMPDEST
00000281: POP
00000282: SWAP3
00000283: POP
00000284: POP
00000285: POP
00000286: PUSH1 0x40
00000288: MLOAD
00000289: DUP1
0000028a: SWAP2
0000028b: SUB
0000028c: SWAP1
0000028d: REVERT
0000028e: JUMPDEST
0000028f: PUSH32 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50
000002b0: SLOAD
000002b1: SWAP1
000002b2: JUMP
000002b3: INVALID
000002b4: COINBASE
000002b5: PUSH5 0x6472657373
000002bb: GASPRICE
000002bc: KECCAK256
000002bd: PUSH5 0x656c656761
000002c3: PUSH21 0x652063616c6c20746f206e6f6e2d636f6e74726163
000002d9: PUSH21 0xa26469706673582212202d92425cab0cd3b71cbc16
000002ef: UNKNOWN(0xC4)
000002f0: DUP15
000002f1: UNKNOWN(0xEA)
000002f2: DUP11
000002f3: PUSH10 0xf3738d5cf7b303a8f177
000002fe: EQ
000002ff: UNKNOWN(0x0C)
00000300: JUMPI
00000301: REVERT
00000302: UNKNOWN(0x29)
00000303: UNKNOWN(0xDB)
00000304: PUSH5 0x736f6c6343
0000030a: STOP
0000030b: SMOD
0000030c: MOD
0000030d: STOP
0000030e: CALLER

Findings

validated = confirmed for call sinks; sink observed for others
Severity Category Tool Title Confidence Validated Confirmed Details
critical upgradeability detector Untrusted DELEGATECALL target reachable 70 no no
view
validation_json
{
    "sink": "DELEGATECALL",
    "errors": 10,
    "status": "unknown",
    "attempts": 11,
    "confirmed": false,
    "trace_mode": "callTracer",
    "call_targets": null,
    "matched_probe": null,
    "target_varies": null,
    "classification": "no_sink"
}
witness_json
{
    "notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
    "constraints": [],
    "entrypoints": [
        {
            "calldata": "0x",
            "selector": "fallback",
            "calldata_variants": [
                "0x"
            ]
        }
    ]
}
evidence_json
{
    "examples": [
        {
            "pc": 246,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        },
        {
            "pc": 468,
            "opcode": "DELEGATECALL",
            "returndata_checked": null,
            "value_expression_category": "n/a",
            "target_expression_category": "computed"
        }
    ],
    "guards_detected": [],
    "delegatecall_count": 2,
    "untrusted_delegatecall_count": 2
}
critical auth codex `claimRewardsBySig` trusts attacker-chosen reward amounts and can drain pooled rewards 90 no
view
evidence_json
{
    "evidence": "Both by-signature claim entrypoints only verify the user's signature over arbitrary `_babl`/`_profits` values, then call `_sendRewardsInternal`. `_sendRewardsInternal` only checks that `_profits` is nonzero, `reserveAssetRewardsSetAside >= _profits`, and then subtracts `_profits` from the shared reserve and transfers it out; it never validates that the caller actually earned that amount.",
    "locations": [
        "contracts/gardens/Garden.sol:525",
        "contracts/gardens/Garden.sol:576",
        "contracts/gardens/Garden.sol:1011"
    ],
    "recommendation": "Do not accept beneficiary-supplied reward amounts. Compute claimable rewards on-chain from canonical accounting, or verify a protocol-authenticated proof that binds garden, contributor, strategy set, and exact amounts. Remove `_profits`/`_babl` as free user-controlled inputs in by-signature claim flows.",
    "exploit_scenario": "A contributor signs a `claimRewardsBySig` or `claimAndStakeRewardsBySig` payload with `_profits` set to the entire `reserveAssetRewardsSetAside` and `_babl` set to the per-tx cap. A whitelisted keeper submits it, and the garden transfers the reserve asset directly from the shared reward pool to the attacker without checking actual entitlement. The attacker can repeat with new nonces until the garden's reserved rewards are exhausted, and may also over-pull BABL through the distributor path."
}
critical oracle codex Keeper-supplied `pricePerShare` can be ratcheted away from NAV to steal value from other LPs 90 no
view
evidence_json
{
    "evidence": "`depositBySig` and `withdrawBySig` take `_pricePerShare` from the keeper instead of deriving it on-chain. `_internalDeposit` and `_withdrawInternal` use that value directly to mint/burn shares. `_checkLastPricePerShare` does not compare against actual NAV; it only compares against `lastPricePerShare`, then stores the new attacker-chosen value as the next reference point. On the first by-signature use, `lastPricePerShare == 0`, so any PPS is accepted.",
    "locations": [
        "contracts/gardens/Garden.sol:315",
        "contracts/gardens/Garden.sol:427",
        "contracts/gardens/Garden.sol:863",
        "contracts/gardens/Garden.sol:947",
        "contracts/gardens/Garden.sol:1232"
    ],
    "recommendation": "Do not let relayers provide an untrusted PPS that becomes the next anchor. Bind pricing to an on-chain valuation or a protocol-signed snapshot, include the exact PPS in the signed payload, and validate it against current NAV/oracle bounds instead of the previous user-supplied price alone.",
    "exploit_scenario": "An attacker collaborates with any whitelisted keeper and submits a series of tiny `depositBySig`/`withdrawBySig` calls with manipulated `_pricePerShare` values. Because each call updates `lastPricePerShare` and future checks only compare against that last user-supplied value, the attacker can staircase the accepted price far below or above real NAV. Once the anchor is moved, a large deposit mints excess shares at an artificially low price, or a large withdrawal redeems too much reserve asset at an artificially high price, diluting or draining other contributors."
}
medium upgradeability cast DELEGATECALL present 80 no
view
evidence_json
{
    "notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
    "delegatecall_count": 2
}