019d5666-71df-70bc-b254-bdcd67639ea9
Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?
| 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 |
{
"method": "etherscan+onchain",
"status": "confirmed",
"etherscan_hint": {
"source": "etherscan",
"etherscan": {
"proxy_flag": true,
"implementation": "0x04393e5c6701237d7ee836d644f8dbdd122afebc"
}
},
"implementation": "0x04393e5c6701237d7ee836d644f8dbdd122afebc",
"onchain_method": "beacon"
}
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
[2m2026-04-04T04:13:51.549217Z[0m [31mERROR[0m [2mfoundry_compilers_artifacts_solc::sources[0m[2m:[0m [3merror[0m[2m=[0m"/tmp/slither-zdp771mw/lib/openzeppelin-contracts/contracts/proxy/UpgradeableBeacon.sol": No such file or directory (os error 2)
stdout: [2m2026-04-04T04:13:51.549303Z[0m [31mERROR[0m [2mfoundry_compilers_artifacts_solc::sources[0m[2m:[0m [3merror[0m[2m=[0m"/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.
{"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"]}]}
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
{
"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
}
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
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| critical | upgradeability | detector | Untrusted DELEGATECALL target reachable | 70 | no | no |
viewvalidation_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 | — |
viewevidence_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 | — |
viewevidence_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 | — |
viewevidence_json{
"notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
"delegatecall_count": 2
}
|